diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..c5b352318c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,31 @@ +# Hide generated JSON spec test files from displaying in diffs by default +# https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github + +/source/auth/tests/**/*.json linguist-generated=true +/source/change-streams/tests/unified/*.json linguist-generated=true +/source/client-side-encryption/tests/**/*.json linguist-generated=true +/source/client-side-operations-timeout/tests/*.json linguist-generated=true +/source/collection-management/tests/*.json linguist-generated=true +/source/command-logging-and-monitoring/tests/**/*.json linguist-generated=true +/source/connection-monitoring-and-pooling/tests/**/*.json linguist-generated=true +/source/connection-string/tests/*.json linguist-generated=true +/source/crud/tests/**/*.json linguist-generated=true +/source/gridfs/tests/*.json linguist-generated=true +/source/index-management/tests/*.json linguist-generated=true +/source/initial-dns-seedlist-discovery/tests/**/*.json linguist-generated=true +/source/load-balancers/tests/*.json linguist-generated=true +/source/max-staleness/tests/**/*.json linguist-generated=true +/source/read-write-concern/tests/**/*.json linguist-generated=true +/source/retryable-reads/tests/legacy/*.json linguist-generated=true +/source/retryable-reads/tests/unified/*.json linguist-generated=true +/source/retryable-writes/tests/legacy/*.json linguist-generated=true +/source/retryable-writes/tests/unified/*.json linguist-generated=true +/source/run-command/tests/unified/*.json linguist-generated=true +/source/server-discovery-and-monitoring/tests/**/*.json linguist-generated=true +/source/server-selection/tests/**/*.json linguist-generated=true +/source/sessions/tests/*.json linguist-generated=true +/source/transactions-convenient-api/tests/*.json linguist-generated=true +/source/transactions/tests/**/*.json linguist-generated=true +/source/unified-test-format/tests/**/*.json linguist-generated=true +/source/uri-options/tests/*.json linguist-generated=true +/source/versioned-api/tests/*.json linguist-generated=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..6771d2eec6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,108 @@ +# Default ownership +* @mongodb/dbx-leadership @mongodb/dbx-spec-maintainers-cross-spec-consistency + +# Cross-spec consistency: guidelines +README.md @mongodb/dbx-spec-maintainers-cross-spec-consistency +/source/driver-mantras.md @mongodb/dbx-spec-maintainers-cross-spec-consistency +AGENTS.md @mongodb/dbx-spec-maintainers-cross-spec-consistency +CLAUDE.md @mongodb/dbx-spec-maintainers-cross-spec-consistency + +# Authentication +/source/auth/ @mongodb/dbx-spec-maintainers-auth + +# Backpressure +/source/client-backpressure/ @mongodb/dbx-spec-maintainers-backpressure + +#Benchmarking +/source/benchmarking/ @mongodb/dbx-spec-maintainers-benchmarking + +# BSON & Extended JSON +/source/bson-binary-encrypted/ @mongodb/dbx-spec-maintainers-bson +/source/bson-binary-uuid/ @mongodb/dbx-spec-maintainers-bson +/source/bson-binary-vector/ @mongodb/dbx-spec-maintainers-bson +/source/bson-corpus/ @mongodb/dbx-spec-maintainers-bson +/source/bson-decimal128/ @mongodb/dbx-spec-maintainers-bson +/source/bson-objectid/ @mongodb/dbx-spec-maintainers-bson +/source/dbref/ @mongodb/dbx-spec-maintainers-bson +/source/extended-json/ @mongodb/dbx-spec-maintainers-bson + +# Change Streams +/source/change-streams/ @mongodb/dbx-spec-maintainers-change-streams + +# Client Side Encryption +/source/client-side-encryption/ @mongodb/dbx-spec-maintainers-client-side-encryption + +# Client Side Operations Timeout +/source/client-side-operations-timeout/ @mongodb/dbx-spec-maintainers-csot + +# Connection Establishment +/source/load-balancers/ @mongodb/dbx-spec-maintainers-connection-establishment +/source/mongodb-handshake/ @mongodb/dbx-spec-maintainers-connection-establishment +/source/socks5-support/ @mongodb/dbx-spec-maintainers-connection-establishment +/source/versioned-api/ @mongodb/dbx-spec-maintainers-connection-establishment +/source/wireversion-featurelist/ @mongodb/dbx-spec-maintainers-connection-establishment + +# Connection String & URI Options +/source/connection-string/ @mongodb/dbx-spec-maintainers-connection-string +/source/uri-options/ @mongodb/dbx-spec-maintainers-connection-string + +# CMAP +/source/connection-monitoring-and-pooling/ @mongodb/dbx-spec-maintainers-cmap + +# CRUD & Command-level operations +/source/crud/ @mongodb/dbx-spec-maintainers-crud +/source/collation/ @mongodb/dbx-spec-maintainers-crud +/source/driver-bulk-update.rst @mongodb/dbx-spec-maintainers-crud +/source/find_getmore_killcursors_commands/ @mongodb/dbx-spec-maintainers-crud +/source/gridfs/ @mongodb/dbx-spec-maintainers-crud +/source/read-write-concern/ @mongodb/dbx-spec-maintainers-crud +/source/run-command/ @mongodb/dbx-spec-maintainers-crud +/source/server_write_commands/ @mongodb/dbx-spec-maintainers-crud + +# Database & Collection Enumeration +/source/enumerate-collections/ @mongodb/dbx-spec-maintainers-database-collection-enumeration +/source/enumerate-databases/ @mongodb/dbx-spec-maintainers-database-collection-enumeration +/source/collection-management/ @mongodb/dbx-spec-maintainers-database-collection-enumeration + +# SRV-based Cluster Discovery +/source/initial-dns-seedlist-discovery/ @mongodb/dbx-spec-maintainers-srv-clusters +/source/polling-srv-records-for-mongos-discovery/ @mongodb/dbx-spec-maintainers-srv-clusters + +# FAAS Testing +/source/faas-automated-testing/ @mongodb/dbx-spec-maintainers-faas + +# Index Management +/source/index-management @mongodb/dbx-spec-maintainers-index-management + +# Observability +/source/command-logging-and-monitoring/ @mongodb/dbx-spec-maintainers-observability +/source/logging/ @mongodb/dbx-spec-maintainers-observability +/source/open-telemetry/ @mongodb/dbx-spec-maintainers-observability + +# Retryability +/source/retryable-reads/ @mongodb/dbx-spec-maintainers-retryability +/source/retryable-writes/ @mongodb/dbx-spec-maintainers-retryability + +# Server Discovery and Topology Resilience +/source/server-discovery-and-monitoring/ @mongodb/dbx-spec-maintainers-server-discovery-topology-resilience +/source/connections-survive-step-down/ @mongodb/dbx-spec-maintainers-server-discovery-topology-resilience + +# Server Selection & Staleness +/source/server-selection/ @mongodb/dbx-spec-maintainers-server-selection +/source/max-staleness/ @mongodb/dbx-spec-maintainers-server-selection + +# Session & Transaction Semantics +/source/causal-consistency/ @mongodb/dbx-spec-maintainers-session-transactions +/source/sessions/ @mongodb/dbx-spec-maintainers-session-transactions +/source/transactions/ @mongodb/dbx-spec-maintainers-session-transactions +/source/transactions-convenient-api/ @mongodb/dbx-spec-maintainers-session-transactions + +# Unified Test Format +/source/unified-test-format/ @mongodb/dbx-spec-maintainers-unified-test-format + +# Wire Protocol & Compression +/source/message/ @mongodb/dbx-spec-maintainers-wire-protocol-compression +/source/compression/ @mongodb/dbx-spec-maintainers-wire-protocol-compression + +# Workflows and code ownership file +/.github/ @mongodb/dbx-leadership diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..2bc30c6b2e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ + + +Please complete the following before merging: + +- [ ] Is the relevant DRIVERS ticket in the PR title? + + + + + +- [ ] Update changelog. +- [ ] Test changes in at least one language driver. +- [ ] Test these changes against all server versions and topologies (including standalone, replica set, and sharded + clusters). + + diff --git a/.github/workflows/check-schema-latest.sh b/.github/workflows/check-schema-latest.sh new file mode 100644 index 0000000000..06243b748c --- /dev/null +++ b/.github/workflows/check-schema-latest.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Change to the working directory where the schema files are located +cd source/unified-test-format || { + echo "Directory source/unified-test-format not found." + exit 1 +} + +# Find the max X in schema-1.X.json +max=0 +for file in schema-1.*.json; do + # Extract the version number from the filename + version=${file##*schema-1.} + version=${version%.json} + + if [[ $version =~ ^[0-9]+$ ]]; then + # Compare the version number with the current max + if ((version > max)); then + max=$version + fi + fi +done + +if ((max == 0)); then + echo "No schema files found." + exit 1 +fi + +# Compare that file vs schema-latest.json +expected="schema-1.$max.json" +if ! diff -u "$expected" schema-latest.json >/dev/null; then + echo "schema-latest.json is not up to date with schema-1.$max.json" + echo "please run 'make update-schema-latest' from source/ to update it" + exit 1 +fi + +exit 0 diff --git a/.github/workflows/check_schema_version.sh b/.github/workflows/check_schema_version.sh new file mode 100755 index 0000000000..a97e81a695 --- /dev/null +++ b/.github/workflows/check_schema_version.sh @@ -0,0 +1,45 @@ +#! /usr/bin/env bash + +# Collect error codes so we can see all the invalid json files in one run +exitCode=0 + +# $1 - takes a single argument of the path to the JSON file containing a schemaVersion key at the top level. +function get_schema_version() { + js-yaml $1 | jq -r .schemaVersion +} + +function get_json_schema_url () { + schemaVersion=$(get_schema_version "$1") + schemaFile="source/unified-test-format/schema-$schemaVersion.json" + cat "$schemaFile" | jq -r '.["$schema"]' +} + +function get_all_schemaVersion_defining_files () { + # look for all yaml files with "schemaVersion: ["'][1-9]" + grep --include=*.{yml,yaml} --files-with-matches --recursive --word-regexp --regexp="schemaVersion: [\"'][1-9]" source | \ + # Remove the known invalid test files from 'unified-test-format/tests/invalid' + grep --word-regexp --invert-match 'unified-test-format/tests/invalid' | \ + # sort the result! + sort +} + +for testFile in $(get_all_schemaVersion_defining_files) +do + schemaVersion=$(get_schema_version "$testFile") + jsonSchemaURL=$(get_json_schema_url "$testFile") + if [ "$jsonSchemaURL" = "https://json-schema.org/draft/2019-09/schema#" ]; then + spec="draft2019" + elif [ "$jsonSchemaURL" = "http://json-schema.org/draft-07/schema#" ]; then + spec="draft7" + else + echo "Do not know how to validate $jsonSchemaURL" + exit 1 + fi + + if ! ajvCheck=$(ajv --spec="$spec" -s "source/unified-test-format/schema-$schemaVersion.json" -d "$testFile"); then + exitCode=1 + fi + echo "$ajvCheck using schema v$schemaVersion" +done + +exit $exitCode diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..50724407ff --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,34 @@ +name: Lint Files + +on: + push: + branches: [master] + pull_request: + +jobs: + # Run "pre-commit run --all-files" + pre-commit: + name: Run pre-commit checks + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + - name: "Run pre-commit" + run: | + pip install -U -q pre-commit + pre-commit run --all-files --hook-stage manual || (git diff; false) + + mkdocs: + name: Check docs + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + - name: Setup dependencies + run: pip install -r source/requirements.txt + - name: Check docs + run: mkdocs build --strict diff --git a/.github/workflows/unified-tests.yml b/.github/workflows/unified-tests.yml new file mode 100644 index 0000000000..24f4be3a4a --- /dev/null +++ b/.github/workflows/unified-tests.yml @@ -0,0 +1,66 @@ +name: Unified Tests + +on: + push: + branches: [master] + pull_request: ~ + +jobs: + schema-check: + name: Check unified test format files against schema + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + - name: Set up npm + uses: actions/setup-node@v6 + with: + node-version: lts/* + - name: Install dependencies + run: | + npm install -g ajv-cli js-yaml + - name: Check unified format test files against schema + working-directory: source/unified-test-format/tests + run: make + - name: Check all unified tests schema version is valid + run: bash .github/workflows/check_schema_version.sh + - name: Check schema-latest.json has been updated + run: bash .github/workflows/check-schema-latest.sh + + json-check: + name: Check if all JSON test files are up-to-date + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: 3.14 + - name: Set up npm + uses: actions/setup-node@v6 + with: + node-version: lts/* + - name: Install dependencies + run: | + npm install -g js-yaml + pip3 install pymongo pyyaml jinja2 + - name: Regenerate JSON test files + run: | + python3 ./source/server-discovery-and-monitoring/tests/errors/generate-error-tests.py + python3 ./source/client-side-encryption/etc/generate-corpus.py ./source/client-side-encryption/corpus + python3 ./source/client-side-encryption/etc/generate-test.py ./source/client-side-encryption/etc/test-templates/*.template ./source/client-side-encryption/tests/legacy + python3 ./source/client-side-operations-timeout/etc/generate-basic-tests.py ./source/client-side-operations-timeout/etc/templates ./source/client-side-operations-timeout/tests + python3 ./source/etc/generate-handshakeError-tests.py + cd source && make -B + + - name: "Commit the changes" + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Update generated files" diff --git a/.gitignore b/.gitignore index 1c30701882..3ef5ceb240 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,9 @@ makefile.generated logo.png .DS_Store codereview.rc +.idea/** +docs_build +.pytest_cache +node_modules +package-lock.json +site \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..6b389908ab --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,92 @@ + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + #- id: end-of-file-fixer + #- id: trailing-whitespace + #- id: check-json + +# We use the Python version instead of the original version which seems to require Docker +# https://github.com/koalaman/shellcheck-precommit +- repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + name: shellcheck + args: ["--severity=error"] + stages: [manual] + +- repo: https://github.com/executablebooks/mdformat + rev: 1.0.0 + hooks: + - id: mdformat + args: ["--wrap=120", "--number"] + additional_dependencies: + - mdformat-footnote==0.1.2 + - mdformat-frontmatter==2.0.10 + - mdformat-gfm==1.0.0 + - mdformat-gfm-alerts==2.0.0 + - mdformat-mkdocs==5.1.4 + - markdown-it-py==3.0.0 + # Files that mdformat cannot parse: + exclude: | + (?x)^(source/extended-json/extended-json.md + )$ + +- repo: local + hooks: + - id: generate-index + name: generate-index + entry: python3 scripts/generate_index.py + language: system + - id: markdown-link-format-check + name: markdown-link-format-check + types: [markdown] + language: system + entry: python3 scripts/check_links.py + - id: markdown-html-check + name: markdown-html-check + types: [markdown] + language: system + entry: python3 scripts/check_md_html.py + +- repo: https://github.com/tcort/markdown-link-check + rev: v3.13.7 + hooks: + - id: markdown-link-check + args: ["-q", "-c", "markdown_link_config.json"] + +- repo: https://github.com/rstcheck/rstcheck + rev: v6.2.0 + hooks: + - id: rstcheck + additional_dependencies: [sphinx] + args: ["--ignore-languages=c,python,java", "--report-level=error"] + stages: [manual] + +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.4 + hooks: + - id: check-github-workflows + +- repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.10.0" + hooks: + # - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + +- repo: https://github.com/codespell-project/codespell + rev: "v2.3.0" + hooks: + - id: codespell + args: ["-L", "fle,re-use,merchantibility,synching,crate,nin,infinit,te,checkin,aks"] + exclude: | + (?x)^(.*\.rst + )$ diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..ddd534c41f --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,19 @@ +# Read the Docs configuration file for MkDocs projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + +mkdocs: + configuration: mkdocs.yml + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: source/requirements.txt \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..746766fea3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,159 @@ +# MongoDB Specifications — Agent Guidance + +## Repository Purpose + +This is the official MongoDB Specifications repository containing technical specifications for MongoDB drivers and +associated products. It includes: + +- Specification documents (Markdown in `source/`) +- Unified test format files (YAML + auto-generated JSON) consumed by all MongoDB drivers +- Documentation published via MkDocs and ReadTheDocs + +## Common Commands + +### Documentation + +```bash +pip install -r source/requirements.txt # Install Python deps +mkdocs serve # Live preview at localhost:8000 +mkdocs build --strict # Build (CI mode, no warnings allowed) +``` + +### Pre-commit Hooks + +```bash +pre-commit install # Set up hooks +pre-commit run --all-files # Run all hooks manually +pre-commit run --all-files --hook-stage manual shellcheck # Run shellcheck manually +``` + +### YAML → JSON Test Conversion + +CI will fail if JSON files are out of date with their YAML sources. Always run `make -C source` after editing YAML test +files. + +```bash +npm install -g js-yaml # Required once +make -C source # Convert all YAML test files to JSON +``` + +### Schema Latest Update (after adding a new unified test format schema version) + +```bash +make update-schema-latest -C source +``` + +### Test Generation Scripts + +Some specs include Python scripts that generate test files. These often live alongside their respective specs rather +than in a single directory. Examples: + +```bash +python source/server-discovery-and-monitoring/tests/errors/generate-error-tests.py # SDAM error tests +python source/client-side-encryption/etc/generate-corpus.py # Client-side encryption corpus tests +python source/etc/generate-handshakeError-tests.py # Handshake error tests +``` + +A general pattern: look for `generate-*.py` scripts in a spec's `etc/` or `tests/` subdirectory. + +## Architecture + +### Specification Documents (`source/`) + +Each subdirectory under `source/` is a specification area (e.g., `crud/`, `transactions/`, `auth/`). Specs are written +in GitHub Flavored Markdown at 120-character line width, following the +[MongoDB Documentation Style Guidelines](https://www.mongodb.com/docs/meta/style-guide/). Each spec typically contains: + +- `*.md` — the specification itself, with a `## Changelog` section at the bottom +- `tests/` — YAML test files (human-editable) and auto-generated JSON (never edit manually) + +### Changelog Format + +Each spec file contains its own `## Changelog` section (not a separate file). Entries are dated and prepended at the +top: + +```markdown +## Changelog + +- 2026-05-13: Describe the change made. + +- 2024-03-01: Previous change. +``` + +### Unified Test Format + +The unified test format (`source/unified-test-format/`) defines a YAML/JSON schema for cross-driver tests. Key rules: + +- **Always use the lowest possible schema version** that satisfies a test's requirements — do not default to the latest +- YAML files are the source of truth; JSON files are auto-generated and committed +- Schema versions live in `source/unified-test-format/schema-*.json`; `schema-latest.json` must match the highest + version (run `make update-schema-latest -C source` to update it) +- CI validates test files against the declared schema version using `ajv-cli`. Using any fields from a newer schema + version than declared will fail validation. + +### Test File Conventions + +- YAML test files are human-editable and located in `tests/` subdirectories +- JSON files are generated automatically by CI from YAML — never edit `.json` test files manually +- Use `runOnRequirements` to restrict tests to specific server versions/topologies +- Topologies to consider: standalone, replica set, sharded cluster, load balanced + +### Documentation Build (`mkdocs.yml`) + +MkDocs with `pymdown-extensions` and `mkdocs-github-admonitions-plugin`. The navigation is largely auto-generated via +`scripts/generate_index.py`. The build must pass in `--strict` mode (no warnings). + +## Test Authoring Rules + +### Immutability of Existing Tests + +**Do not modify existing tests** unless they are testing incorrect behavior. Default to creating new tests or new test +files instead of altering existing ones. + +- Test files can only be deleted once **no driver runs them anymore**. +- For spec changes that remove functionality: use `runOnRequirements` (unified tests) or have drivers skip the test + (non-unified tests like SDAM). +- Outdated prose tests must not be removed — mark them as such (e.g., strikethrough or *Removed*). + +### Prose Test Numbering + +Always use relative numbered bullets (`1.`) for prose tests. **New tests must be appended at the end** of the list, +since drivers may reference existing tests by number. + +### Test Isolation + +Only test functionality directly related to the new spec requirements. Omit irrelevant fields in command expectations. +This makes tests more resilient against future spec updates. + +## Linting & Formatting + +| Tool | Purpose | +| --------------------- | ------------------------------------------- | +| `mdformat` | Auto-formats Markdown (GFM, 120-char lines) | +| `markdown-link-check` | Validates links | +| `codespell` | Spell checking | +| `shellcheck` | Shell script linting | + +All checks run via `pre-commit`. CI enforces them in `.github/workflows/lint.yml`. + +## PR Requirements + +Per the PR template and project workflow: + +- Title must include a DRIVERS ticket (e.g., `DRIVERS-1234`) +- Update the `## Changelog` section in each modified spec file +- Test changes in at least one language driver +- Include links to the driver implementation PRs in the PR description (e.g., + `Python implementation: https://github.com/mongodb/mongo-python-driver/pull/…`) +- Tests must pass against all supported server versions and topologies + +## Specification Writing Guidelines + +From `source/driver-mantras.md`: + +- Prefer **MUST** over **SHOULD** — wishy-washy specs produce incompatible drivers +- Be topology-agnostic wherever possible +- Minimize configuration options ("No Knobs" principle) +- Check wire protocol version, not server version +- Follow semantic versioning for behavior changes +- Design for the next release, not hypothetical future ones ("Defy augury") diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/LICENSE.md b/LICENSE.md index 604209a804..f587604443 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,359 +1,207 @@ Creative Commons Legal Code -Attribution-ShareAlike 3.0 Unported +Attribution-NonCommercial-ShareAlike 3.0 United States - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR - DAMAGES RESULTING FROM ITS USE. +```text +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL +SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT +RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. +CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, +AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. +``` License -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). +THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS +LICENSE OR COPYRIGHT LAW IS PROHIBITED. -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO +THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN +CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined below) for the purposes of this - License. - c. "Creative Commons Compatible License" means a license that is listed - at https://creativecommons.org/compatiblelicenses that has been - approved by Creative Commons as being essentially equivalent to this - License, including, at a minimum, because that license: (i) contains - terms that have the same purpose, meaning and effect as the License - Elements of this License; and, (ii) explicitly permits the relicensing - of adaptations of works made available under that license under this - License or a Creative Commons jurisdiction license with the same - License Elements as this License. - d. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - e. "License Elements" means the following high-level license attributes - as selected by Licensor and indicated in the title of this License: - Attribution, ShareAlike. - f. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - g. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - h. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - i. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - j. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - k. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(c), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(c), as requested. - b. You may Distribute or Publicly Perform an Adaptation only under the - terms of: (i) this License; (ii) a later version of this License with - the same License Elements as this License; (iii) a Creative Commons - jurisdiction license (either this or a later license version) that - contains the same License Elements as this License (e.g., - Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible - License. If you license the Adaptation under one of the licenses - mentioned in (iv), you must comply with the terms of that license. If - you license the Adaptation under the terms of any of the licenses - mentioned in (i), (ii) or (iii) (the "Applicable License"), you must - comply with the terms of the Applicable License generally and the - following provisions: (I) You must include a copy of, or the URI for, - the Applicable License with every copy of each Adaptation You - Distribute or Publicly Perform; (II) You may not offer or impose any - terms on the Adaptation that restrict the terms of the Applicable - License or the ability of the recipient of the Adaptation to exercise - the rights granted to that recipient under the terms of the Applicable - License; (III) You must keep intact all notices that refer to the - Applicable License and to the disclaimer of warranties with every copy - of the Work as included in the Adaptation You Distribute or Publicly - Perform; (IV) when You Distribute or Publicly Perform the Adaptation, - You may not impose any effective technological measures on the - Adaptation that restrict the ability of a recipient of the Adaptation - from You to exercise the rights granted to that recipient under the - terms of the Applicable License. This Section 4(b) applies to the - Adaptation as incorporated in a Collection, but this does not require - the Collection apart from the Adaptation itself to be made subject to - the terms of the Applicable License. - c. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Ssection 3(b), in the case of an - Adaptation, a credit identifying the use of the Work in the Adaptation - (e.g., "French translation of the Work by Original Author," or - "Screenplay based on original Work by Original Author"). The credit - required by this Section 4(c) may be implemented in any reasonable - manner; provided, however, that in the case of a Adaptation or - Collection, at a minimum such credit will appear, if a credit for all - contributing authors of the Adaptation or Collection appears, then as - part of these credits and in a manner at least as prominent as the - credits for the other contributing authors. For the avoidance of - doubt, You may only use the credit required by this Section for the - purpose of attribution in the manner set out above and, by exercising - Your rights under this License, You may not implicitly or explicitly - assert or imply any connection with, sponsorship or endorsement by the - Original Author, Licensor and/or Attribution Parties, as appropriate, - of You or Your use of the Work, without the separate, express prior - written permission of the Original Author, Licensor and/or Attribution - Parties. - d. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. + a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its + entirety in unmodified form, along with one or more other contributions, constituting separate and independent + works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be + considered a Derivative Work (as defined below) for the purposes of this License. b. "Derivative Work" means a work + based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, + dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, + condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that + constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the + avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this + License. c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms + of this License. d. "Original Author" means the individual, individuals, entity or entities who created the Work. + e. "Work" means the copyrightable work of authorship offered under the terms of this License. f. "You" means an + individual or entity exercising rights under this License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission from the Licensor to exercise rights under this + License despite a previous violation. g. "License Elements" means the following high-level license attributes as + selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike. + +2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, + first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other + applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, + royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the + rights in the Work as stated below: + + a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as + incorporated in the Collective Works; b. to create and reproduce Derivative Works provided that any such Derivative + Work, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise + identify that changes were made to the original Work. For example, a translation could be marked "The original work + was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a + digital audio transmission the Work including as incorporated in Collective Works; d. to distribute copies or + phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission + Derivative Works; + + The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights + include the right to make such modifications as are technically necessary to exercise the rights in other media and + formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights + set forth in Sections 4(e) and 4(f). + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following + restrictions: + + a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the + terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with + every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally + perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability + of a recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may + not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of + warranties. When You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You + may not impose any technological measures on the Work that restrict the ability of a recipient of the Work from You + to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the + Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself + to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You + must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(d), as + requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, + remove from the Derivative Work any credit as required by Section 4(d), as requested. b. You may distribute, + publicly display, publicly perform, or publicly digitally perform a Derivative Work only under: (i) the terms of + this License; (ii) a later version of this License with the same License Elements as this License; or, (iii) either + the unported Creative Commons license or a Creative Commons license for another jurisdiction (either this or a + later license version) that contains the same License Elements as this License (e.g. + Attribution-NonCommercial-ShareAlike 3.0 (Unported)) ("the Applicable License"). You must include a copy of, or the + Uniform Resource Identifier for, the Applicable License with every copy or phonorecord of each Derivative Work You + distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any + terms on the Derivative Works that restrict the terms of the Applicable License or the ability of a recipient of + the Work to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep + intact all notices that refer to the Applicable License and to the disclaimer of warranties. When You distribute, + publicly display, publicly perform, or publicly digitally perform the Derivative Work, You may not impose any + technological measures on the Derivative Work that restrict the ability of a recipient of the Derivative Work from + You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) + applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work + apart from the Derivative Work itself to be made subject to the terms of the Applicable License. c. You may not + exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or + directed toward commercial advantage or private monetary compensation. The exchange of the Work for other + copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or + directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary + compensation in connection with the exchange of copyrighted works. d. If You distribute, publicly display, publicly + perform, or publicly digitally perform the Work (as defined in Section 1 above) or any Derivative Works (as defined + in Section 1 above) or Collective Works (as defined in Section 1 above), You must, unless a request has been made + pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or + means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) + if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing + entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by + other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent + reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the + Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, consistent + with Section 3(b) in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work + (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original + Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, + that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear, if a credit for all + contributing authors of the Derivative Work or Collective Work appears, then as part of these credits and in a + manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may + only use the credit required by this Section for the purpose of attribution in the manner set out above and, by + exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection + with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of + You or Your use of the Work, without the separate, express prior written permission of the Original Author, + Licensor and/or Attribution Parties. + + e. For the avoidance of doubt, where the Work is a musical composition: + + ```text + i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to + collect whether individually or, in the event that Licensor is a member of a performance + rights society (e.g. ASCAP, BMI, SESAC), via that society, royalties for the public + performance or public digital performance (e.g. webcast) of the Work if that performance is + primarily intended for or directed toward commercial advantage or private monetary + compensation. + ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to + collect, whether individually or via a music rights agency or designated agent (e.g. Harry + Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and + distribute, subject to the compulsory license created by 17 USC Section 115 of the US + Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover + version is primarily intended for or directed toward commercial advantage or private + monetary compensation. + ``` + + f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, + Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. + SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory + license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your + public digital performance is primarily intended for or directed toward commercial advantage or private monetary + compensation. 5. Representations, Warranties and Disclaimer -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT + OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY + KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF + TITLE, MARKETABILITY, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT + OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS + DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU + ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS + LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. + a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of + this License. Individuals or entities who have received Derivative Works (as defined in Section 1 above) or + Collective Works (as defined in Section 1 above) from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, + 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license + granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, + Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to withdraw this License (or any other license + that has been, or is required to be, granted under the terms of this License), and this License will continue in + full force and effect unless terminated as stated above. 8. Miscellaneous - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - + a. Each time You distribute or publicly digitally perform the Work (as defined in Section 1 above) or a Collective + Work (as defined in Section 1 above), the Licensor offers to the recipient a license to the Work on the same terms + and conditions as the license granted to You under this License. b. Each time You distribute or publicly digitally + perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and + conditions as the license granted to You under this License. c. If any provision of this License is invalid or + unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the + terms of this License, and without further action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of + this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement + between the parties with respect to the Work licensed here. There are no understandings, agreements or + representations with respect to the Work not specified here. Licensor shall not be bound by any additional + provisions that may appear in any communication from You. This License may not be modified without the mutual + written agreement of the Licensor and You. Creative Commons Notice - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. +Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative +Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without +limitation any general, special, incidental or consequential damages arising in connection to this license. +Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor +hereunder, it shall have all rights and obligations of Licensor. - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of the License. +Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons +does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of +Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with +Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of +this License. - Creative Commons may be contacted at https://creativecommons.org/. +Creative Commons may be contacted at https://creativecommons.org/. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..a355b3ce52 --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +# MongoDB Specifications + +[![Documentation Status](https://readthedocs.org/projects/specifications/badge/?version=latest)](http://specifications.readthedocs.io/en/latest/?badge=latest) + +This repository holds in progress and completed specification for features of MongoDB, Drivers, and associated products. +Also contained is a rudimentary system for producing these documents. + +## Driver Mantras + +See [Documentation](./source/driver-mantras.md). + +## Writing Documents + +Write documents using [GitHub Flavored Markdown](https://github.github.com/gfm/), following the +[MongoDB Documentation Style Guidelines](https://www.mongodb.com/docs/meta/style-guide/). + +Store all source documents in the `source/` directory. + +## Linting + +This repo uses [pre-commit](https://pypi.org/project/pre-commit/) for managing linting. `pre-commit` performs various +checks on the files and uses tools that help follow a consistent style within the repo. + +To set up `pre-commit` locally, run: + +```bash +brew install pre-commit +pre-commit install +``` + +To run `pre-commit` manually, run `pre-commit run --all-files`. + +To run a manual hook like `shellcheck` manually, run: + +```bash +pre-commit run --all-files --hook-stage manual shellcheck +``` + +## Prose Test Numbering + +When numbering prose tests, always use relative numbered bullets (`1.`). New tests must be appended at the end of the +test list, since drivers may refer to existing tests by number. + +Outdated tests must not be removed completely, but may be marked as such (e.g. by striking through or replacing the +entire test with a note (e.g. *Removed*). + +## Automated Test Best Practices + +### Immutability of Existing Tests + +**Do not modify existing tests**, unless they are testing incorrect behavior. Default to creating new tests or test +files instead of altering existing ones. + +Test files can only be deleted once no driver runs them anymore. In the meantime, for cases where a spec change removes +functionality: + +- **Unified Tests:** Use `runOnRequirements` to ensure tests are only executed by drivers supporting the required + functionality. +- **Non-Unified Tests (e.g., SDAM):** Drivers should skip tests that no longer apply to them. + +### Test Isolation + +When creating a new test, only test functionality directly related to the new spec requirements. Omit irrelevant fields +in command expectations. + +This makes tests more resilient against spec updates and avoids needing to change tests down the line. + +### Schema Version Usage + +Use the **lowest possible schema version** for each test. + +Do NOT default to using the latest unified test format schema version, as the drivers may not all implement it. Use the +oldest schema version that supports all functionality used in the test, even if it requires creating a new test file +with a lower schema version. + +## Building Documents + +We use [mkdocs](https://www.mkdocs.org/) to render the documentation. To see a live view of the documentation, in a +Python [venv](https://docs.python.org/3/library/venv.html) run: + +```bash +pip install -r source/requirements.txt +mkdocs serve +``` + +To build the docs, use `mkdocs build`. + +In CI we verify that there are no warnings. To replicate locally, run `mkdocs build --strict`. + +## Converting to JSON + +There are many YAML to JSON converters. There are even several converters called `yaml2json` in NPM. Alas, we are not +using `yaml2json` anymore, but instead the [js-yaml](https://www.npmjs.com/package/js-yaml) package. Use only that +converter, so that JSON is formatted consistently. + +Run `npm install -g js-yaml`, then run `make` in the `source` directory at the top level of this repository to convert +all YAML test files to JSON. + +## Licensing + +All the specs in this repository are available under the +[Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License](https://creativecommons.org/licenses/by-nc-sa/3.0/us/). diff --git a/README.rst b/README.rst deleted file mode 100644 index 5ed39278b4..0000000000 --- a/README.rst +++ /dev/null @@ -1,69 +0,0 @@ -====================== -MongoDB Specifications -====================== - -This repository holds in progress and completed specification for -features of MongoDB, Drivers, and associated products. Also contained -is a rudimentary system for producing these documents. - -Writing Documents ------------------ - -Write documents using `reStructuredText`_, following the `MongoDB -Documentation Style Guidelines `_. - -Store all source documents in the ``source/`` directory. - -.. _`reStructuredText`: http://docutils.sourceforge.net/rst.html - -Building Documents ------------------- - -To build documents issue the ``make`` command in a local copy of this -repository. The output PDFs end up in the ``build/`` directory. The -build depends on: - -- `Python Docutils `_ - -- A functioning basic LaTeX/TeX install with ``pdflatex``. If you run - OS X, use `MacTeX`_ - -``make all`` will build all documents in the ``source/`` folder. The -system builds all targets in ``build/``. - -Run ``make setup`` to generate (or regenerate) a ``makefile.generated`` -file which provides specific targets for all files in the source file -so you can choose to compile only some of the files that you -need. Once generated, running "``make [file-name-without-extension]``" -will rebuild only those files (if needed.) - -Use ``make clean`` to remove the ``build/`` directory and "``make -cleanup``" to remove the LaTeX by-products from ``build/``. - -.. _`MacTeX` : http://www.tug.org/mactex/ - -Converting to JSON ------------------- - -There are many YAML to JSON converters. There are even several converters called -``yaml2json`` in NPM. Alas, we are not using ``yaml2json`` anymore, but instead -the `js-yaml `_ package. Use only that -converter, so that JSON is formatted consistently. - -Run ``npm install -g js-yaml``, then run ``make`` in the ``source`` directory -at the top level of this repository to convert all YAML test files to JSON. - -Licensing ----------------- -All the specs in this repository are available under the `Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License `_. - -In the future... ----------------- - -- Templates will have logos, and templates for authorship, copyright, - disclaimers, etc. - -- Non-PDF output targets. - -If you have specific feature requests, or need help getting things -running, please contact samk@10gen.com. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..7dabff0028 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,7 @@ +# MongoDB Non-Drivers Specifications + +Supporting material for various MongoDB Drivers Specifications lives here. + +## Authentication + +- [Requirements for Client Applications implementing Workforce (Human) OIDC Auth For MongoDB](./workforce-human-oidc-auth.md) diff --git a/docs/workforce-human-oidc-auth.md b/docs/workforce-human-oidc-auth.md new file mode 100644 index 0000000000..baad3b4574 --- /dev/null +++ b/docs/workforce-human-oidc-auth.md @@ -0,0 +1,269 @@ +# Requirements for Client Applications implementing Workforce (Human) OIDC Auth For MongoDB + +# Overview + +## Abstract + +MongoDB offers OpenID Connect (OIDC) authentication and authorization for database users. OIDC auth in clients generally +falls into one of two categories: either Workflow OIDC targeting programmatic users, which is +[fully specified here](https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#mongodb-oidc) and does +not involve user interaction, or Workforce OIDC targeting human users, which authenticate explicitly through means such +as browsers. + +## Audience + +This document is intended for authors and maintainers of MongoDB client applications that implement Workforce OIDC +authentication, or those who wish to understand existing implementations of it. This document does *not* describe the +server implementation of OIDC, nor the details of OIDC protocol interactions that are part of external standards and +generally implemented through the use of a third-party library. + +## Meta + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Token acquisition flows + +Currently, all workforce OIDC clients in MongoDB support one or two mechanisms for acquiring OIDC tokens: + +- Authorization Code Flow with PKCE (short: Auth Code, PKCE is pronounced “pixie”) +- Device Authorization Grant (short: Device Auth) + +Auth Code Flow is generally the preferred mechanism. If an application decides to only support one of these flows, that +should be the typical choice. If an application supports both flows, it MAY default to Auth Code Flow and fall back to +Device Auth only if + +- The former is unavailable under specific circumstances, which are described in the Auth Code Flow section; and +- The user has explicitly indicated that Device Auth is enabled for this connection attempt. + +In both of these cases, the general chain of events is as follows: + +1. The user passes a connection string to the client application and indicates (possibly through said connection string) + that they desire to use `MONGODB-OIDC` as their authentication mechanism. +2. The application connects to the MongoDB endpoint and asks it for information about the Identity Provider (IdP)[^1] it + is supposed to authenticate with. If there was a username in the connection string, the application sends that as + well. +3. The MongoDB endpoint responds with information about the IdP, in the form of an `issuer` URL identifying the IdP and + other metadata about it. +4. The application looks up the public metadata document for the IdP, which includes information about user-facing URL + endpoints. +5. The application composes a URL based on those endpoints and presents it to the user in some form (the details depend + on the exact flow chosen). +6. The user visits that URL, which can result in any number of steps to authenticate the user. +7. The application receives a token from the IdP (the details again depend on the exact flow chosen). +8. The application presents this token to the MongoDB endpoint. + +Since in these steps the client application performs actions depending on information supplied by external sources, this +document specifies mechanisms that help ensure that these actions are carried out securely. MongoDB clients are “public +clients” in the sense of the OIDC specs (i.e. they do not possess a shared secret with the Identity Provider), and must +behave accordingly. + +Since these steps require the involvement of a human being, the application should be set up to gracefully handle +timeouts and provide the user with a way to abort the connection attempt. + +# General requirements + +An application implementing workforce OIDC needs to generally comply with OpenID and OAuth 2.0 standards. + +In particular: + +- Any HTTP calls made to non-local servers MUST be made using HTTPS. +- IdP metadata discovery MUST be implemented as described in [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). + +Using a well-tested and standards-compliant third-party library for core OIDC logic for the respective ecosystem is +highly recommended. If this is not possible, implementers need to pay special attention to the specifications referenced +in this document. + +## Token management + +After a successful authentication, applications SHOULD periodically attempt to use the OIDC token refresh mechanism in +order to exchange the access token it uses for a fresh one. The refresh interval SHOULD be determined by the token +expiry time indicated by the Identity Provider. If the refresh attempt fails, the application MAY ask or suggest the +user to re-authenticate from scratch, even if the currently used access token has not expired yet. + +Under normal circumstances, the application provides an access token received from the Identity Provider to the driver. +The application SHOULD provide a toggle to users that can be used to indicate that it should use the ID token, rather +than the access token, in its place.[^2] + +## OIDC Scopes + +The initial request to the IdP involves specifying the `scope` parameter. This is a space-separated unordered list of +strings which contains: + +- The scopes listed in the `requestScopes` field of the IdP response from the MongoDB endpoint, and +- The `openid` and `offline_access` scopes[^3]. If the IdP metadata document contains a `scopes_supported` field, and + this field does not list one of these scopes, then the respective scope SHOULD NOT be added to the `scope` + parameter. + +# Authorization Code Flow + +In this flow, the client application uses a local HTTP server to receive a response from the Identity Provider, as +described in [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252).[^4] The application follows these steps: + +1. Generate a code challenge for PKCE using cryptographically random data, as described in + [RFC7636](https://datatracker.ietf.org/doc/html/rfc7636). +2. Launch a local HTTP server. The default (incoming) redirect URL for MongoDB applications is + `http://localhost:27097/redirect`, which MAY be configurable. If the application allows configuring the URL, the + port MAY be specified as `0` to allow listening on an arbitrary port. The application listens on the host and port + listed in the URL. The application MUST listen on all unique addresses that the hostname resolves to through + `getaddrinfo()`, and MUST listen on the same port in all cases. If listening on any address fails, or + `getaddrinfo()` did not return any addresses, it must abort this process and the application MAY fall back to + Device Auth. + 1. To preserve privacy, pages served from this local HTTP server MUST NOT reference external resources directly. + They MAY provide external links that require user interaction. + 2. The HTTP server SHOULD set default headers of `Content-Security-Policy: default-src 'self'` and + `Referrer-Policy: no-referrer`, or similarly restrictive defaults. +3. Compose the URL for Auth Code Flow, using the code challenge generated earlier and the redirect URL from the + previous step, with the port replaced with the actual port if it was specified as `0`. + 1. The application MUST add a `state` parameter containing cryptographically random data + ([RFC6749](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1) specifies this as optional but + recommended). + 2. The application SHOULD allow the user to indicate that it should add a `nonce` parameter containing + cryptographically random data to the authentication request, as defined in the OpenID Connect specification, + which is then later embedded in the ID token itself. This option SHOULD be on-by-default. (Some Identity + Providers may effectively require a `nonce` option, but since it is an OpenID Connect feature and not an OAuth + 2.0 feature, we allow users to disable this if their Identity Provider does not support it.) +4. The application SHOULD, instead of directly presenting the URL from the previous step to the user, register that URL + as an outgoing HTTP 307 redirect from the local HTTP server. The redirect base URL SHOULD contain a piece of + cryptographically random data. The advantages of this approach are that: + 1. The application can ensure that the URL that is passed to the browser in the next step does not contain special + characters (other than the typical `/` and `:` present in URLs) which would need to be escaped when opening a + browser through a shell command. + 2. It allows tracking whether the URL has been accessed, i.e. get a definite confirmation of whether the user + successfully opened it in a browser. +5. Open a browser pointing at that URL, or instruct the user to do so. The former approach is preferred. If opening a + browser fails, the application MAY fall back to Device Auth. +6. Wait until one of the following events occurs: + 1. The local outgoing redirect URL is accessed (if any). + 2. Opening the browser fails. + 3. A timeout occurs or the connection attempt is aborted. +7. Wait until the incoming redirect URL is accessed in a valid way, i.e. using GET or POST with the correct `state` + parameter and without an `error` parameter. If the incoming redirect URL is accessed with a mismatching `state` + parameter or the `error` parameter is set, the application SHOULD display a helpful error page. If an `iss` + parameter is present, it MUST be validated according to [RFC9207](https://datatracker.ietf.org/doc/html/rfc9207), + i.e. compare equal to the `issuer` string in the issuer metadata document. Subsequent requests to the incoming + redirect URL should be rejected. + 1. If error parameters such as `error`, `error_description` and/or `error_uri` are displayed to the user, they MUST + be validated to match the `NQSCHAR` definition of [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749) (and + in the case of `error_uri`, a valid URI) before doing so, and they MUST be escaped as appropriate for the + chosen display format. +8. The application MUST redirect from the incoming redirect URL to a different URL on the same local HTTP server as a + HTTP 303 redirect, removing all query parameters and/or the request body. The target page SHOULD indicate + successful authentication to the Identity Provider, and SHOULD clarify that the user is not authenticated with the + MongoDB endpoint yet. It MAY be updated once the application successfully authenticates against the MongoDB + endpoint to reflect that. +9. Stop listening on and close the local HTTP server. As browsers may keep lingering open connections, the application + SHOULD ensure that these connections do not prevent the application from progressing to the next step (i.e. do not + block closing the server). +10. Verify the parameters it received against the initial PKCE code challenge. +11. Perform a request to the OIDC token endpoint using the code received from the IdP on the incoming redirect URL. +12. Provide the token it received from the token endpoint to the MongoDB driver. + +The developer tools team owns example templates for the local HTTP server at +[https://github.com/mongodb-js/devtools-shared/tree/main/packages/oidc-http-server-pages](https://github.com/mongodb-js/devtools-shared/tree/main/packages/oidc-http-server-pages), +and publishes templates that can be accessed at e.g. +[https://unpkg.com/@mongodb-js/oidc-http-server-pages/dist/templates.gz](https://unpkg.com/@mongodb-js/oidc-http-server-pages/dist/templates.gz). + +# Device Auth Grant + +This flow is optional, and MUST NOT be used unless either: + +- The application has been unable to open a browser locally (e.g. in headless environments), or unable to listen on a + local HTTP endpoint, and the user has explicitly indicated that falling back to this flow is acceptable; or +- The user has explicitly requested this flow. + +It is significantly simpler than the Auth Code Flow, but generally considered the less secure one between the two, and +more susceptible to e.g. phishing attacks. + +The application follows these steps: + +1. Reach out to the IdP device authorization endpoint. This should result in a URL and a “user code”. +2. Present the URL and the user code to the user, expecting the user to open that URL manually (possibly on a different + device). +3. Repeatedly poll the token endpoint for an access token. +4. Provide the token it received from the token endpoint to the MongoDB driver. + +# Diagnostics + +It is recommended to log the following events for diagnostic purposes: + +- All outgoing HTTP calls, minus sensitive information (e.g. URLs, with search parameter values redacted) +- All inbound HTTP calls, minus sensitive information +- Starting to listen on the local HTTP server, including port and address(es), and whether that was successful or not +- Incoming redirect to the local HTTP server accessed and whether the redirect was accepted or rejected +- Closing of the local HTTP server +- Browser opening and whether it were successful or not +- Acquisition of new tokens from the Identity Provider, and in particular whether an ID token was present or not +- Token refresh attempts and whether they were successful or not + +# Appendix: Relevant standards (non-exhaustive) + +- [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749): The OAuth 2.0 Authorization Framework +- [RFC6819](https://datatracker.ietf.org/doc/html/rfc6819): OAuth 2.0 Threat Model and Security Considerations +- [RFC7636](https://datatracker.ietf.org/doc/html/rfc7636): Proof Key for Code Exchange by OAuth Public Clients (PKCE) +- [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252): OAuth 2.0 for Native Apps +- [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414): OAuth 2.0 Authorization Server Metadata +- [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628): OAuth 2.0 Device Authorization Grant +- [RFC8707](https://datatracker.ietf.org/doc/html/rfc8707): Resource Indicators for OAuth 2.0 +- [RFC9207](https://datatracker.ietf.org/doc/html/rfc9207): OAuth 2.0 Authorization Server Issuer Identification +- [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449): OAuth 2.0 Demonstrating Proof of Possession (DPoP) +- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) +- [Draft](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics): OAuth 2.0 Security Best Current + Practice +- [Risk of phishing Access Tokens from clients using OIDC Authentication](https://docs.google.com/document/d/1TdcBtRu4yNXQkI7ZdKWZlSIaWs29tIQblyS3805nK1A/edit?tab=t.0) + +# Appendix A: Multiple MongoClients + +Some applications may require support for multiple concurrent MongoClients using the same OIDC tokens. In this case, a +token set (access token, ID token and refresh token) may be re-used, if and only if: + +- The `username` parameter is equal +- The IdP metadata provided by the MongoDB endpoint is equal +- Both token sets contain an ID token and the respective `aud` and `sub` claims[^5] of the ID tokens are equal, OR + neither token set contains an ID token + +If an application supports multiple MongoClients, it MUST ensure that only one token acquisition flow is in use at the +same time, and coordinate token refresh intervals accordingly. + +The Developer Tools team maintains an implementation that integrates with multiple MongoClient instances at +[https://github.com/mongodb-js/oidc-plugin](https://github.com/mongodb-js/oidc-plugin), which can be used as a reference +implementation (and which can generally be used in other applications based on the Node.js driver, although as a +standalone package it is not considered a supported product of MongoDB). + +# Appendix B: Future intentions for endpoint restrictions + +Currently, users who connect to a host other than localhost or an Atlas hostname need to specify this host in the +`ALLOWED_HOSTS` auth mechanism property. In the future, MongoDB is hoping to support Demonstrating Proof of Possession +(DPoP, [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449)) which will allow lifting this restriction. The goal +here of either of these mechanisms is to prevent users from connecting to untrusted endpoints that could advertise +attacker-controlled IdP metadata and intercept tokens intended for other clusters (or even non-MongoDB OIDC +applications). + +We would also like to adopt [RFC8707](https://datatracker.ietf.org/doc/html/rfc8707), but have not decided on a specific +format for expressing MongoDB clusters as resource URLs. + +## Changelog + +- 2024-11-14: Initial version. +- 2025-02-27: Specify that the local http server in [Authorization Code Flow](#authorization-code-flow) must listen on + unique addresses. + +[^1]: Technically, this refers to an Authorization Server (AS). Inside MongoDB, the usage of AS and IdP has been + considered more or less interchangeable. + +[^2]: At the time of writing, there are Identity Providers which do not support JWT access tokens, but which do provide + JWT ID tokens that are usable for authentication. This requires opt-in on both the server configuration side and the + client side. + +[^3]: The `offline_access` scope is what provides the ability to refresh tokens without performing a full + re-authentication. Some particularly security-sensitive customers may choose this approach at the expense of a + greatly diminished UX, where users need to frequently re-authenticate. + +[^4]: [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252) lays out alternatives to this approach, such as registering + a custom URI scheme handler or registering “Claims” on specific HTTPS URLs, but we’ve decided not to pursue these + approaches in any existing applications, since some of our applications are CLI applications without an installation + step where neither of these alternative approaches is feasible. + +[^5]: OIDC as a protocol allows for `aud` claims that contain multiple strings, instead of a single one. MongoDB endpoints + are [restricted](https://jira.mongodb.org/browse/SERVER-86607) to single-value `aud` claims, although client + applications do not need to be concerned with this restriction. diff --git a/markdown_link_config.json b/markdown_link_config.json new file mode 100644 index 0000000000..69f0a8fbf7 --- /dev/null +++ b/markdown_link_config.json @@ -0,0 +1,16 @@ +{ + "ignorePatterns": [ + { + "pattern": "^#" + }, + { + "pattern": "^https://github.com/10gen/mongo-enterprise-modules" + }, + { + "pattern": "^https://github.com/10gen/mongohouse" + }, + { + "pattern": "cloudkms.googleapis.com" + } + ] +} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..216d68305a --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,22 @@ +site_name: MongoDB Specifications +docs_dir: source +nav: + - 'index.md' +markdown_extensions: + - admonition + - footnotes + - pymdownx.superfences + - pymdownx.arithmatex + - pymdownx.escapeall + - toc: + permalink:  +plugins: + - gh-admonitions +validation: + absolute_links: warn + unrecognized_links: warn + anchors: warn +extra_javascript: + - 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS_CHTML' +extra_css: + - extra.css \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..ea8cd91943 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,42 @@ +# Scripts for use with Drivers specifications repository + +## migrate_to_md + +Use this file to automate the process of converting a specification from rst to GitHub Flavored Markdown. + +The goal of the script is to do most of the work, including updating relative links to the new document from other +files. Note that all known features will translate, including explicit cross-reference markers (e.g. `.. _foo`) which +are translated to a `
`. It will also create a new changelog entry with today's date marking the +conversion. + +### Prerequisites + +```bash +brew install pandoc +brew install pre-commit +brew install python # or get python through your preferred channel +pre-commit install +``` + +### Usage + +- Run the script as: + +```bash +python3 scripts/migrate_to_md.py "source/" +``` + +- Address any errors that were printed during the run. + +- Ensure that the generated markdown file is properly formatted. + +- Ensure that the links in the new file are working, by running `pre-commit run markdown-link-check` and addressing + failures until that passes. + +- Remove the rst file using `git rm`. + +- Create a PR. When you commit the changes, the `mdformat` `pre-commit` hook will update the formatting as appropriate. + +## generate_index + +Use this file to generate the top level Markdown index file. It is independent to be used as a pre-commit hook. diff --git a/scripts/check_links.py b/scripts/check_links.py new file mode 100644 index 0000000000..5dc25d2f68 --- /dev/null +++ b/scripts/check_links.py @@ -0,0 +1,29 @@ +import sys, re + +fname = sys.argv[-1] + +# Roughly detect fenced code even inside block quotes +fenced_code = re.compile(r"^\s*(>\s+)*```") + +# Check for markdown links that got improperly line wrapped. +in_code_block = False +with open(fname) as fid: + for line in fid: + # Ignore code blocks. + if fenced_code.match(line): + in_code_block = not in_code_block + if in_code_block: + continue + id0 = line.index("[") if "[" in line else -1 + id1 = line.index("]") if "]" in line else -1 + id2 = line.index("(") if "(" in line else -1 + id3 = line.index(")") if ")" in line else -1 + if id1 == -1 or id2 == -1 or id3 == -1: + continue + if id2 < id1 or id3 < id2: + continue + if id0 == -1: + print("*** Malformed link in line:", line, fname) + sys.exit(1) + +assert not in_code_block diff --git a/scripts/check_md_html.py b/scripts/check_md_html.py new file mode 100644 index 0000000000..cd4a296c11 --- /dev/null +++ b/scripts/check_md_html.py @@ -0,0 +1,55 @@ +import sys, re + +fname = sys.argv[-1] + +# Check for allowed HTML elements in markdown. +# Ignores inline and fenced code, but intentionally doesn't ignore backslash +# escaping. (For compatibility, we want to avoid unintentional inline HTML +# even on markdown implementations where "\<" escapes are not supported.) + +disallowed_re = re.compile( + r""" + [^`]*(`[^`]+`)* + <(?! + - | + /p> | + /span> | + /sub> | + /sup> | + /table> | + /td> | + /tr> | + \d | + \s | + \w+@(\w+\.)+\w+> | # Cover email addresses in license files + = | + br> | + https:// | # Cover HTTPS links but not HTTP + p> | + span[\s>] | + sub> | + sup> | + table[\s>] | + td[\s>] | + tr> | + !-- ) + """, + re.VERBOSE, +) + +# Roughly detect fenced code even inside block quotes +fenced_code = re.compile(r"^\s*(>\s+)*```") + +in_code_block = False +with open(fname) as fid: + for line in fid: + # Ignore code blocks. + if fenced_code.match(line): + in_code_block = not in_code_block + if in_code_block: + continue + if disallowed_re.match(line): + print("*** Markdown contains unexpected HTML in line:", line, fname) + sys.exit(1) + +assert not in_code_block diff --git a/scripts/generate_index.py b/scripts/generate_index.py new file mode 100644 index 0000000000..94c1ca253a --- /dev/null +++ b/scripts/generate_index.py @@ -0,0 +1,30 @@ +import os +from pathlib import Path + +source = Path(__file__).resolve().parent.parent / "source" +source = source.resolve() +info = {} +for p in Path(source).rglob("*.md"): + relpath = os.path.relpath(p.parent, start=source) + if "tests" in relpath: + continue + if "node_modules" in relpath: + continue + if p.name in ["index.md"]: + continue + fpath = relpath + "/" + p.name + name = None + with p.open() as fid: + for line in fid: + if line.startswith("# "): + name = line.replace("# ", "").strip() + break + if name is None: + raise ValueError(f"Could not find name for {fpath}") + info[name] = fpath + +index_file = source / "index.md" +with index_file.open("w") as fid: + fid.write("# MongoDB Specifications\n\n") + for name in sorted(info): + fid.write(f"- [{name}]({info[name]})\n") diff --git a/scripts/migrate_to_md.py b/scripts/migrate_to_md.py new file mode 100644 index 0000000000..2b87db29ef --- /dev/null +++ b/scripts/migrate_to_md.py @@ -0,0 +1,178 @@ +import subprocess +import os +import re +import sys +from pathlib import Path +import datetime +import subprocess + +if len(sys.argv) < 2: + print("Must provide a path to an RST file") + sys.exit(1) + +path = Path(sys.argv[1]) + +# Ensure git history for the md file. +md_file = str(path).replace(".rst", ".md") +subprocess.check_call(["git", "mv", path, md_file]) +subprocess.check_call(["git", "add", md_file]) +subprocess.check_call( + ["git", "commit", "--no-verify", "-m", f"Rename {path} to {md_file}"] +) +subprocess.check_call(["git", "checkout", "HEAD~1", path]) +subprocess.check_call(["git", "add", path]) + +# Get the contents of the file. +with path.open() as fid: + lines = fid.readlines() + +TEMPLATE = """ +.. note:: + This specification has been converted to Markdown and renamed to + `{0} <{0}>`_. +""" + +# Update the RST file with a stub pointer to the MD file. +if not path.name == "README.rst": + new_body = TEMPLATE.format(os.path.basename(md_file)) + with path.open("w") as fid: + fid.write("".join(new_body)) + +# Pre-process the file. +for i, line in enumerate(lines): + # Replace curly quotes with regular quotes. + line = line.replace("”", '"') + line = line.replace("“", '"') + line = line.replace("’", "'") + line = line.replace("‘", "'") + lines[i] = line + + # Replace the colon fence blocks with bullets, + # e.g. :Status:, :deprecated:, :changed:. + # This also includes the changelog entries. + match = re.match(r":(\S+):(.*)", line) + if match: + name, value = match.groups() + lines[i] = f"- {name.capitalize()}:{value}\n" + + # Handle "":Minimum Server Version:"" as a block quote. + if line.strip().startswith(":Minimum Server Version:"): + lines[i] = "- " + line.strip()[1:] + "" + + # Remove the "".. contents::" block - handled by GitHub UI. + if line.strip() == ".. contents::": + lines[i] = "" + +# Run pandoc and capture output. +proc = subprocess.Popen( + ["pandoc", "-f", "rst", "-t", "gfm"], stdin=subprocess.PIPE, stdout=subprocess.PIPE +) +data = "".join(lines).encode("utf8") +outs, _ = proc.communicate(data) +data = outs.decode("utf8") + +# Fix the strings that were missing backticks. +data = re.sub(r'', "`", data, flags=re.MULTILINE) +data = data.replace("", "`") + +# Handle div blocks that were created. +# These are admonition blocks, convert to new GFM format. +# Also add a changelog entry. +in_block_outer = False +in_block_inner = False +in_changelog_first = False +lines = data.splitlines() +new_lines = [] +for i, line in enumerate(lines): + match = re.match(r'
', line) + if not in_block_outer and match: + in_block_outer = True + new_lines.append(f"> [!{match.groups()[0].upper()}]") + continue + if line.strip() == "
": + if in_block_outer: + in_block_outer = False + in_block_inner = True + elif in_block_inner: + in_block_inner = False + continue + if in_block_inner: + line = "> " + line.strip() + + if in_changelog_first: + today = datetime.date.today().strftime("%Y-%m-%d") + line = f"\n- {today}: Migrated from reStructuredText to Markdown." + in_changelog_first = False + + if line.strip() == "## Changelog": + in_changelog_first = True + + if not in_block_outer: + new_lines.append(line) + + +# Write the new content to the markdown file. +with open(md_file, "w") as fid: + fid.write("\n".join(new_lines)) + +# Handle links in other files. +# We accept relative path links or links to master +# (https://github.com/mongodb/specifications/blob/master/source/...) +# and rewrite them to use appropriate md links. +# If the link is malformed we ignore and print an error. +target = path.name +curr = path +while curr.parent.name != "source": + target = f"{curr.parent.name}/{target}" + curr = curr.parent +suffix = rf"\S*/{target}" +rel_pattern = re.compile(rf"(\.\.{suffix})") +md_pattern = re.compile(rf"(\(http{suffix})") +html_pattern = re.compile(f"(http{suffix})") +abs_pattern = re.compile(f"(/source{suffix})") +for p in Path("source").rglob("*"): + if p.suffix not in [".rst", ".md"]: + continue + with p.open() as fid: + lines = fid.readlines() + new_lines = [] + changed_lines = [] + relpath = os.path.relpath(md_file, start=p.parent) + for line in lines: + new_line = line + if re.search(rel_pattern, line): + matchstr = re.search(rel_pattern, line).groups()[0] + new_line = line.replace(matchstr, relpath) + elif re.search(md_pattern, line): + matchstr = re.search(md_pattern, line).groups()[0] + if not matchstr.startswith( + "(https://github.com/mongodb/specifications/blob/master/source" + ): + print("*** Error in link: ", matchstr, p) + else: + new_line = line.replace(matchstr, f"({relpath}") + elif re.search(html_pattern, line): + matchstr = re.search(html_pattern, line).groups()[0] + if not matchstr.startswith( + "https://github.com/mongodb/specifications/blob/master/source" + ): + print("*** Error in link: ", matchstr, p) + else: + new_line = line.replace(matchstr, f"{relpath}") + elif re.search(abs_pattern, line): + matchstr = re.search(abs_pattern, line).groups()[0] + new_line = line.replace(matchstr, relpath) + + if new_line != line: + changed_lines.append(new_line) + new_lines.append(new_line) + + if changed_lines: + with p.open("w") as fid: + fid.writelines(new_lines) + print("-" * 80) + print(f"Updated link(s) in {p}...") + print(" " + "\n ".join(changed_lines)) + +print("Created markdown file:") +print(md_file) diff --git a/scripts/yaml2json.mjs b/scripts/yaml2json.mjs new file mode 100644 index 0000000000..471f6d446b --- /dev/null +++ b/scripts/yaml2json.mjs @@ -0,0 +1,17 @@ +#!/usr/bin/env node +// Usage: node yaml2json.mjs +// Converts a YAML file to JSON, with YAML 1.1 merge key (<<:) support for legacy tests. +// TODO(DRIVERS-3178): after removing legacy tests, consider removing this script and invoking `js-yaml` directly. +import { readFileSync } from 'fs'; +import { execSync } from 'child_process'; + +// Import globally installed `js-yaml` package: +const globalRoot = execSync('npm root -g').toString().trim(); +const pkg = JSON.parse(readFileSync(`${globalRoot}/js-yaml/package.json`, 'utf8')); +const entry = pkg.exports?.['.']?.import ?? pkg.module ?? pkg.main; +const { load, CORE_SCHEMA, mergeTag } = await import(`${globalRoot}/js-yaml/${entry}`); + +const file = process.argv[2]; +const content = readFileSync(file, 'utf8'); +const data = load(content, { schema: CORE_SCHEMA.withTags(mergeTag) }); +process.stdout.write(JSON.stringify(data, null, 2) + '\n'); diff --git a/source/Makefile b/source/Makefile index eaa584eb6b..51ef248a36 100644 --- a/source/Makefile +++ b/source/Makefile @@ -4,12 +4,12 @@ JSON_FILES=$(patsubst %.yml,%.json,$(YAML_FILES)) all: $(JSON_FILES) HAS_JSYAML %.json: %.yml - tmpfile=$$(mktemp); \ - if js-yaml $< > "$$tmpfile"; then \ - mv "$$tmpfile" $@; \ - else \ - rm "$$tmpfile"; \ - exit 1; \ + tmpfile=$$(mktemp); \ + if node $(dir $(lastword $(MAKEFILE_LIST)))/../scripts/yaml2json.mjs $< > "$$tmpfile"; then \ + mv "$$tmpfile" $@; \ + else \ + rm "$$tmpfile"; \ + exit 1; \ fi HAS_JSYAML: @@ -17,3 +17,16 @@ HAS_JSYAML: echo 'Error: need "npm install -g js-yaml"' 1>&2; \ exit 1; \ fi + +VERSION := $(shell \ + find unified-test-format -maxdepth 1 -type f -name 'schema-1.*.json' \ + | sed -E 's:.*/schema-1\.([0-9]+)\.json:\1:' \ + | sort -n \ + | tail -n1 \ +) + +.PHONY: update-schema-latest +update-schema-latest: + cp unified-test-format/schema-1.$(VERSION).json \ + unified-test-format/schema-latest.json + diff --git a/source/atlas-sfp-testing/atlas-sfp-testing.md b/source/atlas-sfp-testing/atlas-sfp-testing.md new file mode 100644 index 0000000000..b687161362 --- /dev/null +++ b/source/atlas-sfp-testing/atlas-sfp-testing.md @@ -0,0 +1,120 @@ +# Atlas Secure Frontend Processor (SFP) Testing + +- Status: Accepted +- Minimum Server Version: 7.0 + +______________________________________________________________________ + +## Abstract + +This specification defines the tests that drivers MUST run to verify connectivity and authentication through an Atlas +Secure Frontend Processor (SFP). The SFP is a proxy that sits in front of Atlas clusters to provide additional security +capabilities. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### SFP + +Secure Frontend Processor - a proxy service that sits in front of Atlas clusters, providing TLS termination, +authentication forwarding, and additional security features. + +### Test Environment + +SFP clusters are **preconfigured** and do not require provisioning or teardown as part of the test run. Drivers will be +provided with connection URIs and credentials via environment variables. + +The SFP proxy is fully transparent to drivers - all standard MongoDB operations should work exactly as they would +against a normal Atlas cluster. + +### Required Environment Variables + +The following environment variables will be available to run the tests: + +| Variable | Description | +| -------------------- | -------------------------------------------------- | +| `SFP_ATLAS_URI` | MongoDB connection URI for the SFP-proxied cluster | +| `SFP_ATLAS_USER` | Username for SCRAM authentication | +| `SFP_ATLAS_PASSWORD` | Password for SCRAM authentication | + +For X.509 authentication tests, the following additional variables are required: + +| Variable | Description | +| --------------------- | ----------------------------------------------- | +| `SFP_ATLAS_X509_URI` | MongoDB connection URI for X.509 authentication | +| `SFP_ATLAS_X509_CERT` | Path to client certificate (PEM format) | + +### Test Isolation and Cleanup + +To prevent conflicts between concurrent test runs and avoid unbounded collection growth: + +1. Drivers MUST use a unique collection name for each test run, e.g., `sfp_test_` where `` is an + ObjectID string representation +2. Drivers MUST drop the test collection after all tests complete, regardless of test success or failure + +## Required Tests + +Drivers MUST implement and run the following tests against SFP-proxied clusters. + +### Common Assertions + +The following assertions are used across multiple tests: + +#### Assertion: Ping + +1. Execute a `ping` command against the `admin` database +2. Assert that the command succeeds with `ok: 1` + +#### Assertion: Connection Status + +1. Execute a `connectionStatus` command against the `admin` database +2. Assert that the command succeeds with `ok: 1` +3. If authenticated, assert that `authInfo.authenticatedUsers` contains at least one user + +#### Assertion: CRUD Operations + +1. Insert a document into a test collection and assert the insert succeeds +2. Query the collection using `find` and assert the inserted document is returned + +### Unauthenticated Tests + +Create a `MongoClient` configured with `SFP_ATLAS_URI` but without credentials. Run the following assertions: + +- Ping +- Connection Status (assert `authenticatedUsers` is empty) + +### Authenticated Tests + +Each authenticated test MUST be run under each of the following variations: + +1. No additional configuration (baseline) +2. With at least one compressor enabled (e.g., zlib, snappy, or zstd) +3. With [Server API](../versioned-api/versioned-api.md#mongoclient-changes) version 1 + +#### SCRAM-SHA-256 + +Create a `MongoClient` with the connection string and SCRAM-SHA-256 credentials from environment variables. Run the +following assertions: + +- Ping +- Connection Status +- CRUD Operations + +#### X.509 + +Create a `MongoClient` with the connection string and X.509 authentication using the client certificate. Run the +following assertions: + +- Ping +- Connection Status +- CRUD Operations + +## Changelog + +- 2025-02-27: Initial version diff --git a/source/auth/auth.md b/source/auth/auth.md new file mode 100644 index 0000000000..efaea8779c --- /dev/null +++ b/source/auth/auth.md @@ -0,0 +1,2261 @@ +# Authentication + +- Status: Accepted +- Minimum Server Version: 2.6 + +______________________________________________________________________ + +## Abstract + +MongoDB supports various authentication strategies across various versions. When authentication is turned on in the +database, a driver must authenticate before it is allowed to communicate with the server. This spec defines when and how +a driver performs authentication with a MongoDB server. + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### References + +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) + +## Specification + +### Definitions + +- Credential + + The pieces of information used to establish the authenticity of a user. This is composed of an identity and some form + of evidence such as a password or a certificate. + +- FQDN + + Fully Qualified Domain Name + +- Mechanism + + A SASL implementation of a particular type of credential negotiation. + +- Source + + The authority used to establish credentials and/or privileges in reference to a mongodb server. In practice, it is the + database to which sasl authentication commands are sent. + +- Realm + + The authority used to establish credentials and/or privileges in reference to GSSAPI. + +- SASL + + Simple Authentication and Security Layer - [RFC 4422](http://www.ietf.org/rfc/rfc4422.txt) + +### Client Implementation + +#### MongoCredential + +Drivers SHOULD contain a type called `MongoCredential`. It SHOULD contain some or all of the following information. + +- username (string) + + - Applies to all mechanisms. + - Optional for MONGODB-X509, MONGODB-AWS, and MONGODB-OIDC. + +- source (string) + + - Applies to all mechanisms. + - Always '$external' for GSSAPI and MONGODB-X509. + - This is the database to which the authenticate command will be sent. + - This is the database to which sasl authentication commands will be sent. + +- password (string) + + - Does not apply to all mechanisms. + +- mechanism (string) + + - Indicates which mechanism to use with the credential. + +- mechanism_properties + + - Includes additional properties for the given mechanism. + +Each mechanism requires certain properties to be present in a MongoCredential for authentication to occur. See the +individual mechanism definitions in the "MongoCredential Properties" section. All requirements listed for a mechanism +must be met for authentication to occur. + +#### Credential delimiter in URI implies authentication + +The presence of a credential delimiter (i.e. @) in the URI connection string is evidence that the user has unambiguously +specified user information and MUST be interpreted as a user configuring authentication credentials (even if the +username and/or password are empty strings). + +##### Authentication source and URI database do not imply authentication + +The presence of a database name in the URI connection string MUST NOT be interpreted as a user configuring +authentication credentials. The URI database name is only used as a default source for some mechanisms when +authentication has been configured and a source is required but has not been specified. See individual mechanism +definitions for details. + +Similarly, the presence of the `authSource` option in the URI connection string without other credential data such as +Userinfo or authentication parameters in connection options MUST NOT be interpreted as a request for authentication. + +#### Errors + +Drivers SHOULD raise an error as early as possible when detecting invalid values in a credential. For instance, if a +`mechanism_property` is specified for [MONGODB-CR](#mongodb-cr), the driver should raise an error indicating that the +property does not apply. + +Drivers MUST raise an error if any required information for a mechanism is missing. For instance, if a `username` is not +specified for SCRAM-SHA-256, the driver must raise an error indicating the the property is missing. + +#### Naming + +Naming of this information MUST be idiomatic to the driver's language/framework but still remain consistent. For +instance, python would use "mechanism_properties" and .NET would use "MechanismProperties". + +Naming of mechanism properties MUST be case-insensitive. For instance, SERVICE_NAME and service_name refer to the same +property. + +### Authentication + +A MongoClient instance MUST be considered a single logical connection to the server/deployment. + +Socket connections from a MongoClient to deployment members can be one of two types: + +- Monitoring-only socket: multi-threaded drivers maintain monitoring sockets separate from sockets in connection pools. +- General-use socket: for multi-threaded drivers, these are sockets in connection pools used for (non-monitoring) user + operations; in single-threaded drivers, these are used for both monitoring and user operations. + +Authentication (including mechanism negotiation) MUST NOT happen on monitoring-only sockets. + +If one or more credentials are provided to a MongoClient, then whenever a general-use socket is opened, drivers MUST +immediately conduct an authentication handshake over that socket. + +Drivers SHOULD require all credentials to be specified upon construction of the MongoClient. This is defined as eager +authentication and drivers MUST support this mode. + +#### Authentication Handshake + +An authentication handshake consists of an initial `hello` or legacy hello command possibly followed by one or more +authentication conversations. + +Drivers MUST follow the following steps for an authentication handshake: + +1. Upon opening a general-use socket to a server for a given MongoClient, drivers MUST issue a + [MongoDB Handshake](../mongodb-handshake/handshake.md) immediately. This allows a driver to determine the server + type. If the `hello` or legacy hello of the MongoDB Handshake fails with an error, drivers MUST treat this as an + authentication error. +2. If the server is of type RSArbiter, no authentication is possible and the handshake is complete. +3. Inspect the value of `maxWireVersion`. If the value is greater than or equal to `6`, then the driver MUST use + `OP_MSG` for authentication. Otherwise, it MUST use `OP_QUERY`. +4. If credentials exist: 3.1. A driver MUST authenticate with all credentials provided to the MongoClient. 3.2. A single + invalid credential is the same as all credentials being invalid. + +If the authentication handshake fails for a socket, drivers MUST mark the server Unknown and clear the server's +connection pool. (See [Q & A](#q-and-a) below and SDAM's +[Why mark a server Unknown after an auth error](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#why-mark-a-server-unknown-after-an-auth-error) +for rationale.) + +All blocking operations executed as part of the authentication handshake MUST apply timeouts per the +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. + +#### Mechanism Negotiation via Handshake + +- Since: 4.0 + +If an application provides a username but does not provide an authentication mechanism, drivers MUST negotiate a +mechanism via a `hello` or legacy hello command requesting a user's supported SASL mechanisms: + +```javascript +{hello: 1, saslSupportedMechs: "."} +``` + +In this example `` is the authentication database name that either SCRAM-SHA-1 or SCRAM-SHA-256 would use (they +are the same; either from the connection string or else defaulting to 'admin') and `` is the username provided +in the auth credential. The username MUST NOT be modified from the form provided by the user (i.e. do not normalize with +SASLprep), as the server uses the raw form to look for conflicts with legacy credentials. + +If the handshake response includes a `saslSupportedMechs` field, then drivers MUST use the contents of that field to +select a default mechanism as described later. If the command succeeds and the response does not include a +`saslSupportedMechs` field, then drivers MUST use the legacy default mechanism rules for servers older than 4.0. + +Drivers MUST NOT validate the contents of the `saslSupportedMechs` attribute of the initial handshake reply. Drivers +MUST NOT raise an error if the `saslSupportedMechs` attribute of the reply includes an unknown mechanism. + +### Single-credential drivers + +When the authentication mechanism is not specified, drivers that allow only a single credential per client MUST perform +mechanism negotiation as part of the MongoDB Handshake portion of the authentication handshake. This lets authentication +proceed without a separate negotiation round-trip exchange with the server. + +### Multi-credential drivers + +The use of multiple credentials within a driver is discouraged, but some legacy drivers still allow this. Such drivers +may not have user credentials when connections are opened and thus will not be able to do negotiation. + +Drivers with a list of credentials at the time a connection is opened MAY do mechanism negotiation on the initial +handshake, but only for the first credential in the list of credentials. + +When authenticating each credential, if the authentication mechanism is not specified and has not been negotiated for +that credential: + +- If the connection handshake results indicate the server version is 4.0 or later, drivers MUST send a new `hello` or + legacy hello negotiation command for the credential to determine the default authentication mechanism. +- Otherwise, when the server version is earlier than 4.0, the driver MUST select a default authentication mechanism for + the credential following the instructions for when the `saslSupportedMechs` field is not present in a legacy hello + response. + +### Caching credentials in SCRAM + +In the implementation of SCRAM authentication mechanisms (e.g. SCRAM-SHA-1 and SCRAM-SHA-256), drivers MUST maintain a +cache of computed SCRAM credentials. The cache entries SHOULD be identified by the password, salt, iteration count, and +a value that uniquely identifies the authentication mechanism (e.g. "SHA1" or "SCRAM-SHA-256"). + +The cache entry value MUST be either the `saltedPassword` parameter or the combination of the `clientKey` and +`serverKey` parameters. + +### Reauthentication + +On any operation that requires authentication, the server may raise the error `ReauthenticationRequired` (391), +typically if the user's credential has expired. Drivers MUST immediately attempt a reauthentication on the connection +using suitable credentials, as specified by the particular authentication mechanism when this error is raised, and then +re-attempt the operation. This attempt MUST be irrespective of whether the operation is considered retryable. Drivers +MUST NOT resend a `hello` message during reauthentication, instead using SASL messages directly. Any errors that could +not be recovered from during reauthentication, or that were encountered during the subsequent re-attempt of the +operation MUST be raised to the user. + +Currently the only authentication mechanism on the server that supports reauthentication is MONGODB-OIDC. See the +[MONGODB-OIDC](#mongodb-oidc) section on reauthentication for more details. Note that in order to implement the unified +spec tests for reauthentication, it may be necessary to add reauthentication support for whichever auth mechanism is +used when running the authentication spec tests. + +### Default Authentication Methods + +- Since: 3.0 +- Revised: 4.0 + +If the user did not provide a mechanism via the connection string or via code, the following logic describes how to +select a default. + +If a `saslSupportedMechs` field was present in the handshake response for mechanism negotiation, then it MUST be +inspected to select a default mechanism: + +```javascript +{ + "hello" : true, + "saslSupportedMechs": ["SCRAM-SHA-1", "SCRAM-SHA-256"], + ... + "ok" : 1 +} +``` + +If SCRAM-SHA-256 is present in the list of mechanism, then it MUST be used as the default; otherwise, SCRAM-SHA-1 MUST +be used as the default, regardless of whether SCRAM-SHA-1 is in the list. Drivers MUST NOT attempt to use any other +mechanism (e.g. PLAIN) as the default. + +If `saslSupportedMechs` is not present in the handshake response for mechanism negotiation, then SCRAM-SHA-1 MUST be +used when talking to servers >= 3.0. Prior to server 3.0, MONGODB-CR MUST be used. + +When a user has specified a mechanism, regardless of the server version, the driver MUST honor this. + +#### Determining Server Version + +Drivers SHOULD use the server's wire version ranges to determine the server's version. + +### MONGODB-CR + +- Since: 1.4 +- Deprecated: 3.0 +- Removed: 4.0 + +MongoDB Challenge Response is a nonce and MD5 based system. The driver sends a `getnonce` command, encodes and hashes +the password using the returned nonce, and then sends an `authenticate` command. + +#### Conversation + +1. Send `getnonce` command + + ```javascript + CMD = { getnonce: 1 } + RESP = { nonce: } + ``` + +2. Compute key + + ```javascript + passwordDigest = HEX( MD5( UTF8( username + ':mongo:' + password ))) + key = HEX( MD5( UTF8( nonce + username + passwordDigest ))) + ``` + +3. Send `authenticate` command + + ```javascript + CMD = { authenticate: 1, nonce: nonce, user: username, key: key } + ``` + +As an example, given a username of "user" and a password of "pencil", the conversation would appear as follows: + +```javascript +CMD = {getnonce : 1} +RESP = {nonce: "2375531c32080ae8", ok: 1} +CMD = {authenticate: 1, user: "user", nonce: "2375531c32080ae8", key: "21742f26431831d5cfca035a08c5bdf6"} +RESP = {ok: 1} +``` + +#### [MongoCredential](#mongocredential) Properties + +- username + + MUST be specified and non-zero length. + +- source + + MUST be specified. Defaults to the database name if supplied on the connection string or `admin`. + +- password + + MUST be specified. + +- mechanism + + MUST be "MONGODB-CR" + +- mechanism_properties + + MUST NOT be specified. + +### MONGODB-X509 + +- Since: 2.6 +- Changed: 3.4 + +MONGODB-X509 is the usage of X.509 certificates to validate a client where the distinguished subject name of the client +certificate acts as the username. + +When connected to MongoDB 3.4: + +- You MUST NOT raise an error when the application only provides an X.509 certificate and no username. +- If the application does not provide a username you MUST NOT send a username to the server. +- If the application provides a username you MUST send that username to the server. + +When connected to MongoDB 3.2 or earlier: + +- You MUST send a username to the server. +- If no username is provided by the application, you MAY extract the username from the X.509 certificate instead of + requiring the application to provide it. +- If you choose not to automatically extract the username from the certificate you MUST error when no username is + provided by the application. + +#### Conversation + +1. Send `authenticate` command (MongoDB 3.4+) + + ```javascript + CMD = {"authenticate": 1, "mechanism": "MONGODB-X509"} + RESP = {"dbname" : "$external", "user" : "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client", "ok" : 1} + ``` + +2. Send `authenticate` command with username: + + ```bash + username = $(openssl x509 -subject -nameopt RFC2253 -noout -inform PEM -in my-cert.pem) + ``` + + ```javascript + CMD = {authenticate: 1, mechanism: "MONGODB-X509", user: "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client"} + RESP = {"dbname" : "$external", "user" : "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client", "ok" : 1} + ``` + +#### [MongoCredential](#mongocredential) Properties + +- username + + SHOULD NOT be provided for MongoDB 3.4+ MUST be specified and non-zero length for MongoDB prior to 3.4 + +- source + + MUST be "$external". Defaults to `$external\`. + +- password + + MUST NOT be specified. + +- mechanism + + MUST be "MONGODB-X509" + +- mechanism_properties + + MUST NOT be specified. + +TODO: Errors + +### SASL Mechanisms + +- Since: 2.4 Enterprise + +SASL mechanisms are all implemented using the same sasl commands and interpreted as defined by the +[SASL specification RFC 4422](http://tools.ietf.org/html/rfc4422). + +1. Send the `saslStart` command. + + ```javascript + CMD = { saslStart: 1, mechanism: , payload: BinData(...), autoAuthorize: 1 } + RESP = { conversationId: , code: , done: , payload: } + ``` + + - conversationId: the conversation identifier. This will need to be remembered and used for the duration of the + conversation. + - code: A response code that will indicate failure. This field is not included when the command was successful. + - done: a boolean value indicating whether or not the conversation has completed. + - payload: a sequence of bytes or a base64 encoded string (depending on input) to pass into the SASL library to + transition the state machine. + +2. Continue with the `saslContinue` command while `done` is `false`. + + ```javascript + CMD = { saslContinue: 1, conversationId: conversationId, payload: BinData(...) } + RESP = { conversationId: , code: , done: , payload: } + ``` + + Many languages will have the ability to utilize 3rd party libraries. The server uses + [cyrus-sasl](https://www.cyrusimap.org/sasl/) and it would make sense for drivers with a choice to also choose + cyrus. However, it is important to ensure that when utilizing a 3rd party library it does implement the mechanism + on all supported OS versions and that it interoperates with the server. For instance, the cyrus sasl library + offered on RHEL 6 does not implement SCRAM-SHA-1. As such, if your driver supports RHEL 6, you'll need to implement + SCRAM-SHA-1 from scratch. + +### GSSAPI + +- Since: + + 2.4 Enterprise + + 2.6 Enterprise on Windows + +GSSAPI is kerberos authentication as defined in [RFC 4752](http://tools.ietf.org/html/rfc4752). Microsoft has a +proprietary implementation called SSPI which is compatible with both Windows and Linux clients. + +[MongoCredential](#mongocredential) properties: + +- username + + MUST be specified and non-zero length. + +- source + + MUST be "$external". Defaults to `$external\`. + +- password + + MAY be specified. If omitted, drivers MUST NOT pass the username without password to SSPI on Windows and instead use + the default credentials. + +- mechanism + + MUST be "GSSAPI" + +- mechanism_properties + + - SERVICE_NAME + + Drivers MUST allow the user to specify a different service name. The default is "mongodb". + + - CANONICALIZE_HOST_NAME + + Drivers MAY allow the user to request canonicalization of the hostname. This might be required when the hosts report + different hostnames than what is used in the kerberos database. The value is a string of either "none", "forward", + or "forwardAndReverse". "none" is the default and performs no canonicalization. "forward" performs a forward DNS + lookup to canonicalize the hostname. "forwardAndReverse" performs a forward DNS lookup and then a reverse lookup + on that value to canonicalize the hostname. The driver MUST fallback to the provided host if any lookup errors or + returns no results. Drivers MAY decide to also keep the legacy boolean values where `true` equals the + "forwardAndReverse" behaviour and `false` equals "none". + + - SERVICE_REALM + + Drivers MAY allow the user to specify a different realm for the service. This might be necessary to support + cross-realm authentication where the user exists in one realm and the service in another. + + - SERVICE_HOST + + Drivers MAY allow the user to specify a different host for the service. This is stored in the service principal name + instead of the standard host name. This is generally used for cases where the initial role is being created from + localhost but the actual service host would differ. + +#### Hostname Canonicalization + +Valid values for CANONICALIZE_HOST_NAME are `true`, `false`, "none", "forward", "forwardAndReverse". If a value is +provided that does not match one of these the driver MUST raise an error. + +If CANONICALIZE_HOST_NAME is `false`, "none", or not provided, the driver MUST NOT canonicalize the host name. + +If CANONICALIZE_HOST_NAME is `true`, "forward", or "forwardAndReverse", the client MUST canonicalize the name of each +host it uses for authentication. There are two options. First, if the client's underlying GSSAPI library provides +hostname canonicalization, the client MAY rely on it. For example, MIT Kerberos has +[a configuration option for canonicalization](https://web.mit.edu/kerberos/krb5-1.13/doc/admin/princ_dns.html#service-principal-canonicalization). + +Second, the client MAY implement its own canonicalization. If so, the canonicalization algorithm MUST be: + +```python +addresses = fetch addresses for host +if no addresses: + throw error + +address = first result in addresses + +while true: + cnames = fetch CNAME records for host + if no cnames: + break + + # Unspecified which CNAME is used if > 1. + host = one of the records in cnames + +if forwardAndReverse or true: + reversed = do a reverse DNS lookup for address + canonicalized = lowercase(reversed) +else: + canonicalized = lowercase(host) +``` + +For example, here is a Python implementation of this algorithm using `getaddrinfo` (for address and CNAME resolution) +and `getnameinfo` (for reverse DNS). + +```python +from socket import * +import sys + + +def canonicalize(host, mode): + # Get a CNAME for host, if any. + af, socktype, proto, canonname, sockaddr = getaddrinfo( + host, None, 0, 0, IPPROTO_TCP, AI_CANONNAME)[0] + + print('address from getaddrinfo: [%s]' % (sockaddr[0],)) + print('canonical name from getaddrinfo: [%s]' % (canonname,)) + + if (mode == true or mode == 'forwardAndReverse'): + try: + # NI_NAMEREQD requests an error if getnameinfo fails. + name = getnameinfo(sockaddr, NI_NAMEREQD) + except gaierror as exc: + print('getname info failed: "%s"' % (exc,)) + return canonname.lower() + return name[0].lower() + else: + return canonname.lower() + + +canonicalized = canonicalize(sys.argv[1]) +print('canonicalized: [%s]' % (canonicalized,)) +``` + +Beware of a bug in older glibc where `getaddrinfo` uses PTR records instead of CNAMEs if the address family hint is +AF_INET6, and beware of a bug in older MIT Kerberos that causes it to always do reverse DNS lookup even if the `rdns` +configuration option is set to `false`. + +### PLAIN + +- Since: 2.6 Enterprise + +The PLAIN mechanism, as defined in [RFC 4616](http://tools.ietf.org/html/rfc4616), is used in MongoDB to perform LDAP +authentication. It cannot be used to perform any other type of authentication. Since the credentials are stored outside +of MongoDB, the `$external` database must be used for authentication. + +#### Conversation + +As an example, given a username of "user" and a password of "pencil", the conversation would appear as follows: + +```javascript +CMD = {saslStart: 1, mechanism: "PLAIN", payload: BinData(0, "AHVzZXIAcGVuY2ls")} +RESP = {conversationId: 1, payload: BinData(0,""), done: true, ok: 1} +``` + +If your sasl client is also sending the authzid, it would be "user" and the conversation would appear as follows: + +```javascript +CMD = {saslStart: 1, mechanism: "PLAIN", payload: BinData(0, "dXNlcgB1c2VyAHBlbmNpbA==")} +RESP = {conversationId: 1, payload: BinData(0,""), done: true, ok: 1} +``` + +MongoDB supports either of these forms. + +#### [MongoCredential](#mongocredential) Properties + +- username + + MUST be specified and non-zero length. + +- source + + MUST be specified. Defaults to the database name if supplied on the connection string or `$external`. + +- password + + MUST be specified. + +- mechanism + + MUST be "PLAIN" + +- mechanism_properties + + MUST NOT be specified. + +### SCRAM-SHA-1 + +- Since: 3.0 + +SCRAM-SHA-1 is defined in [RFC 5802](http://tools.ietf.org/html/rfc5802). + +[Page 11 of the RFC](http://tools.ietf.org/html/rfc5802#page-11) specifies that user names be prepared with SASLprep, +but drivers MUST NOT do so. + +[Page 8 of the RFC](http://tools.ietf.org/html/rfc5802#page-8) identifies the "SaltedPassword" as +`:= Hi(Normalize(password), salt, i)`. The `password` variable MUST be the mongodb hashed variant. The mongo hashed +variant is computed as `hash = HEX( MD5( UTF8( username + ':mongo:' + plain_text_password )))`, where +`plain_text_password` is actually plain text. The `username` and `password` MUST NOT be prepared with SASLprep before +hashing. + +For example, to compute the ClientKey according to the RFC: + +```javascript +// note that "salt" and "i" have been provided by the server +function computeClientKey(username, plain_text_password) { + mongo_hashed_password = HEX( MD5( UTF8( username + ':mongo:' + plain_text_password ))); + saltedPassword = Hi(Normalize(mongo_hashed_password), salt, i); + clientKey = HMAC(saltedPassword, "Client Key"); +} +``` + +In addition, SCRAM-SHA-1 requires that a client create a randomly generated nonce. It is imperative, for security sake, +that this be as secure and truly random as possible. For instance, Java provides both a Random class as well as a +SecureRandom class. SecureRandom is cryptographically generated while Random is just a pseudo-random generator with +predictable outcomes. + +Additionally, drivers MUST enforce a minimum iteration count of 4096 and MUST error if the authentication conversation +specifies a lower count. This mitigates downgrade attacks by a man-in-the-middle attacker. + +Drivers MUST NOT advertise support for channel binding, as the server does not support it and legacy servers may fail +authentication if drivers advertise support. I.e. the client-first-message MUST start with `n,`. + +Drivers MUST add a top-level `options` field to the saslStart command, whose value is a document containing a field +named `skipEmptyExchange` whose value is true. Older servers will ignore the `options` field and continue with the +longer conversation as shown in the "Backwards Compatibility" section. Newer servers will set the `done` field to `true` +when it responds to the client at the end of the second round trip, showing proof that it knows the password. This will +shorten the conversation by one round trip. + +#### Conversation + +As an example, given a username of "user" and a password of "pencil" and an r value of "fyko+d2lbbFgONRv9qkxdawL", a +SCRAM-SHA-1 conversation would appear as follows: + +```javascript +CMD = "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL" +RESP = "r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000" +CMD = "c=biws,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,p=MC2T8BvbmWRckDw8oWl5IVghwCY=" +RESP = "v=UMWeI25JD1yNYZRMpZ4VHvhZ9e0=" +``` + +This same conversation over MongoDB's SASL implementation would appear as follows: + +```javascript +CMD = {saslStart: 1, mechanism: "SCRAM-SHA-1", payload: BinData(0, "biwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM"), options: { skipEmptyExchange: true }} +RESP = {conversationId : 1, payload: BinData(0,"cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw"), done: false, ok: 1} +CMD = {saslContinue: 1, conversationId: 1, payload: BinData(0, "Yz1iaXdzLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMSG8rVmdrN3F2VU9LVXd1V0xJV2c0bC85U3JhR01IRUUscD1NQzJUOEJ2Ym1XUmNrRHc4b1dsNUlWZ2h3Q1k9")} +RESP = {conversationId: 1, payload: BinData(0,"dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9"), done: true, ok: 1} +``` + +#### [MongoCredential](#mongocredential) Properties + +- username + + MUST be specified and non-zero length. + +- source + + MUST be specified. Defaults to the database name if supplied on the connection string or `admin`. + +- password + + MUST be specified. + +- mechanism + + MUST be "SCRAM-SHA-1" + +- mechanism_properties + + MUST NOT be specified. + +### SCRAM-SHA-256 + +- Since: 4.0 + +SCRAM-SHA-256 extends [RFC 5802](http://tools.ietf.org/html/rfc5802) and is formally defined in +[RFC 7677](https://tools.ietf.org/html/rfc7677). + +The MongoDB SCRAM-SHA-256 mechanism works similarly to the SCRAM-SHA-1 mechanism, with the following changes: + +- The SCRAM algorithm MUST use SHA-256 as the hash function instead of SHA-1. +- User names MUST NOT be prepared with SASLprep. This intentionally contravenes the "SHOULD" provision of RFC 5802. +- Passwords MUST be prepared with SASLprep, per RFC 5802. Passwords are used directly for key derivation ; they MUST NOT + be digested as they are in SCRAM-SHA-1. + +Additionally, drivers MUST enforce a minimum iteration count of 4096 and MUST error if the authentication conversation +specifies a lower count. This mitigates downgrade attacks by a man-in-the-middle attacker. + +Drivers MUST add a top-level `options` field to the saslStart command, whose value is a document containing a field +named `skipEmptyExchange` whose value is true. Older servers will ignore the `options` field and continue with the +longer conversation as shown in the "Backwards Compatibility" section. Newer servers will set the `done` field to `true` +when it responds to the client at the end of the second round trip, showing proof that it knows the password. This will +shorten the conversation by one round trip. + +#### Conversation + +As an example, given a username of "user" and a password of "pencil" and an r value of "rOprNGfwEbeRWgbNEkqO", a +SCRAM-SHA-256 conversation would appear as follows: + +```javascript +CMD = "n,,n=user,r=rOprNGfwEbeRWgbNEkqO" +RESP = "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096" +CMD = "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=" +RESP = "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=" +``` + +This same conversation over MongoDB's SASL implementation would appear as follows: + +```javascript +CMD = {saslStart: 1, mechanism:"SCRAM-SHA-256", options: {skipEmptyExchange: true}, payload: BinData(0, "biwsbj11c2VyLHI9ck9wck5HZndFYmVSV2diTkVrcU8=")} +RESP = {conversationId: 1, payload: BinData(0, "cj1yT3ByTkdmd0ViZVJXZ2JORWtxTyVodllEcFdVYTJSYVRDQWZ1eEZJbGopaE5sRiRrMCxzPVcyMlphSjBTTlk3c29Fc1VFamI2Z1E9PSxpPTQwOTY="), done: false, ok: 1} +CMD = {saslContinue: 1, conversationId: 1, payload: BinData(0, "Yz1iaXdzLHI9ck9wck5HZndFYmVSV2diTkVrcU8laHZZRHBXVWEyUmFUQ0FmdXhGSWxqKWhObEYkazAscD1kSHpiWmFwV0lrNGpVaE4rVXRlOXl0YWc5empmTUhnc3FtbWl6N0FuZFZRPQ==")} +RESP = {conversationId: 1, payload: BinData(0, "dj02cnJpVFJCaTIzV3BSUi93dHVwK21NaFVaVW4vZEI1bkxUSlJzamw5NUc0PQ=="), done: true, ok: 1} +``` + +#### [MongoCredential](#mongocredential) Properties + +- username + + MUST be specified and non-zero length. + +- source + + MUST be specified. Defaults to the database name if supplied on the connection string or `admin`. + +- password + + MUST be specified. + +- mechanism + + MUST be "SCRAM-SHA-256" + +- mechanism_properties + + MUST NOT be specified. + +### MONGODB-AWS + +- Since: 4.4 + +MONGODB-AWS authenticates using AWS IAM credentials (an access key ID and a secret access key), +[temporary AWS IAM credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html) obtained from +an [AWS Security Token Service (STS)](https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html) +[Assume Role](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) request, an OpenID Connect ID +token that supports +[AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html), or +temporary AWS IAM credentials assigned to an +[EC2 instance](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html) or ECS task. +Temporary credentials, in addition to an access key ID and a secret access key, includes a security (or session) token. + +MONGODB-AWS requires that a client create a randomly generated nonce. It is imperative, for security sake, that this be +as secure and truly random as possible. Additionally, the secret access key and only the secret access key is sensitive. +Drivers MUST take proper precautions to ensure we do not leak this info. + +All messages between MongoDB clients and servers are sent as BSON V1.1 Objects in the payload field of saslStart and +saslContinue. All fields in these messages have a "short name" which is used in the serialized BSON representation and a +human-readable "friendly name" which is used in this specification. They are as follows: + +| Name | Friendly Name | Type | Description | +| ---- | -------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| r | client nonce | BinData Subtype 0 | 32 byte cryptographically secure random number | +| p | gs2-cb-flag | int32 | The integer representation of the ASCII character 'n' or 'y', i.e., `110` or `121` | +| s | server nonce | BinData Subtype 0 | 64 bytes total, 32 bytes from the client first message and a 32 byte cryptographically secure random number generated by the server | +| h | sts host | string | FQDN of the STS service | +| a | authorization header | string | Authorization header for [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html?shortFooter=true) | +| d | X-AMZ-Date | string | Current date in UTC. See [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html?shortFooter=true) | +| t | X-AMZ-Security-Token | string | Optional AWS security token | + +Drivers MUST NOT advertise support for channel binding, as the server does not support it and legacy servers may fail +authentication if drivers advertise support. The client-first-message MUST set the gs2-cb-flag to the integer +representation of the ASCII character `n`, i.e., `110`. + +#### Conversation + +The first message sent by drivers MUST contain a `client nonce` and `gs2-cb-flag`. In response, the server will send a +`server nonce` and `sts host`. Drivers MUST validate that the server nonce is exactly 64 bytes and the first 32 bytes +are the same as the client nonce. Drivers MUST also validate that the length of the host is greater than 0 and less than +or equal to 255 bytes per [RFC 1035](https://tools.ietf.org/html/rfc1035). Drivers MUST reject FQDN names with empty +labels (e.g., "abc..def"), names that start with a period (e.g., ".abc.def") and names that end with a period (e.g., +"abc.def."). Drivers MUST respond to the server's message with an `authorization header` and a `date`. + +As an example, given a client nonce value of "dzw1U2IwSEtgaWI0IUxZMVJqc2xuQzNCcUxBc05wZjI=", a MONGODB-AWS conversation +decoded from BSON to JSON would appear as follows: + +Client First + +```javascript +{ + "r" : new BinData(0, "dzw1U2IwSEtgaWI0IUxZMVJqc2xuQzNCcUxBc05wZjI="), + "p" : 110 +} +``` + +Server First + +```javascript +{ + "s" : new BinData(0, "dzw1U2IwSEtgaWI0IUxZMVJqc2xuQzNCcUxBc05wZjIGS0J9EgLwzEZ9dIzr/hnnK2mgd4D7F52t8g9yTC5cIA=="), + "h" : "sts.amazonaws.com" +} +``` + +Client Second + +```javascript +{ + "a" : "AWS4-HMAC-SHA256 Credential=AKIAICGVLKOKZVY3X3DA/20191107/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-mongodb-gs2-cb-flag;x-mongodb-server-nonce, Signature=ab62ce1c75f19c4c8b918b2ed63b46512765ed9b8bb5d79b374ae83eeac11f55", + "d" : "20191107T002607Z" + "t" : "" +} +``` + +Note that `X-AMZ-Security-Token` is required when using temporary credentials. When using regular credentials, it MUST +be omitted. Each message above will be encoded as BSON V1.1 objects and sent to the peer as the value of `payload`. +Therefore, the SASL conversation would appear as: + +Client First + +```javascript +{ + "saslStart" : 1, + "mechanism" : "MONGODB-AWS" + "payload" : new BinData(0, "NAAAAAVyACAAAAAAWj0lSjp8M0BMKGU+QVAzRSpWfk0hJigqO1V+b0FaVz4QcABuAAAAAA==") +} +``` + +Server First + +```javascript +{ + "conversationId" : 1, + "done" : false, + "payload" : new BinData(0, "ZgAAAAVzAEAAAAAAWj0lSjp8M0BMKGU+QVAzRSpWfk0hJigqO1V+b0FaVz5Rj7x9UOBHJLvPgvgPS9sSzZUWgAPTy8HBbI1cG1WJ9gJoABIAAABzdHMuYW1hem9uYXdzLmNvbQAA"), + "ok" : 1.0 +} +``` + +Client Second: + +```javascript +{ + "saslContinue" : 1, + "conversationId" : 1, + "payload" : new BinData(0, "LQEAAAJhAAkBAABBV1M0LUhNQUMtU0hBMjU2IENyZWRlbnRpYWw9QUtJQUlDR1ZMS09LWlZZM1gzREEvMjAxOTExMTIvdXMtZWFzdC0xL3N0cy9hd3M0X3JlcXVlc3QsIFNpZ25lZEhlYWRlcnM9Y29udGVudC1sZW5ndGg7Y29udGVudC10eXBlO2hvc3Q7eC1hbXotZGF0ZTt4LW1vbmdvZGItZ3MyLWNiLWZsYWc7eC1tb25nb2RiLXNlcnZlci1ub25jZSwgU2lnbmF0dXJlPThhMTI0NGZjODYyZTI5YjZiZjc0OTFmMmYwNDE5NDY2ZGNjOTFmZWU1MTJhYTViM2ZmZjQ1NDY3NDEwMjJiMmUAAmQAEQAAADIwMTkxMTEyVDIxMDEyMloAAA==") +} +``` + +In response to the Server First message, drivers MUST send an `authorization header`. Drivers MUST follow the +[Signature Version 4 Signing Process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) to +calculate the signature for the `authorization header`. The required and optional headers and their associated values +drivers MUST use for the canonical request (see +[Summary of Signing Steps](https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html)) are +specified in the table below. The following pseudocode shows the construction of the Authorization header. + +```text +Authorization: algorithm Credential=access key ID/credential scope, SignedHeaders=SignedHeaders, Signature=signature +``` + +The following example shows a finished Authorization header. + +```text +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7 +``` + +The following diagram is a summary of the steps drivers MUST follow to calculate the signature. + +![image](includes/calculating_a_signature.png) + +| Name | Value | +| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| HTTP Request Method | POST | +| URI | / | +| Content-Type\* | application/x-www-form-urlencoded | +| Content-Length\* | 43 | +| Host\* | Host field from Server First Message | +| Region | Derived from Host - see [Region Calculation](#region-calculation) below | +| X-Amz-Date\* | See [Amazon Documentation](https://docs.aws.amazon.com/general/latest/gr/sigv4_elements.html) | +| X-Amz-Security-Token\* | Optional, see [Amazon Documentation](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html?shortFooter=true) | +| X-MongoDB-Server-Nonce\* | Base64 string of server nonce | +| X-MongoDB-GS2-CB-Flag\* | ASCII lower-case character 'n' or 'y' or 'p' | +| X-MongoDB-Optional-Data\* | Optional data, base64 encoded representation of the optional object provided by the client | +| Body | Action=GetCallerIdentity&Version=2011-06-15 | + +> [!NOTE] +> `*`, Denotes a header that MUST be included in SignedHeaders, if present. + +#### Region Calculation + +To get the region from the host, the driver MUST follow the algorithm expressed in pseudocode below. : + +```text +if the host is invalid according to the rules described earlier + the region is undefined and the driver must raise an error. +else if the host is "aws.amazonaws.com" + the region is "us-east-1" +else if the host contains the character '.' (a period) + split the host by its periods. The region is the second label. +else // the valid host string contains no periods and is not "aws.amazonaws.com" + the region is "us-east-1" +``` + +Examples are provided below. + +| Host | Region | Notes | +| ------------------------------ | --------- | ------------------------------------------------ | +| sts.amazonaws.com | us-east-1 | the host is "sts.amazonaws.com"; use `us-east-1` | +| sts.us-west-2.amazonaws.com | us-west-2 | use the second label | +| sts.us-west-2.amazonaws.com.ch | us-west-2 | use the second label | +| example.com | com | use the second label | +| localhost | us-east-1 | no "`.`" character; use the default region | +| sts..com | < Error > | second label is empty | +| .amazonaws.com | < Error > | starts with a period | +| sts.amazonaws. | < Error > | ends with a period | +| "" | < Error > | empty string | +| "string longer than 255" | < Error > | string longer than 255 bytes | + +#### [MongoCredential](#mongocredential) Properties + +- username + + MAY be specified. The non-sensitive AWS access key. + +- source + + MUST be "$external". Defaults to `$external\`. + +- password + + MAY be specified. The sensitive AWS secret key. + +- mechanism + + MUST be "MONGODB-AWS" + +- mechanism_properties + + - AWS_CREDENTIAL_PROVIDER + + An AWS [Custom Credential Provider](#custom-credential-providers) that returns AWS credentials. Drivers MAY allow + the user to specify an object or function, depending on what is idiomatic for the driver. This property MUST + follow the same API as the driver language's AWS SDK credential provider. + +#### Obtaining Credentials + +Drivers will need AWS IAM credentials (an access key, a secret access key and optionally a session token) to complete +the steps in the +[Signature Version 4 Signing Process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html?shortFooter=true). +Regardless of how Drivers obtain credentials the only valid combination of credentials is an access key ID and a secret +access key or an access key ID, a secret access key and a session token. These values MUST be present in the environment +or be retrieved via the optional AWS SDK. If credentials are provided in the URI or client options, the driver MUST +raise an error. + +AWS recommends using an SDK to "take care of some of the heavy lifting necessary in successfully making API calls, +including authentication, retry behavior, and more". + +A recommended pattern for drivers with existing custom implementation is to not further enhance existing +implementations, and take an optional dependency on the AWS SDK. If the SDK is available, use it, otherwise fallback to +the existing implementation. + +One thing to be mindful of when adopting an AWS SDK is that they typically will check for credentials in a shared AWS +credentials file when one is present, which may be confusing for users relying on the previous authentication handling +behavior. It would be helpful to include a note like the following: + +"Because we are now using the AWS SDK to handle credentials, if you have a shared AWS credentials or config file, then +those credentials will be used by default if AWS auth environment variables are not set. To override this behavior, set +`AWS_SHARED_CREDENTIALS_FILE=""` in your shell or set the equivalent environment variable value in your script or +application. Alternatively, you can create an AWS profile specifically for your MongoDB credentials and set the +`AWS_PROFILE` environment variable to that profile name." + +##### Custom Credential Providers + +Drivers that choose to use the AWS SDK to fetch credentials MAY also allow users to provide a custom credential provider +as an option to the `MongoClient`. The interface for the option provided depends on the individual language SDK and +drivers MUST consult AWS SDK documentation to determine that format when implementing. The name of the option MUST be +`AWS_CREDENTIAL_PROVIDER` and be part of the authentication mechanism properties options that can be provided to the +client. + +Drivers MAY expose API for default providers for the following scenarios when applicable in their language's SDK: + +1. The default SDK credential provider. +2. A custom credential provider chain. +3. A single credential provider of any available SDK options provided by the SDK. + +##### Credential Fetching Order + +The order in which Drivers MUST search for credentials is: + +1. A custom AWS credential provider if the driver supports it. +2. Environment variables +3. Using `AssumeRoleWithWebIdentity` if `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` are set. +4. The ECS endpoint if `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` is set. Otherwise, the EC2 endpoint. + +> [!NOTE] +> See *Should drivers support accessing Amazon EC2 instance metadata in Amazon ECS* in [Q & A](#q-and-a) +> +> Drivers are not expected to handle +> [AssumeRole](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html) requests directly. See +> description of `AssumeRole` below, which is distinct from `AssumeRoleWithWebIdentity` requests that are meant to be +> handled directly by the driver. + +##### Environment variables + +AWS Lambda runtimes set several +[environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime) +during initialization. To support AWS Lambda runtimes Drivers MUST check a subset of these variables, i.e., +`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`, for the access key ID, secret access key and +session token, respectively. The `AWS_SESSION_TOKEN` may or may not be set. However, if `AWS_SESSION_TOKEN` is set +Drivers MUST use its value as the session token. Drivers implemented in programming languages that support altering +environment variables MUST always read environment variables dynamically during authorization, to handle the case where +another part the application has refreshed the credentials. + +However, if environment variables are not present during initial authorization, credentials may be fetched from another +source and cached. Even if the environment variables are present in subsequent authorization attempts, the driver MUST +use the cached credentials, or refresh them if applicable. This behavior is consistent with how the AWS SDKs behave. + +##### AssumeRoleWithWebIdentity + +AWS EKS clusters can be configured to automatically provide a valid OpenID Connect ID token and associated role ARN. +These can be exchanged for temporary credentials using an +[AssumeRoleWithWebIdentity request](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html). + +If the `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` environment variables are set, drivers MUST make an +`AssumeRoleWithWebIdentity` request to obtain temporary credentials. AWS recommends using an AWS Software Development +Kit (SDK) to make STS requests. + +The `WebIdentityToken` value is obtained by reading the contents of the file given by `AWS_WEB_IDENTITY_TOKEN_FILE`. The +`RoleArn` value is obtained from `AWS_ROLE_ARN`. If `AWS_ROLE_SESSION_NAME` is set, it MUST be used for the +`RoleSessionName` parameter, otherwise a suitable random name can be chosen. No other request parameters need to be set +if using an SDK. + +If not using an AWS SDK, the request must be made manually. If making a manual request, the `Version` should be +specified as well. An example manual POST request looks like the following: + +```text +https://sts.amazonaws.com/ +?Action=AssumeRoleWithWebIdentity +&RoleSessionName=app1 +&RoleArn= +&WebIdentityToken= +&Version=2011-06-15 +``` + +with the header: + +```text +Accept: application/json +``` + +The JSON response from the STS endpoint will contain credentials in this format: + +```javascript +{ + "Credentials": { + "AccessKeyId": , + "Expiration": , + "RoleArn": , + "SecretAccessKey": , + "SessionToken": + } +} +``` + +Note that the token is called `SessionToken` and not `Token` as it would be with other credential responses. + +##### ECS endpoint + +If a username and password are not provided and the aforementioned environment variables are not set, drivers MUST query +a link-local AWS address for temporary credentials. If temporary credentials cannot be obtained then drivers MUST fail +authentication and raise an error. Drivers SHOULD enforce a 10 second read timeout while waiting for incoming content +from both the ECS and EC2 endpoints. If the environment variable `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` is set then +drivers MUST assume that it was set by an AWS ECS agent and use the URI +`http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` to obtain temporary credentials. Querying the URI will +return the JSON response: + +```javascript +{ + "AccessKeyId": , + "Expiration": , + "RoleArn": , + "SecretAccessKey": , + "Token": +} +``` + +##### EC2 endpoint + +If the environment variable `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` is unset, drivers MUST use the EC2 endpoint, + +```text +http://169.254.169.254/latest/meta-data/iam/security-credentials/ +``` + +with the required header, + +```text +X-aws-ec2-metadata-token: +``` + +to access the EC2 instance's metadata. Drivers MUST obtain the role name from querying the URI + +```text +http://169.254.169.254/latest/meta-data/iam/security-credentials/ +``` + +The role name request also requires the header `X-aws-ec2-metadata-token`. Drivers MUST use v2 of the EC2 Instance +Metadata Service +([IMDSv2](https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/)) +to access the secret token. In other words, Drivers MUST + +- Start a session with a simple HTTP PUT request to IMDSv2. + + - The URL is `http://169.254.169.254/latest/api/token`. + - The required header is `X-aws-ec2-metadata-token-ttl-seconds`. Its value is the number of seconds the secret token + should remain valid with a max of six hours (`21600` seconds). + +- Capture the secret token IMDSv2 returned as a response to the PUT request. This token is the value for the header + `X-aws-ec2-metadata-token`. + +The curl recipe below demonstrates the above. It retrieves a secret token that's valid for 30 seconds. It then uses that +token to access the EC2 instance's credentials: + +```bash +$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 30"` +$ ROLE_NAME=`curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ -H "X-aws-ec2-metadata-token: $TOKEN"` +$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME -H "X-aws-ec2-metadata-token: $TOKEN" +``` + +Drivers can test this process using the mock EC2 server in +[mongo-enterprise-modules](https://github.com/10gen/mongo-enterprise-modules/blob/master/jstests/external_auth/lib/ec2_metadata_http_server.py). +The script must be run with `python3`: + +```bash +python3 ec2_metadata_http_server.py +``` + +To re-direct queries from the EC2 endpoint to the mock server, replace the link-local address (`http://169.254.169.254`) +with the IP and port of the mock server (by default, `http://localhost:8000`). For example, the curl script above +becomes: + +```bash +$ TOKEN=`curl -X PUT "http://localhost:8000/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 30"` +$ ROLE_NAME=`curl http://localhost:8000/latest/meta-data/iam/security-credentials/ -H "X-aws-ec2-metadata-token: $TOKEN"` +$ curl http://localhost:8000/latest/meta-data/iam/security-credentials/$ROLE_NAME -H "X-aws-ec2-metadata-token: $TOKEN" +``` + +The JSON response from both the actual and mock EC2 endpoint will be in this format: + +```javascript +{ + "Code": "Success", + "LastUpdated" : , + "Type": "AWS-HMAC", + "AccessKeyId" : , + "SecretAccessKey": , + "Token" : , + "Expiration": +} +``` + +From the JSON response drivers MUST obtain the `access_key`, `secret_key` and `security_token` which will be used during +the +[Signature Version 4 Signing Process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html?shortFooter=true). + +##### Caching Credentials + +Credentials fetched by the driver using AWS endpoints MUST be cached and reused to avoid hitting AWS rate limitations. +AWS recommends using a suitable Software Development Kit (SDK) for your language. If that SDK supports credential fetch +and automatic refresh/caching, then that mechanism can be used in lieu of manual caching. + +If using manual caching, the "Expiration" field MUST be stored and used to determine when to clear the cache. +Credentials are considered valid if they are more than five minutes away from expiring; to the reduce the chance of +expiration before they are validated by the server. Credentials that are retrieved from environment variables MUST NOT +be cached. + +If there are no current valid cached credentials, the driver MUST initiate a credential request. To avoid adding a +bottleneck that would override the `maxConnecting` setting, the driver MUST not place a lock on making a request. The +cache MUST be written atomically. + +If AWS authentication fails for any reason, the cache MUST be cleared. + +> [!NOTE] +> Five minutes was chosen based on the AWS documentation for +> [IAM roles for EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) : "We make new +> credentials available at least five minutes before the expiration of the old credentials". The intent is to have some +> buffer between when the driver fetches the credentials and when the server verifies them. + +### MONGODB-OIDC + +- Since: 7.0 Enterprise + +MONGODB-OIDC authenticates using an [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) +access token. + +There are two OIDC authentication flows that drivers can support: machine-to-machine ("machine") and human-in-the-loop +("human"). Drivers MUST support the machine authentication flow. Drivers MAY support the human authentication flow. + +The MONGODB-OIDC specification refers to the following OIDC concepts: + +- **Identity Provider (IdP)**: A service that manages user accounts and authenticates users or applications, such as + Okta or OneLogin. In the [Human Authentication Flow](#human-authentication-flow), the + [OIDC Human Callback](#oidc-human-callback) interacts directly the IdP. In the + [Machine Authentication Flow](#machine-authentication-flow), only the MongoDB server interacts directly the IdP. +- **Access token**: Used to authenticate requests to protected resources. OIDC access tokens are signed JWT strings. +- **Refresh token**: Some OIDC providers may return a refresh token in addition to an access token. A refresh token can + be used to retrieve new access tokens without requiring a human to re-authorize the application. Refresh tokens are + typically only supported by the [Human Authentication Flow](#human-authentication-flow). + +#### Machine Authentication Flow + +The machine authentication flow is intended to be used in cases where human interaction is not necessary or practical, +such as to authenticate database access for a web service. Some OIDC documentation refers to the machine authentication +flow as "workload authentication". + +Drivers MUST implement all behaviors described in the MONGODB-OIDC specification, unless the section or block +specifically says that it only applies to the [Human Authentication Flow](#human-authentication-flow). + +#### Human Authentication Flow + +The human authentication flow is intended to be used for applications that involve direct human interaction, such as +database tools or CLIs. Some OIDC documentation refers to the human authentication flow as "workforce authentication". + +Drivers that support the [Human Authentication Flow](#human-authentication-flow) MUST implement all behaviors described +in the MONGODB-OIDC specification, including sections or blocks that specifically say that it only applies the +[Human Authentication Flow](#human-authentication-flow). + +#### [MongoCredential](#mongocredential) Properties + +- username + + MAY be specified. Its meaning varies depending on the OIDC provider integration used. + +- source + + MUST be "$external". Defaults to `$external\`. + +- password + + MUST NOT be specified. + +- mechanism + + MUST be "MONGODB-OIDC" + +- mechanism_properties + + - ENVIRONMENT + + Drivers MUST allow the user to specify the name of a built-in OIDC application environment integration to use to + obtain credentials. If provided, the value MUST be one of `["test", "azure", "gcp", "k8s"]`. If both `ENVIRONMENT` + and an [OIDC Callback](#oidc-callback) or [OIDC Human Callback](#oidc-human-callback) are provided for the same + `MongoClient`, the driver MUST raise an error. + + - TOKEN_RESOURCE + + The URI of the target resource. If `TOKEN_RESOURCE` is provided and `ENVIRONMENT` is not one of `["azure", "gcp"]` + or `TOKEN_RESOURCE` is not provided and `ENVIRONMENT` is one of `["azure", "gcp"]`, the driver MUST raise an + error. Note: because the `TOKEN_RESOURCE` is often itself a URL, drivers MUST document that a `TOKEN_RESOURCE` + with a comma `,` must be given as a `MongoClient` configuration and not as part of the connection string, and that + the `TOKEN_RESOURCE` value can contain a colon `:` character. + + - OIDC_CALLBACK + + An [OIDC Callback](#oidc-callback) that returns OIDC credentials. Drivers MAY allow the user to specify an + [OIDC Callback](#oidc-callback) using a `MongoClient` configuration instead of a mechanism property, depending on + what is idiomatic for the driver. Drivers MUST NOT support both the `OIDC_CALLBACK` mechanism property and a + `MongoClient` configuration. + + - OIDC_HUMAN_CALLBACK + + An [OIDC Human Callback](#oidc-human-callback) that returns OIDC credentials. Drivers MAY allow the user to specify + a [OIDC Human Callback](#oidc-human-callback) using a `MongoClient` configuration instead of a mechanism property, + depending on what is idiomatic for the driver. Drivers MUST NOT support both the `OIDC_HUMAN_CALLBACK` mechanism + property and a `MongoClient` configuration. Drivers MUST return an error if both an + [OIDC Callback](#oidc-callback) and `OIDC Human Callback` are provided for the same `MongoClient`. This property + is only required for drivers that support the [Human Authentication Flow](#human-authentication-flow). + + - ALLOWED_HOSTS + + The list of allowed hostnames or ip-addresses (ignoring ports) for MongoDB connections. The hostnames may include a + leading "\*." wildcard, which allows for matching (potentially nested) subdomains. `ALLOWED_HOSTS` is a security + feature and MUST default to + `["*.mongodb.net", "*.mongodb-qa.net", "*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1", "*.mongo.com"]`. + When MONGODB-OIDC authentication using a [OIDC Human Callback](#oidc-human-callback) is attempted against a + hostname that does not match any of list of allowed hosts, the driver MUST raise a client-side error without + invoking any user-provided callbacks. This value MUST NOT be allowed in the URI connection string. The hostname + check MUST be performed after SRV record resolution, if applicable. This property is only required for drivers + that support the [Human Authentication Flow](#human-authentication-flow). + + - AWS_CREDENTIAL_PROVIDER + + A function or object from the AWS SDK that can be used to return AWS credentials. Drivers MAY allow the user to + specify the callback using a `MongoClient` configuration instead of a mechanism property, depending on what is + idiomatic for the driver. + + + +#### Built-in OIDC Environment Integrations + +Drivers MUST support all of the following built-in OIDC application environment integrations. + +**Test** + +The test integration is enabled by setting auth mechanism property `ENVIRONMENT:test`. It is meant for driver testing +purposes, and is not meant to be documented as a user-facing feature. + +If enabled, drivers MUST generate a token using a script in the `auth_oidc` +[folder](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/auth_oidc#readme) in Drivers +Evergreen Tools. The driver MUST then set the `OIDC_TOKEN_FILE` environment variable to the path to that file. At +runtime, the driver MUST use the `OIDC_TOKEN_FILE` environment variable and read the OIDC access token from that path. +The driver MUST use the contents of that file as value in the `jwt` field of the `saslStart` payload. + +Drivers MAY implement the "test" integration so that it conforms to the function signature of the +[OIDC Callback](#oidc-callback) to prevent having to re-implement the "test" integration logic in the OIDC prose tests. + +**Azure** + +The Azure provider integration is enabled by setting auth mechanism property `ENVIRONMENT:azure`. + +If enabled, drivers MUST use an internal machine callback that calls the +[Azure Instance Metadata Service](https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service) +and parse the JSON response body, as follows: + +Make an HTTP GET request to + +```text +http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=&client_id= +``` + +with headers + +```text +Accept: application/json +Metadata: true +``` + +where `` is the url-encoded value of the `TOKEN_RESOURCE` mechanism property and `` is the +`username` from the connection string. If a `username` is not provided, the `client_id` query parameter should be +omitted. The timeout should equal the `callbackTimeoutMS` parameter given to the callback. + +```bash +curl -X GET \ + -H "Accept: application/json" \ + -H "Metadata: true" \ + --max-time $CALLBACK_TIMEOUT_MS \ + "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=$ENCODED_TOKEN_RESOURCE" +``` + +The JSON response will be in this format: + +```json +{ + "access_token": "eyJ0eXAi...", + "refresh_token": "", + "expires_in": "3599", + "expires_on": "1506484173", + "not_before": "1506480273", + "resource": "https://management.azure.com/", + "token_type": "Bearer" +} +``` + +The driver MUST use the returned `"access_token"` value as the access token in a `JwtStepRequest`. If the response does +not return a status code of 200, the driver MUST raise an error including the HTTP response body. + +For more details, see +[How to use managed identities for Azure resources on an Azure VM to acquire an access token](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token). + +The callback itself MUST not perform any caching, and the driver MUST cache its tokens in the same way as if a custom +callback had been provided by the user. + +For details on test environment setup, see the README in +[Drivers-Evergreen-Tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/azure/README.md). + +**GCP** + +The GCP provider integration is enabled by setting auth mechanism property `ENVIRONMENT:gcp`. + +If enabled, drivers MUST use an internal machine callback that calls the +[Google Cloud VM metadata](https://cloud.google.com/compute/docs/metadata/overview) endpoint and parse the JSON response +body, as follows: + +Make an HTTP GET request to + +```text +http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience= +``` + +with headers + +```text +Metadata-Flavor: Google +``` + +where `` is the url-encoded value of the `TOKEN_RESOURCE` mechanism property. The timeout should equal the +`callbackTimeoutMS` parameter given to the callback. + +```bash +curl -X GET \ + -H "Metadata-Flavor: Google" \ + --max-time $CALLBACK_TIMEOUT_MS \ + "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=$ENCODED_TOKEN_RESOURCE" +``` + +The response body will be the access token itself. + +The driver MUST use the returned value as the access token in a `JwtStepRequest`. If the response does not return a +status code of 200, the driver MUST raise an error including the HTTP response body. + +For more details, see [View and query VM metadata](https://cloud.google.com/compute/docs/metadata/querying-metadata). + +The callback itself MUST not perform any caching, and the driver MUST cache its tokens in the same way as if a custom +callback had been provided by the user. + +For details on test environment setup, see the README in +[Drivers-Evergreen-Tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/gcp/README.md). + +***Kubernetes*** + +The Kubernetes integration is enabled by setting auth mechanism property `ENVIRONMENT:k8s`. In this configuration, the +driver is expected to be running inside a Kubernetes environment with a configured +[ServiceAccount](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#bound-service-account-token-volume). + +If enabled, drivers MUST read the contents of the token from the local file path found using the following algorithm: + +```python +if 'AZURE_FEDERATED_TOKEN_FILE' in os.environ: + fname = os.environ['AZURE_FEDERATED_TOKEN_FILE'] +elif 'AWS_WEB_IDENTITY_TOKEN_FILE' in os.environ: + fname = os.environ['AWS_WEB_IDENTITY_TOKEN_FILE'] +else: + fname = '/var/run/secrets/kubernetes.io/serviceaccount/token' +``` + +Where `AZURE_FEDERATED_TOKEN_FILE` contains the file path on Azure Kubernetes Service (AKS), +`AWS_WEB_IDENTITY_TOKEN_FILE` contains the file path on Elastic Kubernetes Service (EKS), and +`/var/run/secrets/kubernetes.io/serviceaccount/token` is the default path for a Kubernetes +[ServiceAccount token](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#serviceaccount-admission-controller), +which is used by Google Kubernetes Engine (GKE). + +The callback itself MUST not perform any caching, and the driver MUST cache its tokens in the same way as if a custom +callback had been provided by the user. + +For details on test environment setup, see the README in +[Drivers-Evergreen-Tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/k8s/README.md). + +#### OIDC Callback + +Drivers MUST allow users to provide a callback that returns an OIDC access token. The purpose of the callback is to +allow users to integrate with OIDC providers not supported by the +[Built-in Provider Integrations](#built-in-provider-integrations). Callbacks can be synchronous or asynchronous, +depending on the driver and/or language. Asynchronous callbacks should be preferred when other operations in the driver +use asynchronous functions. + +Drivers MUST provide a way for the callback to be either automatically canceled, or to cancel itself. This can be as a +timeout argument to the callback, a cancellation context passed to the callback, or some other language-appropriate +mechanism. The timeout value MUST be `min(remaining connectTimeoutMS, remaining timeoutMS)` as described in the Server +Selection section of the CSOT spec. If CSOT is not applied, then the driver MUST use 1 minute as the timeout. + +The driver MUST pass the following information to the callback: + +- `timeout`: A timeout, in milliseconds, a deadline, or a `timeoutContext`. + +- `username`: The username given as part of the connection string or `MongoClient` parameter. + +- `version`: The callback API version number. The version number is used to communicate callback API changes that are + not breaking but that users may want to know about and review their implementation. Drivers MUST pass `1` for the + initial callback API version number and increment the version number anytime the API changes. Note that this may + eventually lead to some drivers having different callback version numbers. + + For example, users may add the following check in their callback: + + ```typescript + if(params.version > 1) { + throw new Error("OIDC callback API has changed!"); + } + ``` + +The callback MUST be able to return the following information: + +- `accessToken`: An OIDC access token string. The driver MUST NOT attempt to validate `accessToken` directly. +- `expiresIn`: An optional expiry duration for the access token. Drivers with optional parameters MAY interpret a + missing value as infinite. Drivers MUST error if a negative value is returned. Drivers SHOULD use the most idiomatic + type for representing a duration in the driver's language. Note that the access token expiry value is currently not + used in [Credential Caching](#credential-caching), but is intended to support future caching optimizations. + +The signature and naming of the callback API is up to the driver's discretion. Drivers MUST ensure that additional +optional input parameters and return values can be added to the callback signature in the future without breaking +backward compatibility. + +An example callback API might look like: + +```typescript +interface OIDCCallbackParams { + callbackTimeoutMS: int; + username: str; + version: int; +} + +interface OIDCCredential { + accessToken: string; + expiresInSeconds: Optional; +} + +function oidcCallback(params: OIDCCallbackParams): OIDCCredential +``` + +##### OIDC Human Callback + +The human callback is an OIDC callback that includes additional information that is required when using the +[Human Authentication Flow](#human-authentication-flow). Drivers that support the +[Human Authentication Flow](#human-authentication-flow) MUST implement the human callback. + +In addition to the information described in the [OIDC Callback](#oidc-callback) section, drivers MUST be able to pass +the following information to the callback: + +- `idpInfo`: Information used to authenticate with the IdP. + - `issuer`: A URL which describes the Authentication Server. This identifier should be the iss of provided access + tokens, and be viable for RFC8414 metadata discovery and RFC9207 identification. + - `clientId`: A unique client ID for this OIDC client. + - `requestScopes`: A list of additional scopes to request from IdP. +- `refreshToken`: The refresh token, if applicable, to be used by the callback to request a new token from the issuer. + +In addition to the information described in the [OIDC Callback](#oidc-callback) section, the callback MUST be able to +return the following information: + +- `refreshToken`: An optional refresh token that can be used to fetch new access tokens. + +The signature and naming of the callback API is up to the driver's discretion. Drivers MAY use a single callback API for +both callback types or separate callback APIs for each callback type. Drivers MUST ensure that additional optional input +parameters and return values can be added to the callback signature in the future without breaking backward +compatibility. + +An example human callback API might look like: + +```typescript +interface IdpInfo { + issuer: string; + clientId: Optional; + requestScopes: Optional>; +} + +interface OIDCCallbackParams { + username: str; + callbackTimeoutMS: int; + version: int; + idpInfo: Optional; + refreshToken: Optional; +} + +interface OIDCCredential { + accessToken: string; + expiresInSeconds: Optional; + refreshToken: Optional; +} + +function oidcCallback(params: OIDCCallbackParams): OIDCCredential +``` + +When a human callback is provided, drivers MUST use the following behaviors when calling the callback: + +- The driver MUST pass the `IdpInfo` and the refresh token (if available) to the callback. + - If there is no cached `IdpInfo`, drivers MUST start a [Two-Step](#two-step) conversation before calling the human + callback. See the Conversation and [Credential Caching](#credential-caching) sections for more details. +- The timeout duration MUST be 5 minutes. This is to account for the human interaction required to complete the + callback. In this case, the callback is not subject to CSOT. + +#### Conversation + +OIDC supports two conversation styles: one-step and two-step. The server detects whether the driver is using a one-step +or two-step conversation based on the structure of the `saslStart` payload. + +##### One-Step + +A one-step conversation is used for OIDC providers that allow direct access to an access token. For example, an OIDC +provider configured for machine-to-machine authentication may provide an access token via a local file pre-loaded on an +application host. + +Drivers MUST use a one-step conversation when using a cached access token, one of the +[Built-in Provider Integrations](#built-in-provider-integrations), or an [OIDC Callback](#oidc-callback) (not an +[OIDC Human Callback](#oidc-human-callback)). + +The one-step conversation starts with a `saslStart` containing a `JwtStepRequest` payload. The value of `jwt` is the +OIDC access token string. + +```typescript +interface JwtStepRequest: + // Compact serialized JWT with signature. + jwt: string; +} +``` + +An example OIDC one-step SASL conversation with access token string "abcd1234" looks like: + +```javascript +// Client: +{ + saslStart: 1, + mechanism: "MONGODB-OIDC", + db: "$external" + // payload is a BSON generic binary field containing a JwtStepRequest BSON + // document: {"jwt": "abcd1234"} + payload: BinData(0, "FwAAAAJqd3QACQAAAGFiY2QxMjM0AAA=") +} + +// Server: +{ + conversationId : 1, + payload: BinData(0, ""), + done: true, + ok: 1 +} +``` + +##### Two-Step + +A two-step conversation is used for OIDC providers that require an extra authorization step before issuing a credential. +For example, an OIDC provider configured for end-user authentication may require redirecting the user to a webpage so +they can authorize the request. + +Drivers that support the [Human Authentication Flow](#human-authentication-flow) MUST implement the two-step +conversation. Drivers MUST use a two-step conversation when using a [OIDC Human Callback](#oidc-human-callback) and when +there is no cached access token. + +The two-step conversation starts with a `saslStart` containing a `PrincipalStepRequest` payload. The value of `n` is the +`username` from the connection string. If a `username` is not provided, field `n` should be omitted. + +```typescript +interface PrincipalStepRequest { + // Name of the OIDC user principal. + n: Optional; +} +``` + +The server uses `n` (if provided) to select an appropriate IdP. Note that the principal name is optional as it may be +provided by the IdP in environments where only one IdP is used. + +The server responds to the `PrincipalStepRequest` with `IdpInfo` for the selected IdP: + +```typescript +interface IdpInfo { + // A URL which describes the Authentication Server. This identifier should + // be the iss of provided access tokens, and be viable for RFC8414 metadata + // discovery and RFC9207 identification. + issuer: string; + + // A unique client ID for this OIDC client. + clientId: string; + + // A list of additional scopes to request from IdP. + requestScopes: Optional>; +} +``` + +The driver passes the IdP information to the [OIDC Human Callback](#oidc-human-callback), which should return an OIDC +credential containing an access token and, optionally, a refresh token. + +The driver then sends a `saslContinue` with a `JwtStepRequest` payload to complete authentication. The value of `jwt` is +the OIDC access token string. + +```typescript +interface JwtStepRequest: + // Compact serialized JWT with signature. + jwt: string; +} +``` + +An example OIDC two-step SASL conversation with username "myidp" and access token string "abcd1234" looks like: + +```javascript +// Client: +{ + saslStart: 1, + mechanism: "MONGODB-OIDC", + db: "$external", + // payload is a BSON generic binary field containing a PrincipalStepRequest + // BSON document: {"n": "myidp"} + payload: BinData(0, "EgAAAAJuAAYAAABteWlkcAAA") +} + +// Server: +{ + conversationId : 1, + // payload is a BSON generic binary field containing an IdpInfo BSON document: + // {"issuer": "https://issuer", "clientId": "abcd", "requestScopes": ["a","b"]} + payload: BinData(0, "WQAAAAJpc3N1ZXIADwAAAGh0dHBzOi8vaXNzdWVyAAJjbGllbnRJZAAFAAAAYWJjZAAEcmVxdWVzdFNjb3BlcwAXAAAAAjAAAgAAAGEAAjEAAgAAAGIAAAA="), + done: false, + ok: 1 +} + +// Client: +{ + saslContinue: 1, + conversationId: 1, + // payload is a BSON generic binary field containing a JwtStepRequest BSON + // document: {"jwt": "abcd1234"} + payload: BinData(0, "FwAAAAJqd3QACQAAAGFiY2QxMjM0AAA=") +} + +// Server: +{ + conversationId: 1, + payload: BinData(0, ""), + done: true, + ok: 1 +} +``` + +#### Credential Caching + +Some OIDC providers may impose rate limits, incur per-request costs, or be slow to return. To minimize those issues, +drivers MUST cache and reuse access tokens returned by OIDC providers. + +Drivers MUST cache the most recent access token per `MongoClient` (henceforth referred to as the *Client Cache*). +Drivers MAY store the *Client Cache* on the `MongoClient` object or any object that guarantees exactly 1 cached access +token per `MongoClient`. Additionally, drivers MUST cache the access token used to authenticate a connection on the +connection object (henceforth referred to as the *Connection Cache*). + +Drivers MUST ensure that only one call to the configured provider or OIDC callback can happen at a time. To avoid adding +a bottleneck that would override the `maxConnecting` setting, the driver MUST NOT hold an exclusive lock while running +`saslStart` or `saslContinue`. + +Example code for credential caching using the read-through cache pattern: + +```python +def get_access_token(): + # Lock the OIDC authenticator so that only one caller can modify the cache + # and call the configured OIDC provider at a time. + client.oidc_cache.lock() + + # Check if we can use the access token from the Client Cache or if we need + # to fetch and cache a new access token from the OIDC provider. + access_token = client.oidc_cache.access_token + is_cache = True + if access_token is None + credential = oidc_provider() + is_cache = False + client.oidc_cache.access_token = credential.access_token + + client.oidc_cache.unlock() + + return access_token, is_cache +``` + +Drivers MUST have a way to invalidate a specific access token from the *Client Cache*. Invalidation MUST only clear the +cached access token if it is the same as the invalid access token and MUST be an atomic operation (e.g. using a mutex or +a compare-and-swap operation). + +Example code for invalidation: + +```python +def invalidate(access_token): + client.oidc_cache.lock() + + if client.oidc_cache.access_token == access_token: + client.oidc_cache.access_token = None + + client.oidc_cache.unlock() +``` + +Drivers that support the [Human Authentication Flow](#human-authentication-flow) MUST also cache the `IdPInfo` and +refresh token in the *Client Cache* when a [OIDC Human Callback](#oidc-human-callback) is configured. + +**Authentication** + +Use the following algorithm to authenticate a new connection: + +- Check if the the *Client Cache* has an access token. + - If it does, cache the access token in the *Connection Cache* and perform a `One-Step` SASL conversation using the + access token in the *Client Cache*. If the server returns a Authentication error (18), invalidate that access + token. Raise any other errors to the user. On success, exit the algorithm. +- Call the configured built-in provider integration or the OIDC callback to retrieve a new access token. Wait until it + has been at least 100ms since the last callback invocation, to avoid overloading the callback. +- Cache the new access token in the *Client Cache* and *Connection Cache*. +- Perform a `One-Step` SASL conversation using the new access token. Raise any errors to the user. + +Example code to authenticate a connection using the `get_access_token` and `invalidate` functions described above: + +```python +def auth(connection): + access_token, is_cache = get_access_token() + + # If there is a cached access token, try to authenticate with it. If + # authentication fails with an Authentication error (18), + # invalidate the access token, fetch a new access token, and try + # to authenticate again. + # If the server fails for any other reason, do not clear the cache. + if is_cache: + try: + connection.oidc_cache.access_token = access_token + sasl_start(connection, payload={"jwt": access_token}) + return + except ServerError as e: + if e.code == 18: + invalidate(access_token) + access_token, _ = get_access_token() + else: + raise e # Raise other errors. + + connection.oidc_cache.access_token = access_token + sasl_start(connection, payload={"jwt": access_token}) +``` + +For drivers that support the [Human Authentication Flow](#human-authentication-flow), use the following algorithm to +authenticate a new connection when a [OIDC Human Callback](#oidc-human-callback) is configured: + +- Check if the *Client Cache* has an access token. + - If it does, cache the access token in the *Connection Cache* and perform a [One-Step](#one-step) SASL conversation + using the access token. If the server returns an Authentication error (18), invalidate the access token token from + the *Client Cache*, clear the *Connection Cache*, and restart the authentication flow. Raise any other errors to + the user. On success, exit the algorithm. +- Check if the *Client Cache* has a refresh token. + - If it does, call the [OIDC Human Callback](#oidc-human-callback) with the cached refresh token and `IdpInfo` to get + a new access token. Cache the new access token in the *Client Cache* and *Connection Cache*. Perform a + [One-Step](#one-step) SASL conversation using the new access token. If the the server returns an Authentication + error (18), clear the refresh token, invalidate the access token from the *Client Cache*, clear the *Connection + Cache*, and restart the authentication flow. Raise any other errors to the user. On success, exit the algorithm. +- Start a new [Two-Step](#two-step) SASL conversation. +- Run a `PrincipalStepRequest` to get the `IdpInfo`. +- Call the [OIDC Human Callback](#oidc-human-callback) with the new `IdpInfo` to get a new access token and optional + refresh token. Drivers MUST NOT pass a cached refresh token to the callback when performing a new + [Two-Step](#two-step) conversation. +- Cache the new `IdpInfo` and refresh token in the *Client Cache* and the new access token in the *Client Cache* and + *Connection Cache*. +- Attempt to authenticate using a `JwtStepRequest` with the new access token. Raise any errors to the user. + +#### Speculative Authentication + +Drivers MUST implement speculative authentication for MONGODB-OIDC during the `hello` handshake. Drivers MUST NOT +attempt speculative authentication if the *Client Cache* does not have a cached access token. Drivers MUST NOT +invalidate tokens from the *Client Cache* if speculative authentication does not succeed. + +Use the following algorithm to perform speculative authentication: + +- Check if the *Client Cache* has an access token. + - If it does, cache the access token in the *Connection Cache* and send a `JwtStepRequest` with the cached access + token in the speculative authentication SASL payload. If the response is missing a speculative authentication + document or the speculative authentication document indicates authentication was not successful, clear the the + *Connection Cache* and proceed to the next step. +- Authenticate with the standard authentication handshake. + +Example code for speculative authentication using the `auth` function described above: + +```python +def speculative_auth(connection): + access_token = client.oidc_cache.access_token + if access_token != None: + connection.oidc_cache.access_token = access_token + res = hello(connection, payload={"jwt": access_token}) + if res.speculative_authenticate.done: + return + + connection.oidc_cache.access_token = None + auth(connection) +``` + +#### Reauthentication + +If any operation fails with `ReauthenticationRequired` (error code 391) and MONGODB-OIDC is in use, the driver MUST +reauthenticate the connection. Drivers MUST NOT resend a `hello` message during reauthentication, instead using SASL +messages directly. Drivers MUST NOT try to use Speculative Authentication during reauthentication. See the main +[reauthentication](#reauthentication) section for more information. + +To reauthenticate a connection, invalidate the access token stored on the connection (i.e. the *Connection Cache*) from +the *Client Cache*, fetch a new access token, and re-run the SASL conversation. + +Example code for reauthentication using the `auth` function described above: + +```python +def reauth(connection): + invalidate(connection.oidc_cache.access_token) + connection.oidc_cache.access_token = None + auth(connection) +``` + +### Connection String Options + +`mongodb://[username[:password]@]host1[:port1][,[host2:[port2]],...[hostN:[portN]]][/database][?options]` + + + +#### Auth Related Options + +- authMechanism + + MONGODB-CR, MONGODB-X509, GSSAPI, PLAIN, SCRAM-SHA-1, SCRAM-SHA-256, MONGODB-AWS + + Sets the Mechanism property on the MongoCredential. When not set, the default will be one of SCRAM-SHA-256, + SCRAM-SHA-1 or MONGODB-CR, following the auth spec default mechanism rules. + +- authSource + + Sets the Source property on the MongoCredential. + +For GSSAPI, MONGODB-X509 and MONGODB-AWS authMechanisms the authSource defaults to `$external`. For PLAIN the authSource +defaults to the database name if supplied on the connection string or `$external`. For MONGODB-CR, SCRAM-SHA-1 and +SCRAM-SHA-256 authMechanisms, the authSource defaults to the database name if supplied on the connection string or +`admin`. + +- authMechanismProperties=PROPERTY_NAME:PROPERTY_VALUE,PROPERTY_NAME2:PROPERTY_VALUE2 + + A generic method to set mechanism properties in the connection string. + +For example, to set REALM and CANONICALIZE_HOST_NAME, the option would be +`authMechanismProperties=CANONICALIZE_HOST_NAME:forward,SERVICE_REALM:AWESOME`. + +- gssapiServiceName (deprecated) + + An alias for `authMechanismProperties=SERVICE_NAME:mongodb`. + +#### Errors + +Drivers MUST raise an error if the `authSource` option is specified in the connection string with an empty value, e.g. +`mongodb://localhost/admin?authSource=`. + +#### Implementation + +1. Credentials MAY be specified in the connection string immediately after the scheme separator "//". + +2. A realm MAY be passed as a part of the username in the url. It would be something like , where dev + is the username and MONGODB.COM is the realm. Per the RFC, the @ symbol should be url encoded using %40. + + - When GSSAPI is specified, this should be interpreted as the realm. + - When non-GSSAPI is specified, this should be interpreted as part of the username. + +3. It is permissible for only the username to appear in the connection string. This would be identified by having no + colon follow the username before the '@' hostname separator. + +4. The source is determined by the following: + + - if authSource is specified, it is used. + - otherwise, if database is specified, it is used. + - otherwise, the admin database is used. + +## Test Plan + +Connection string tests have been defined in the associated files: + +- [Connection String](./tests/legacy/connection-string.json). + +### SCRAM-SHA-256 and mechanism negotiation + +Testing SCRAM-SHA-256 requires server version 3.7.3 or later with `featureCompatibilityVersion` of "4.0" or later. + +Drivers that allow specifying auth parameters in code as well as via connection string should test both for the test +cases described below. + +#### Step 1 + +Create three test users, one with only SHA-1, one with only SHA-256 and one with both. For example: + +```javascript +db.runCommand({createUser: 'sha1', pwd: 'sha1', roles: ['root'], mechanisms: ['SCRAM-SHA-1']}) +db.runCommand({createUser: 'sha256', pwd: 'sha256', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) +db.runCommand({createUser: 'both', pwd: 'both', roles: ['root'], mechanisms: ['SCRAM-SHA-1', 'SCRAM-SHA-256']}) +``` + +#### Step 2 + +For each test user, verify that you can connect and run a command requiring authentication for the following cases: + +- Explicitly specifying each mechanism the user supports. +- Specifying no mechanism and relying on mechanism negotiation. + +For the example users above, the `dbstats` command could be used as a test command. + +For a test user supporting both SCRAM-SHA-1 and SCRAM-SHA-256, drivers should verify that negotiation selects +SCRAM-SHA-256. This may require monkey patching, manual log analysis, etc. + +#### Step 3 + +For test users that support only one mechanism, verify that explicitly specifying the other mechanism fails. + +For a non-existent username, verify that not specifying a mechanism when connecting fails with the same error type that +would occur with a correct username but incorrect password or mechanism. (Because negotiation with a non-existent user +name at one point during server development caused a handshake error, we want to verify this is seen by users as similar +to other authentication errors, not as a network or database command error on the `hello` or legacy hello commands +themselves.) + +#### Step 4 + +To test SASLprep behavior, create two users: + +1. username: "IX", password "IX" +2. username: "\\u2168" (ROMAN NUMERAL NINE), password "\\u2163" (ROMAN NUMERAL FOUR) + +To create the users, use the exact bytes for username and password without SASLprep or other normalization and specify +SCRAM-SHA-256 credentials: + +```javascript +db.runCommand({createUser: 'IX', pwd: 'IX', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) +db.runCommand({createUser: '\\u2168', pwd: '\\u2163', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) +``` + +For each user, verify that the driver can authenticate with the password in both SASLprep normalized and non-normalized +forms: + +- User "IX": use password forms "IX" and "I\\u00ADX" +- User "\\u2168": use password forms "IV" and "I\\u00ADV" + +As a URI, those have to be UTF-8 encoded and URL-escaped, e.g.: + +- `mongodb://IX:IX@mongodb.example.com/admin` +- `mongodb://IX:I%C2%ADX@mongodb.example.com/admin` +- `mongodb://%E2%85%A8:IV@mongodb.example.com/admin` +- `mongodb://%E2%85%A8:I%C2%ADV@mongodb.example.com/admin` + +### Speculative Authentication + +See the speculative authentication section in the [MongoDB Handshake spec](../mongodb-handshake/handshake.md). + +### Minimum iteration count + +For SCRAM-SHA-1 and SCRAM-SHA-256, test that the minimum iteration count is respected. This may be done via unit testing +of an underlying SCRAM library. + +## Backwards Compatibility + +Drivers may need to remove support for association of more than one credential with a MongoClient, including + +- Deprecation and removal of MongoClient constructors that take as an argument more than a single credential +- Deprecation and removal of methods that allow lazy authentication (i.e post-MongoClient construction) + +Drivers need to support both the shorter and longer SCRAM-SHA-1 and SCRAM-SHA-256 conversations over MongoDB's SASL +implementation. Earlier versions of the server required an extra round trip due to an implementation decision. This was +accomplished by sending no bytes back to the server, as seen in the following conversation (extra round trip +emphasized): + +```javascript +CMD = {saslStart: 1, mechanism: "SCRAM-SHA-1", payload: BinData(0, "biwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM"), options: {skipEmptyExchange: true}} +RESP = {conversationId : 1, payload: BinData(0,"cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw"), done: false, ok: 1} +CMD = {saslContinue: 1, conversationId: 1, payload: BinData(0, "Yz1iaXdzLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMSG8rVmdrN3F2VU9LVXd1V0xJV2c0bC85U3JhR01IRUUscD1NQzJUOEJ2Ym1XUmNrRHc4b1dsNUlWZ2h3Q1k9")} +RESP = {conversationId: 1, payload: BinData(0,"dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9"), done: false, ok: 1} +# Extra round trip +CMD = {saslContinue: 1, conversationId: 1, payload: BinData(0, "")} +RESP = {conversationId: 1, payload: BinData(0,""), done: true, ok: 1} +``` + +The extra round trip will be removed in server version 4.4 when `options: { skipEmptyExchange: true }` is specified +during `saslStart`. + +## Reference Implementation + +The Java and .NET drivers currently uses eager authentication and abide by this specification. + + + +## Q & A + +Q: According to [Authentication Handshake](#authentication-handshake), we are calling `hello` or legacy hello for every +socket. Isn't this a lot? + +Drivers should be pooling connections and, as such, new sockets getting opened should be relatively infrequent. It's +simply part of the protocol for setting up a socket to be used. + +Q: Where is information related to user management? + +Not here currently. Should it be? This is about authentication, not user management. Perhaps a new spec is necessary. + +Q: It's possible to continue using authenticated sockets even if new sockets fail authentication. Why can't we do that +so that applications continue to work. + +Yes, that's technically true. The issue with doing that is for drivers using connection pooling. An application would +function normally until an operation needed an additional connection(s) during a spike. Each new connection would fail +to authenticate causing intermittent failures that would be very difficult to understand for a user. + +Q: Should a driver support multiple credentials? + +No. + +Historically, the MongoDB server and drivers have supported multiple credentials, one per authSource, on a single +connection. It was necessary because early versions of MongoDB allowed a user to be granted privileges to access the +database in which the user was defined (or all databases in the special case of the "admin" database). But with the +introduction of role-based access control in MongoDB 2.6, that restriction was removed and it became possible to create +applications that access multiple databases with a single authenticated user. + +Role-based access control also introduces the potential for accidental privilege escalation. An application may, for +example, authenticate user A from authSource X, and user B from authSource Y, thinking that user A has privileges only +on collections in X and user B has privileges only on collections in Y. But with role-based access control that +restriction no longer exists, and it's possible that user B has, for example, more privileges on collections in X than +user A does. Due to this risk it's generally safer to create a single user with only the privileges required for a given +application, and authenticate only that one user in the application. + +In addition, since only a single credential is supported per authSource, certain mechanisms are restricted to a single +credential and some credentials cannot be used in conjunction (GSSAPI and X509 both use the "$external" database). + +Finally, MongoDB 3.6 introduces sessions, and allows at most a single authenticated user on any connection which makes +use of one. Therefore any application that requires multiple authenticated users will not be able to make use of any +feature that builds on sessions (e.g. retryable writes). + +Drivers should therefore guide application creators in the right direction by supporting the association of at most one +credential with a MongoClient instance. + +Q: Should a driver support lazy authentication? + +No, for the same reasons as given in the previous section, as lazy authentication is another mechanism for allowing +multiple credentials to be associated with a single MongoClient instance. + +Q: Why does SCRAM sometimes SASLprep and sometimes not? + +When MongoDB implemented SCRAM-SHA-1, it required drivers to *NOT* SASLprep usernames and passwords. The primary reason +for this was to allow a smooth upgrade path from MongoDB-CR using existing usernames and passwords. Also, because +MongoDB's SCRAM-SHA-1 passwords are hex characters of a digest, SASLprep of passwords was irrelevant. + +With the introduction of SCRAM-SHA-256, MongoDB requires users to explicitly create new SCRAM-SHA-256 credentials +distinct from those used for MONGODB-CR and SCRAM-SHA-1. This means SCRAM-SHA-256 passwords are not digested and any +Unicode character could now appear in a password. Therefore, the SCRAM-SHA-256 mechanism requires passwords to be +normalized with SASLprep, in accordance with the SCRAM RFC. + +However, usernames must be unique, which creates a similar upgrade path problem. SASLprep maps multiple byte +representations to a single normalized one. An existing database could have multiple existing users that map to the same +SASLprep form, which makes it impossible to find the correct user document for SCRAM authentication given only a +SASLprep username. After considering various options to address or workaround this problem, MongoDB decided that the +best user experience on upgrade and lowest technical risk of implementation is to require drivers to continue to not +SASLprep usernames in SCRAM-SHA-256. + +Q: Should drivers support accessing Amazon EC2 instance metadata in Amazon ECS? + +No. While it's possible to allow access to EC2 instance metadata in ECS, for security reasons, Amazon states it's best +practice to avoid this. (See +[accessing EC2 metadata in ECS](https://aws.amazon.com/premiumsupport/knowledge-center/ecs-container-ec2-metadata/) and +[IAM Roles for Tasks](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html)) + +## Changelog + +- 2025-11-25: Remove redundant `*.mongodbgov.net` on `ALLOWED_HOSTS` + +- 2025-11-19: Extend `ALLOWED_HOSTS` with `*.mongo.com` and `*.mongodbgov.net` + +- 2025-09-30: Remove support for explicitly specifying MONGODDB-AWS authentication properties. + +- 2025-09-10: Update precedence of MONGODB-AWS credential fetching behaviour. + +- 2025-01-29: Add support for custom AWS credential providers. + +- 2024-10-02: Add Kubernetes built-in OIDC provider integration. + +- 2024-08-19: Clarify Reauthentication and Speculative Authentication combination behavior. + +- 2024-05-29: Disallow comma character when `TOKEN_RESOURCE` is given in a connection string. + +- 2024-05-03: Clarify timeout behavior for OIDC machine callback. Add `serverless:forbid` to OIDC unified tests. Add an + additional prose test for the behavior of `ALLOWED_HOSTS`. + +- 2024-04-24: Clarify that TOKEN_RESOURCE for MONGODB-OIDC must be url-encoded. + +- 2024-04-22: Fix API description for GCP built-in OIDC provider. + +- 2024-04-22: Updated OIDC authentication flow and prose tests. + +- 2024-04-22: Clarify that driver should not validate `saslSupportedMechs` content. + +- 2024-04-03: Added GCP built-in OIDC provider integration. + +- 2024-03-29: Updated OIDC test setup and descriptions. + +- 2024-03-21: Added Azure built-in OIDC provider integration. + +- 2024-03-09: Rename OIDC integration name and values. + +- 2024-01-31: Migrated from reStructuredText to Markdown. + +- 2024-01-17: Added MONGODB-OIDC machine auth flow spec and combine with human auth flow specs. + +- 2023-04-28: Added MONGODB-OIDC auth mechanism + +- 2022-11-02: Require environment variables to be read dynamically. + +- 2022-10-28: Recommend the use of AWS SDKs where available. + +- 2022-10-07: Require caching of AWS credentials fetched by the driver. + +- 2022-10-05: Remove spec front matter and convert version history to changelog. + +- 2022-09-07: Add support for AWS AssumeRoleWithWebIdentity. + +- 2022-01-20: Require that timeouts be applied per the client-side operations timeout spec. + +- 2022-01-14: Clarify that `OP_MSG` must be used for authentication when it is supported. + +- 2021-04-23: Updated to use hello and legacy hello. + +- 2021-03-04: Note that errors encountered during auth are handled by SDAM. + +- 2020-03-06: Add reference to the speculative authentication section of the handshake spec. + +- 2020-02-15: Rename MONGODB-IAM to MONGODB-AWS + +- 2020-02-04: Support shorter SCRAM conversation starting in version 4.4 of the server. + +- 2020-01-31: Clarify that drivers must raise an error when a connection string has an empty value for authSource. + +- 2020-01-23: Clarify when authentication will occur. + +- 2020-01-22: Clarify that authSource in URI is not treated as a user configuring auth credentials. + +- 2019-12-05: Added MONGODB-IAM auth mechanism + +- 2019-07-13: Clarify database to use for auth mechanism negotiation. + +- 2019-04-26: Test format changed to improve specificity of behavior assertions. + + - Clarify that database name in URI is not treated as a user configuring auth credentials. + +- 2018-08-08: Unknown users don't cause handshake errors. This was changed before server 4.0 GA in SERVER-34421, so the + auth spec no longer refers to such a possibility. + +- 2018-04-17: Clarify authSource defaults + + - Fix PLAIN authSource rule to allow user provided values + - Change SCRAM-SHA-256 rules such that usernames are *NOT* normalized; this follows a change in the server design and + should be available in server 4.0-rc0. + +- 2018-03-29: Clarify auth handshake and that it only applies to non-monitoring sockets. + +- 2018-03-15: Describe CANONICALIZE_HOST_NAME algorithm. + +- 2018-03-02: Added SCRAM-SHA-256 and mechanism negotiation as provided by server 4.0 + + - Updated default mechanism determination + - Clarified SCRAM-SHA-1 rules around SASLprep + - Require SCRAM-SHA-1 and SCRAM-SHA-256 to enforce a minimum iteration count + +- 2017-11-10: Updated minimum server version to 2.6 + + - Updated the Q & A to recommend support for at most a single credential per MongoClient + - Removed lazy authentication section + - Changed the list of server types requiring authentication + - Made providing username for X509 authentication optional + +- 2015-02-04: Added SCRAM-SHA-1 sasl mechanism + + - Added connection handshake + - Changed connection string to support mechanism properties in generic form + - Added example conversations for all mechanisms except GSSAPI + - Miscellaneous wording changes for clarification + - Added MONGODB-X509 + - Added PLAIN sasl mechanism + - Added support for GSSAPI mechanism property gssapiServiceName + +______________________________________________________________________ diff --git a/source/auth/auth.rst b/source/auth/auth.rst index e605dabdee..cffd20f3b1 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1,925 +1,6 @@ .. role:: javascript(code) :language: javascript -===================== -Driver Authentication -===================== - -:Spec: 100 -:Spec Version: 1.7.1 -:Title: Driver Authentication -:Author: Craig Wilson, David Golden -:Advisors: Andy Schwerin, Bernie Hacket, Jeff Yemin, David Golden -:Status: Accepted -:Type: Standards -:Minimum Server Version: 2.6 -:Last Modified: 2018-04-17 - -.. contents:: - --------- - -Abstract -======== - -MongoDB supports various authentication strategies across various versions. When authentication is turned on in the database, a driver must authenticate before it is allowed to communicate with the server. This spec defines when and how a driver performs authentication with a MongoDB server. - ----- -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. - ----------- -References ----------- - -`Server Discovery and Monitoring `_ - -Specification -============= - ------------ -Definitions ------------ - -Credential - The pieces of information used to establish the authenticity of a user. This is composed of an identity and some form of evidence such as a password or a certificate. - -FQDN - Fully Qualified Domain Name - -Mechanism - A SASL implementation of a particular type of credential negotiation. - -Source - The authority used to establish credentials and/or privileges in reference to a mongodb server. In practice, it is the database to which sasl authentication commands are sent. - -Realm - The authority used to establish credentials and/or privileges in reference to GSSAPI. - -SASL - Simple Authentication and Security Layer - `RFC 4422 `_ - - ---------------------- -Client Implementation ---------------------- - - -MongoCredential ---------------- - -Drivers SHOULD contain a type called `MongoCredential`. It SHOULD contain some or all of the following information. - -username (string) - * Applies to all mechanisms. - * Optional for MONGODB-X509. -source (string) - * Applies to all mechanisms. - * Always '$external' for GSSAPI and MONGODB-X509. - * This is the database to which the authenticate command will be sent. - * This is the database to which sasl authentication commands will be sent. -password (string) - * Does not apply to all mechanisms. -mechanism (string) - * Indicates which mechanism to use with the credential. -mechanism_properties - * Includes additional properties for the given mechanism. - - -Errors -~~~~~~ - -Drivers SHOULD raise an error as early as possible when detecting invalid values in a credential. For instance, if a ``mechanism_property`` is specified for `MONGODB-CR`_, the driver should raise an error indicating that the property does not apply. - - -Naming -~~~~~~ - -Naming of this information MUST be idiomatic to the driver's language/framework but still remain consistent. For instance, python would use "mechanism_properties" and .NET would use "MechanismProperties". - -Naming of mechanism properties MUST be case-insensitive. For instance, SERVICE_NAME and service_name refer to the same property. - - -Authentication --------------- - -A MongoClient instance MUST be considered a single logical connection to -the server/deployment. - -Socket connections from a MongoClient to deployment members can be one -of two types: - -* Monitoring-only socket: multi-threaded drivers maintain monitoring - sockets separate from sockets in connection pools. - -* General-use socket: for multi-threaded drivers, these are sockets in - connection pools used for (non-monitoring) user operations; in - single-threaded drivers, these are used for both monitoring and user - operations. - -Authentication (including mechanism negotiation) MUST NOT happen on -monitoring-only sockets. - -If one or more credentials are provided to a MongoClient, then whenever -a general-use socket is opened, drivers MUST immediately conduct an -authentication handshake over that socket. - -Drivers SHOULD require all credentials to be specified upon construction -of the MongoClient. This is defined as eager authentication and drivers -MUST support this mode. - -Authentication Handshake -~~~~~~~~~~~~~~~~~~~~~~~~ - -An authentication handshake consists of an initial ``isMaster`` command -possibly followed by one or more authentication conversations. - -Drivers MUST follow the following steps for an authentication -handshake: - -#. Upon opening a general-use socket to a server for a given - MongoClient, drivers MUST issue a `MongoDB Handshake - <../mongodb-handshake/handshake.rst>`_ immediately. This allows a - driver to determine the server type. If the ``isMaster`` of the - MongoDB Handshake fails with an error, drivers MUST treat this an - an authentication error. - -# If the server is not of type Standalone, RSPrimary, RSSecondary or - Mongos, no authentication is possible and the handshake is complete. - -#. If credentials exist: - - #. A driver MUST authenticate with all credentials provided to the - MongoClient. - - #. A single invalid credential is the same as all credentials being - invalid. - -If the authentication handshake fails for a socket, drivers MUST close all -other general-use sockets connected to the same server. - -Mechanism Negotiation via Handshake -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:since: 4.0 - -If an application provides a username but does not provide an -authentication mechanism, drivers MUST negotiate a mechanism via an -``isMaster`` command requesting a user's supported SASL mechanisms:: - - {isMaster: 1, saslSupportedMechs: "."} - -In this example ```` is the authentication database name (default -'admin') and ```` is the username provided in the auth credential. -The username MUST NOT be modified from the form provided by the user (i.e. do -not normalize with SASLprep), as the server uses the raw form to look for -conflicts with legacy credentials. - -If the ``isMaster`` response includes a -``saslSupportedMechs`` field, then drivers MUST use the contents of that field -to select a default mechanism as described later. If the command succeeds and -the response does not include a ``saslSupportedMechs`` field, then drivers MUST -use the legacy default mechanism rules for servers older than 4.0. - -Single-credential drivers -````````````````````````` - -When the authentication mechanism is not specied, drivers that allow -only a single credential per client MUST perform mechanism negotiation -as part of the MongoDB Handshake portion of the authentication -handshake. This lets authentication proceed without a separate -negotiation round-trip exchange with the server. - -Multi-credential drivers -```````````````````````` - -The use of multiple credentials within a driver is discouraged, but some -legacy drivers still allow this. Such drivers may not have user credentials -when connections are opened and thus will not be able to do negotiation. - -Drivers with a list of credentials at the time a connection is opened MAY do -mechanism negotiation on the initial handshake, but only for the first -credential in the list of credentials. - -When authenticating each credential, if the authentication mechanism is not -specified and has not been negotiated for that credential: - -- If the connection handshake results indicate the server version is 4.0 or - later, drivers MUST send a new ``isMaster`` negotiation command for the - credential to determine the default authentication mechanism. - -- Otherwise, when the server version is earlier than 4.0, the driver MUST - select a default authentication mechanism for the credential following the - instructions for when the ``saslSupportedMechs`` field is not present in - an ``isMaster`` response. - -Caching credentials in SCRAM -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the implementation of SCRAM authentication mechanisms (e.g. SCRAM-SHA-1 -and SCRAM-SHA-256), drivers MUST maintain a cache of computed SCRAM credentials. -The cache entries SHOULD be identified by the password, salt, iteration count, -and a value that uniquely identifies the authentication mechanism (e.g. "SHA1" -or "SCRAM-SHA-256"). - -The cache entry value MUST be either the ``saltedPassword`` parameter or the -combination of the ``clientKey`` and ``serverKey`` parameters. - --------------------------------- -Supported Authentication Methods --------------------------------- - -Defaults --------- - -:since: 3.0 -:revised: 4.0 - -If the user did not provide a mechanism via the connection string or via code, -the following logic describes how to select a default. - -If a ``saslSupportedMechs`` field was present in the ``isMaster`` results for -mechanism negotiation, then it MUST be inspected to select a default -mechanism:: - - { - "ismaster" : true, - "saslSupportedMechs": ["SCRAM-SHA-1", "SCRAM-SHA-256"], - ... - "ok" : 1 - } - -If SCRAM-SHA-256 is present in the list of mechanism, then it MUST be -used as the default; otherwise, SCRAM-SHA-1 MUST be used as the default, -regardless of whether SCRAM-SHA-1 is in the list. Drivers MUST NOT -attempt to use any other mechanism (e.g. PLAIN) as the default. - -If ``saslSupportedMechs`` is not present in the ``isMaster`` results for -mechanism negotiation, then SCRAM-SHA-1 MUST be used when talking to servers >= -3.0. Prior to server 3.0, MONGODB-CR MUST be used. - -When a user has specified a mechanism, regardless of the server version, the -driver MUST honor this and attempt to authenticate. - -Determining Server Version -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers SHOULD use the server's wire version ranges to determine the server's -version. - -MongoDB Custom Mechanisms -------------------------- - -MONGODB-CR -~~~~~~~~~~ - -:since: 1.4 -:deprecated: 3.0 -:removed: 4.0 - -MongoDB Challenge Response is a nonce and MD5 based system. The driver sends a `getNonce` command, encodes and hashes the password using the returned nonce, and then sends an `authenticate` command. - -Conversation -```````````` - -#. Send ``getNonce`` command - * :javascript:`{ getNonce: 1 }` - * Response: :javascript:`{ nonce: }` -#. Compute key - * :javascript:`passwordDigest = HEX( MD5( UTF8( username + ':mongo:' + password )))` - * :javascript:`key = HEX( MD5( UTF8( nonce + username + passwordDigest )))` -#. Send ``authenticate`` command - * :javascript:`{ authenticate: 1, nonce: nonce, user: username, key: key }` - -As an example, given a username of "user" and a password of "pencil", the conversation would appear as follows: - -| C: :javascript:`{getnonce : 1}` -| S: :javascript:`{nonce: "2375531c32080ae8", ok: 1}` -| C: :javascript:`{authenticate: 1, user: "user", nonce: "2375531c32080ae8", key: "21742f26431831d5cfca035a08c5bdf6"}` -| S: :javascript:`{ok: 1}` - -`MongoCredential`_ Properties -````````````````````````````` - -username - MUST be specified. - -source - MUST be specified. Defaults to the database name if supplied on the connection string or ``admin``. - -password - MUST be specified. - -mechanism - MUST be "MONGODB-CR" - -mechanism_properties - MUST NOT be specified. - - -MONGODB-X509 -~~~~~~~~~~~~ - -:since: 2.6 -:changed: 3.4 - - -MONGODB-X509 is the usage of X.509 certificates to validate a client where the -distinguished subject name of the client certificate acts as the username. - -When connected to MongoDB 3.4: - * You MUST NOT raise an error when the application only provides an X.509 certificate and no username. - * If the application does not provide a username you MUST NOT send a username to the server. - * If the application provides a username you MUST send that username to the server. -When connected to MongoDB 3.2 or earlier: - * You MUST send a username to the server. - * If no username is provided by the application, you MAY extract the username from the X.509 certificate instead of requiring the application to provide it. - * If you choose not to automatically extract the username from the certificate you MUST error when no username is provided by the application. - - -Conversation -```````````` - -#. Send ``authenticate`` command (MongoDB 3.4+) - * C: :javascript:`{"authenticate": 1, "mechanism": "MONGODB-X509"}` - * S: :javascript:`{"dbname" : "$external", "user" : "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client", "ok" : 1}` - -#. Send ``authenticate`` command with username: - * ``username = openssl x509 -subject -nameopt RFC2253 -noout -inform PEM -in my-cert.pem`` - * C: :javascript:`{authenticate: 1, mechanism: "MONGODB-X509", user: "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client"}` - * S: :javascript:`{"dbname" : "$external", "user" : "C=IS,ST=Reykjavik,L=Reykjavik,O=MongoDB,OU=Drivers,CN=client", "ok" : 1}` - - -`MongoCredential`_ Properties -````````````````````````````` - -username - SHOULD NOT be provided for MongoDB 3.4+ - MUST be specified for MongoDB prior to 3.4 - -source - MUST be "$external". Defaults to ``$external``. - -password - MUST NOT be specified. - -mechanism - MUST be "MONGODB-X509" - -mechanism_properties - MUST NOT be specified. - - -TODO: Errors - - -SASL Mechanisms ---------------- - -:since: 2.4 enterprise - -SASL mechanisms are all implemented using the same sasl commands and interpreted as defined by the `SASL specification RFC 4422 `_. - -#. Send the `saslStart` command. - * :javascript:`{ saslStart: 1, mechanism: , payload: BinData(...), autoAuthorize: 1 }` - * Response: :javascript:`{ conversationId: , code: , done: , payload: }` - - conversationId: the conversation identifier. This will need to be remembered and used for the duration of the conversation. - - code: A response code that will indicate failure. This field is not included when the command was successful. - - done: a boolean value indicating whether or not the conversation has completed. - - payload: a sequence of bytes or a base64 encoded string (depending on input) to pass into the SASL library to transition the state machine. -#. Continue with the `saslContinue` command while `done` is `false`. - * :javascript:`{ saslContinue: 1, conversationId: conversationId, payload: BinData(...) }` - * Response is the same as that of `saslStart` - - -Many languages will have the ability to utilize 3rd party libraries. The server uses `cyrus-sasl `_ and it would make sense for drivers with a choice to also choose cyrus. However, it is important to ensure that when utilizing a 3rd party library it does implement the mechanism on all supported OS versions and that it interoperates with the server. For instance, the cyrus sasl library offered on RHEL 6 does not implement SCRAM-SHA-1. As such, if your driver supports RHEL 6, you'll need to implement SCRAM-SHA-1 from scratch. - - -GSSAPI -~~~~~~ - -:since: - 2.4 enterprise - - 2.6 enterprise on windows - -GSSAPI is kerberos authentication as defined in `RFC 4752 `_. Microsoft has a proprietary implementation called SSPI which is compatible with both windows and linux clients. - -`MongoCredential`_ properties: - -username - MUST be specified. - -source - MUST be "$external". Defaults to ``$external``. - -password - MAY be specified. - -mechanism - MUST be "GSSAPI" - -mechanism_properties - SERVICE_NAME - Drivers MUST allow the user to specify a different service name. The default is "mongodb". - - CANONICALIZE_HOST_NAME - Drivers MAY allow the user to request canonicalization of the hostname. This might be required when the hosts report different hostnames than what is used in the kerberos database. The default is "false". - - SERVICE_REALM - Drivers MAY allow the user to specify a different realm for the service. This might be necessary to support cross-realm authentication where the user exists in one realm and the service in another. - -Hostname Canonicalization -````````````````````````` - -If CANONICALIZE_HOST_NAME is true, the client MUST canonicalize the name of each host it uses for authentication. There are two options. First, if the client's underlying GSSAPI library provides hostname canonicalization, the client MAY rely on it. For example, MIT Kerberos has `a configuration option for canonicalization `_. - -Second, the client MAY implement its own canonicalization. If so, the canonicalization algorithm MUST be:: - - addresses = fetch addresses for host - if no addresses: - throw error - - address = first result in addresses - - while true: - cnames = fetch CNAME records for host - if no cnames: - break - - # Unspecified which CNAME is used if > 1. - host = one of the records in cnames - - reversed = do a reverse DNS lookup for address - if reversed: - canonicalized = lowercase(reversed) - else: - canonicalized = lowercase(host) - -For example, here is a Python implementation of this algorithm using ``getaddrinfo`` (for address and CNAME resolution) and ``getnameinfo`` (for reverse DNS). - -.. code-block:: python - - from socket import * - import sys - - - def canonicalize(host): - # Get a CNAME for host, if any. - af, socktype, proto, canonname, sockaddr = getaddrinfo( - host, None, 0, 0, IPPROTO_TCP, AI_CANONNAME)[0] - - print('address from getaddrinfo: [%s]' % (sockaddr[0],)) - print('canonical name from getaddrinfo: [%s]' % (canonname,)) - - try: - # NI_NAMEREQD requests an error if getnameinfo fails. - name = getnameinfo(sockaddr, NI_NAMEREQD) - except gaierror as exc: - print('getname info failed: "%s"' % (exc,)) - return canonname.lower() - - return name[0].lower() - - - canonicalized = canonicalize(sys.argv[1]) - print('canonicalized: [%s]' % (canonicalized,)) - -Beware of a bug in older glibc where ``getaddrinfo`` uses PTR records instead of CNAMEs if the address family hint is AF_INET6, and beware of a bug in older MIT Kerberos that causes it to always do reverse DNS lookup even if the ``rdns`` configuration option is set to ``false``. - -PLAIN -~~~~~ - -:since: 2.6 enterprise - -The PLAIN mechanism, as defined in `RFC 4616 `_, is used in MongoDB to perform LDAP authentication. It cannot be used to perform any other type of authentication. Since the credentials are stored outside of MongoDB, the `$external` database must be used for authentication. - -Conversation -```````````` - -As an example, given a username of "user" and a password of "pencil", the conversation would appear as follows: - -| C: :javascript:`{saslStart: 1, mechanism: "PLAIN", payload: BinData(0, "AHVzZXIAcGVuY2ls")}` -| S: :javascript:`{conversationId: 1, payload: BinData(0,""), done: true, ok: 1}` - -If your sasl client is also sending the authzid, it would be "user" and the conversation would appear as follows: - -| C: :javascript:`{saslStart: 1, mechanism: "PLAIN", payload: BinData(0, "dXNlcgB1c2VyAHBlbmNpbA==")}` -| S: :javascript:`{conversationId: 1, payload: BinData(0,""), done: true, ok: 1}` - -MongoDB supports either of these forms. - -`MongoCredential`_ Properties -````````````````````````````` - -username - MUST be specified. - -source - MUST be specified. Defaults to the database name if supplied on the connection string or ``$external``. - -password - MUST be specified. - -mechanism - MUST be "PLAIN" - -mechanism_properties - MUST NOT be specified. - - -SCRAM-SHA-1 -~~~~~~~~~~~ - -:since: 3.0 - -SCRAM-SHA-1 is defined in `RFC 5802 `_. - -`Page 11 of the RFC `_ specifies -that user names be prepared with SASLprep, but drivers MUST NOT do so. - -`Page 8 of the RFC `_ identifies the -"SaltedPassword" as ``:= Hi(Normalize(password), salt, i)``. The ``password`` -variable MUST be the mongodb hashed variant. The mongo hashed variant is -computed as :javascript:`hash = HEX( MD5( UTF8( username + ':mongo:' + -plain_text_password )))`, where ``plain_text_password`` is actually plain text. -The ``username`` and ``password`` MUST NOT be prepared with SASLprep before -hashing. - -For example, to compute the ClientKey according to the RFC: - -.. code:: javascript - - // note that "salt" and "i" have been provided by the server - function computeClientKey(username, plain_text_password) { - mongo_hashed_password = HEX( MD5( UTF8( username + ':mongo:' + plain_text_password ))); - saltedPassword = Hi(Normalize(mongo_hashed_password), salt, i); - clientKey = HMAC(saltedPassword, "Client Key"); - } - -In addition, SCRAM-SHA-1 requires that a client create a randomly generated -nonce. It is imperative, for security sake, that this be as secure and truly -random as possible. For instance, Java provides both a Random class as well as -a SecureRandom class. SecureRandom is cryptographically generated while Random -is just a pseudo-random generator with predictable outcomes. - -Additionally, drivers MUST enforce a minimum iteration count of 4096 and MUST -error if the authentication conversation specifies a lower count. This -mitigates downgrade attacks by a man-in-the-middle attacker. - -Drivers MUST NOT advertise support for channel binding, as the server does -not support it and legacy servers may fail authentication if drivers advertise -support. I.e. the client-first-message MUST start with ``n,``. - -Conversation -```````````` - -As an example, given a username of "user" and a password of "pencil" and an r -value of "fyko+d2lbbFgONRv9qkxdawL", a SCRAM-SHA-1 conversation would appear as -follows: - -| C: ``n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL`` -| S: ``r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000`` -| C: ``c=biws,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,p=MC2T8BvbmWRckDw8oWl5IVghwCY=`` -| S: ``v=UMWeI25JD1yNYZRMpZ4VHvhZ9e0=`` - -This same conversation over mongodb's sasl implementation would appear as follows: - -| C: :javascript:`{saslStart: 1, mechanism: "SCRAM-SHA-1", payload: BinData(0, "biwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM")}` -| S: :javascript:`{conversationId : 1, payload: BinData(0,"cj1meWtvK2QybGJiRmdPTlJ2OXFreGRhd0xIbytWZ2s3cXZVT0tVd3VXTElXZzRsLzlTcmFHTUhFRSxzPXJROVpZM01udEJldVAzRTFURFZDNHc9PSxpPTEwMDAw"), done: false, ok: 1}` -| C: :javascript:`{saslContinue: 1, conversationId: 1, payload: BinData(0, "Yz1iaXdzLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMSG8rVmdrN3F2VU9LVXd1V0xJV2c0bC85U3JhR01IRUUscD1NQzJUOEJ2Ym1XUmNrRHc4b1dsNUlWZ2h3Q1k9")}` -| S: :javascript:`{conversationId: 1, payload: BinData(0,"dj1VTVdlSTI1SkQxeU5ZWlJNcFo0Vkh2aFo5ZTA9"), done: false, ok: 1}` -| C: :javascript:`{saslContinue: 1, conversationId: 1, payload: BinData(0, "")}` -| S: :javascript:`{conversationId: 1, payload: BinData(0,""), done: true, ok: 1}` - .. note:: - - There is an extra round trip due to an implementation decision on the server. This is accomplished by sending no bytes back to the server for what is effectively a no-op. - -`MongoCredential`_ Properties -````````````````````````````` - -username - MUST be specified. - -source - MUST be specified. Defaults to the database name if supplied on the connection string or ``admin``. - -password - MUST be specified. - -mechanism - MUST be "SCRAM-SHA-1" - -mechanism_properties - MUST NOT be specified. - -SCRAM-SHA-256 -~~~~~~~~~~~~~ - -:since: 4.0 - -SCRAM-SHA-256 extends `RFC 5802 `_ and -is formally defined in `RFC 7677 `_. - -The MongoDB SCRAM-SHA-256 mechanism works similarly to the SCRAM-SHA-1 -mechanism, with the following changes: - -- The SCRAM algorithm MUST use SHA-256 as the hash function instead of SHA-1. -- User names MUST NOT be prepared with SASLprep. This intentionally - contravenes the "SHOULD" provision of RFC 5802. -- Passwords MUST be prepared with SASLprep, per RFC 5802. Passwords are - used directly for key derivation ; they MUST NOT be digested as they are in - SCRAM-SHA-1. - -Additionally, drivers MUST enforce a minimum iteration count of 4096 and MUST -error if the authentication conversation specifies a lower count. This -mitigates downgrade attacks by a man-in-the-middle attacker. - -Conversation -```````````` - -As an example, given a username of "user" and a password of "pencil" and an r -value of "rOprNGfwEbeRWgbNEkqO", a SCRAM-SHA-256 conversation would appear as -follows: - -| C: ``n,,n=user,r=rOprNGfwEbeRWgbNEkqO`` -| S: ``r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096`` -| C: ``c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=`` -| S: ``v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=`` - -`MongoCredential`_ Properties -````````````````````````````` - -username - MUST be specified. - -source - MUST be specified. Defaults to the database name if supplied on the connection string or ``admin``. - -password - MUST be specified. - -mechanism - MUST be "SCRAM-SHA-256" - -mechanism_properties - MUST NOT be specified. - -------------------------- -Connection String Options -------------------------- - -``mongodb://[username[:password]@]host1[:port1][,[host2:[port2]],...[hostN:[portN]]][/database][?options]`` - - -Auth Related Options --------------------- - -authMechanism - MONGODB-CR, MONGODB-X509, GSSAPI, PLAIN, SCRAM-SHA-1, SCRAM-SHA-256 - - Sets the Mechanism property on the MongoCredential. When not set, the default will be one of SCRAM-SHA-256, SCRAM-SHA-1 or MONGODB-CR, following the auth spec default mechanism rules. - -authSource - Sets the Source property on the MongoCredential. - - For GSSAPI and MONGODB-X509 authMechanisms the authSource defaults to ``$external``. - For PLAIN the authSource defaults to the database name if supplied on the connection string or ``$external``. - For MONGODB-CR, SCRAM-SHA-1 and SCRAM-SHA-256 authMechanisms, the authSource defaults to the database name if supplied on the connection string or ``admin``. - -authMechanismProperties=PROPERTY_NAME:PROPERTY_VALUE,PROPERTY_NAME2:PROPERTY_VALUE2 - A generic method to set mechanism properties in the connection string. - - For example, to set REALM and CANONICALIZE_HOST_NAME, the option would be ``authMechanismProperties=CANONICALIZE_HOST_NAME:true,SERVICE_REALM:AWESOME``. - -gssapiServiceName (deprecated) - An alias for ``authMechanismProperties=SERVICE_NAME:mongodb``. - - -Implementation --------------- - -#. Credentials MAY be specified in the connection string immediately after the scheme separator "//". -#. A realm MAY be passed as a part of the username in the url. It would be something like dev@MONGODB.COM, where dev is the username and MONGODB.COM is the realm. Per the RFC, the @ symbol should be url encoded using %40. - * When GSSAPI is specified, this should be interpretted as the realm. - * When non-GSSAPI is specified, this should be interpetted as part of the username. -#. It is permissible for only the username to appear in the connection string. This would be identified by having no colon follow the username before the '@' hostname separator. -#. The source is determined by the following: - * if authSource is specified, it is used. - * otherwise, if database is specified, it is used. - * otherwise, the admin database is used. - - -Test Plan -========= - -Connection string tests have been defined in the associated files: - -* `Connection String `_. - ---------------------------------------- -SCRAM-SHA-256 and mechanism negotiation ---------------------------------------- - -Testing SCRAM-SHA-256 requires server version 3.7.3 or later with -``featureCompatibilityVersion`` of "4.0" or later. - -Drivers that allow specifying auth parameters in code as well as via -connection string should test both for the test cases described below. - -Step 1 ------- - -Create three test users, one with only SHA-1, one with only SHA-256 and one -with both. For example:: - - db.runCommand({createUser: 'sha1', pwd: 'sha1', roles: ['root'], mechanisms: ['SCRAM-SHA-1']}) - db.runCommand({createUser: 'sha256', pwd: 'sha256', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) - db.runCommand({createUser: 'both', pwd: 'both', roles: ['root'], mechanisms: ['SCRAM-SHA-1', 'SCRAM-SHA-256']}) - -Step 2 ------- - -For each test user, verify that you can connect and run a command requiring -authentication for the following cases: - -- Explicitly specifying each mechanism the user supports. -- Specifying no mechanism and relying on mechanism negotiation. - -For the example users above, the ``dbstats`` command could be used as a test -command. - -For a test user supporting both SCRAM-SHA-1 and SCRAM-SHA-256, drivers should -verify that negotation selects SCRAM-SHA-256. This may require monkey -patching, manual log analysis, etc. - -Step 3 ------- - -For test users that support only one mechanism, verify that explictly specifying -the other mechanism fails. - -For a non-existent username, verify that not specifying a mechanism when -connecting fails with the same error type that would occur with a correct -username but incorrect password or mechanism. (Because negotiation with a -non-existent user name at one point during server development caused an -isMaster error, we want to verify this is seen by users as similar to other -authentication errors, not as a network or database command error on the 'ismaster' -command itself.) - -Step 4 ------- - -To test SASLprep behavior, create two users: - -#. username: "IX", password "IX" -#. username: "\\u2168" (ROMAN NUMERAL NINE), password "\\u2163" (ROMAN NUMERAL FOUR) - -To create the users, use the exact bytes for username and password without -SASLprep or other normalization and specify SCRAM-SHA-256 credentials: - - db.runCommand({createUser: 'IX', pwd: 'IX', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) - db.runCommand({createUser: '\\u2168', pwd: '\\u2163', roles: ['root'], mechanisms: ['SCRAM-SHA-256']}) - -For each user, verify that the driver can authenticate with the password in -both SASLprep normalized and non-normalized forms: - -- User "IX": use password forms "IX" and "I\\u00ADX" -- User "\\u2168": use password forms "IV" and "I\\u00ADV" - -As a URI, those have to be UTF-8 encoded and URL-escaped, e.g.: - -- mongodb://IX:IX@mongodb.example.com/admin -- mongodb://IX:I%C2%ADX@mongodb.example.com/admin -- mongodb://%E2%85%A8:IV@mongodb.example.com/admin -- mongodb://%E2%85%A8:I%C2%ADV@mongodb.example.com/admin - ------------------------ -Minimum iteration count ------------------------ - -For SCRAM-SHA-1 and SCRAM-SHA-256, test that the minimum iteration count -is respected. This may be done via unit testing of an underlying SCRAM -library. - -Backwards Compatibility -======================= - -Drivers may need to remove support for association of more than one credential with a MongoClient, including - - * Deprecation and removal of MongoClient constructors that take as an argument more than a single credential - * Deprecation and removal of methods that allow lazy authentication (i.e post-MongoClient construction) - -Reference Implementation -======================== - -The Java and .NET drivers currently uses eager authentication and abide by this specification. - -Q & A -===== - -Q: According to `Authentication Handshake`_, we are calling isMaster for every socket. Isn't this a lot? - Drivers should be pooling connections and, as such, new sockets getting opened should be relatively infrequent. It's simply part of the protocol for setting up a socket to be used. - -Q: Where is information related to user management? - Not here currently. Should it be? This is about authentication, not user management. Perhaps a new spec is necessary. - -Q: It's possible to continue using authenticated sockets even if new sockets fail authentication. Why can't we do that so that applications continue to work. - Yes, that's technically true. The issue with doing that is for drivers using connection pooling. An application would function normally until an operation needed an additional connection(s) during a spike. Each new connection would fail to authenticate causing intermittent failures that would be very difficult to understand for a user. - -Q: Should a driver support multiple credentials? - No. - - Historically, the MongoDB server and drivers have supported multiple credentials, one per authSource, on a single connection. It was necessary because early versions of MongoDB allowed a user to be granted privileges - to access the database in which the user was defined (or all databases in the special case of the "admin" database). But with the introduction of role-based access control in MongoDB 2.6, that restriction was - removed and it became possible to create applications that access multiple databases with a single authenticated user. - - Role-based access control also introduces the potential for accidental privilege escalation. An application may, for example, authenticate user A from authSource X, and user B from authSource Y, thinking that - user A has privileges only on collections in X and user B has privileges only on collections in Y. But with role-based access control that restriction no longer exists, and it's possible that user B has, for example, - more privileges on collections in X than user A does. Due to this risk it's generally safer to create a single user with only the privileges required for a given application, and authenticate only that one user - in the application. - - In addition, since only a single credential is supported per authSource, certain mechanisms are restricted to a single credential and some credentials cannot be used in conjunction (GSSAPI and X509 both use the "$external" database). - - Finally, MongoDB 3.6 introduces sessions, and allows at most a single authenticated user on any connection which makes use of one. Therefore any application that requires multiple authenticated users will not be able to make use of any feature that builds on sessions (e.g. retryable writes). - - Drivers should therefore guide application creators in the right direction by supporting the association of at most one credential with a MongoClient instance. - -Q: Should a driver support lazy authentication? - No, for the same reasons as given in the previous section, as lazy authentication is another mechanism for allowing multiple credentials to be associated with a single MongoClient instance. - -Q: Why does SCRAM sometimes SASLprep and sometimes not? - When MongoDB implemented SCRAM-SHA-1, it required drivers to *NOT* SASLprep - usernames and passwords. The primary reason for this was to allow a smooth - upgrade path from MongoDB-CR using existing usernames and passwords. - Also, because MongoDB's SCRAM-SHA-1 passwords are hex characters of a digest, - SASLprep of passwords was irrelevant. - - With the introduction of SCRAM-SHA-256, MongoDB requires users to - explicitly create new SCRAM-SHA-256 credentials distinct from those used - for MONGODB-CR and SCRAM-SHA-1. This means SCRAM-SHA-256 passwords are not - digested and any Unicode character could now appear in a password. - Therefore, the SCRAM-SHA-256 mechanism requires passwords to be normalized - with SASLprep, in accordance with the SCRAM RFC. - - However, usernames must be unique, which creates a similar upgrade path - problem. SASLprep maps multiple byte representations to a single - normalized one. An existing database could have multiple existing users - that map to the same SASLprep form, which makes it impossible to find the - correct user document for SCRAM authentication given only a SASLprep - username. After considering various options to address or workaround this - problem, MongoDB decided that the best user experience on upgrade and - lowest technical risk of implementation is to require drivers to continue - to not SASLprep usernames in SCRAM-SHA-256. - -Version History -=============== - -Version 1.7.1 Changes - * Unknown users don't cause ismaster errors. This was changed before - server 4.0 GA in SERVER-34421, so the auth spec no longer refers to - such a possibility. - -Version 1.7 Changes - * Clarify authSource defaults - * Fix PLAIN authSource rule to allow user provided values - -Version 1.6 Changes - * Change SCRAM-SHA-256 rules such that usernames are *NOT* normalized; - this follows a change in the server design and should be available in - server 4.0-rc0. - -Version 1.5 Changes - * Clarify auth handshake and that it only applies to non-monitoring - sockets. - -Version 1.4.1 Changes - * Describe CANONICALIZE_HOST_NAME algorithm. - -Version 1.4 Changes - * Added SCRAM-SHA-256 and mechanism negotiation as provided by server 4.0 - * Updated default mechanism determination - * Clarified SCRAM-SHA-1 rules around SASLprep - * Require SCRAM-SHA-1 and SCRAM-SHA-256 to enforce a minimum iteration count - -Version 1.3 Changes - * Updated minimum server version to 2.6 - * Updated the Q & A to recommend support for at most a single credential per MongoClient - * Removed lazy authentication section - * Changed the list of server types requiring authentication - * Made providing username for X509 authentication optional - -Version 1.2 Changes - * Added SCRAM-SHA-1 sasl mechanism - * Added `Connection Handshake`_ - * Changed connection string to support mechanism properties in generic form - * Added example conversations for all mechanisms except GSSAPI - * Miscellaneous wording changes for clarification - -Version 1.1 Changes - * Added MONGODB-X509 - * Added PLAIN sasl mechanism - * Added support for GSSAPI mechanism property gssapiServiceName + This specification has been converted to Markdown and renamed to + `auth.md `_. diff --git a/source/auth/includes/calculating_a_signature.png b/source/auth/includes/calculating_a_signature.png new file mode 100644 index 0000000000..8bfbbd6516 Binary files /dev/null and b/source/auth/includes/calculating_a_signature.png differ diff --git a/source/auth/tests/README.md b/source/auth/tests/README.md new file mode 100644 index 0000000000..c4b3eec74f --- /dev/null +++ b/source/auth/tests/README.md @@ -0,0 +1,47 @@ +# Auth Tests + +## Introduction + +This document describes the format of the driver spec tests included in the JSON and YAML files included in the `legacy` +sub-directory. Tests in the `unified` directory are written using the +[Unified Test Format](../../unified-test-format/unified-test-format.md). + +The YAML and JSON files in the `legacy` directory tree are platform-independent tests that drivers can use to prove +their conformance to the Auth Spec at least with respect to connection string URI input. + +Drivers should do additional unit testing if there are alternate ways of configuring credentials on a client. + +Driver must also conduct the prose tests in the Auth Spec test plan section. + +## Format + +Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which +have the following keys: + +- `description`: A string describing the test. +- `uri`: A string containing the URI to be parsed. +- `valid:` A boolean indicating if the URI should be considered valid. +- `credential`: If null, the credential must not be considered configured for the the purpose of deciding if the driver + should authenticate to the topology. If non-null, it is an object containing one or more of the following properties + of a credential: + - `username`: A string containing the username. For auth mechanisms that do not utilize a password, this may be the + entire `userinfo` token from the connection string. + - `password`: A string containing the password. + - `source`: A string containing the authentication database. + - `mechanism`: A string containing the authentication mechanism. A null value for this key is used to indicate that a + mechanism wasn't specified and that mechanism negotiation is required. Test harnesses should modify the mechanism + test as needed to assert this condition. + - `mechanism_properties`: A document containing mechanism-specific properties. It specifies a subset of properties + that must match. If a key exists in the test data, it must exist with the corresponding value in the credential. + Other values may exist in the credential without failing the test. + +If any key is missing, no assertion about that key is necessary. Except as specified explicitly above, if a key is +present, but the test value is null, the observed value for that key must be uninitialized (whatever that means for a +given driver and data type). + +## Implementation notes + +Testing whether a URI is valid or not should simply be a matter of checking whether URI parsing (or MongoClient +construction) raises an error or exception. + +If a credential is configured, its properties must be compared to the `credential` field. diff --git a/source/auth/tests/connection-string.json b/source/auth/tests/connection-string.json deleted file mode 100644 index 6ae9491d61..0000000000 --- a/source/auth/tests/connection-string.json +++ /dev/null @@ -1,452 +0,0 @@ -{ - "tests": [ - { - "description": "should use the default source and mechanism", - "uri": "mongodb://user:password@localhost", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "admin" - }, - "options": null - }, - { - "description": "should use the database when no authSource is specified", - "uri": "mongodb://user:password@localhost/foo", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "foo" - }, - "options": null - }, - { - "description": "should use the authSource when specified", - "uri": "mongodb://user:password@localhost/foo?authSource=bar", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "bar" - }, - "options": null - }, - { - "description": "should recognise the mechanism (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI" - } - }, - { - "description": "should ignore the database (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/foo?authMechanism=GSSAPI", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI" - } - }, - { - "description": "should accept valid authSource (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=$external", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI" - } - }, - { - "description": "should accept generic mechanism property (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI", - "authmechanismproperties": { - "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true - } - } - }, - { - "description": "should accept the password (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": "password", - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI" - } - }, - { - "description": "may support deprecated gssapiServiceName option (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&gssapiServiceName=other", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user@DOMAIN.COM", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "GSSAPI", - "authmechanismproperties": { - "SERVICE_NAME": "other" - } - } - }, - { - "description": "should throw an exception if authSource is invalid (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=foo", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should throw an exception if no username (GSSAPI)", - "uri": "mongodb://localhost/?authMechanism=GSSAPI", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should recognize the mechanism (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/?authMechanism=MONGODB-CR", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "admin" - }, - "options": { - "authmechanism": "MONGODB-CR" - } - }, - { - "description": "should use the database when no authSource is specified (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "foo" - }, - "options": { - "authmechanism": "MONGODB-CR" - } - }, - { - "description": "should use the authSource when specified (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR&authSource=bar", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "bar" - }, - "options": { - "authmechanism": "MONGODB-CR" - } - }, - { - "description": "should throw an exception if no username is supplied (MONGODB-CR)", - "uri": "mongodb://localhost/?authMechanism=MONGODB-CR", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should recognize the mechanism (MONGODB-X509)", - "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "MONGODB-X509" - } - }, - { - "description": "should ignore the database (MONGODB-X509)", - "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "MONGODB-X509" - } - }, - { - "description": "should accept valid authSource (MONGODB-X509)", - "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509&authSource=$external", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "MONGODB-X509" - } - }, - { - "description": "should recognize the mechanism with no username (MONGODB-X509)", - "uri": "mongodb://localhost/?authMechanism=MONGODB-X509", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": null, - "password": null, - "db": "$external" - }, - "options": { - "authmechanism": "MONGODB-X509" - } - }, - { - "description": "should throw an exception if supplied a password (MONGODB-X509)", - "uri": "mongodb://user:password@localhost/?authMechanism=MONGODB-X509", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should throw an exception if authSource is invalid (MONGODB-X509)", - "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509&authSource=bar", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should recognize the mechanism (PLAIN)", - "uri": "mongodb://user:password@localhost/?authMechanism=PLAIN", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "$external" - }, - "options": { - "authmechanism": "PLAIN" - } - }, - { - "description": "should use the database when no authSource is specified (PLAIN)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=PLAIN", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "foo" - }, - "options": { - "authmechanism": "PLAIN" - } - }, - { - "description": "should use the authSource when specified (PLAIN)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=PLAIN&authSource=bar", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "bar" - }, - "options": { - "authmechanism": "PLAIN" - } - }, - { - "description": "should throw an exception if no username (PLAIN)", - "uri": "mongodb://localhost/?authMechanism=PLAIN", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should recognize the mechanism (SCRAM-SHA-1)", - "uri": "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-1", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "admin" - }, - "options": { - "authmechanism": "SCRAM-SHA-1" - } - }, - { - "description": "should use the database when no authSource is specified (SCRAM-SHA-1)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "foo" - }, - "options": { - "authmechanism": "SCRAM-SHA-1" - } - }, - { - "description": "should accept valid authSource (SCRAM-SHA-1)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1&authSource=bar", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "bar" - }, - "options": { - "authmechanism": "SCRAM-SHA-1" - } - }, - { - "description": "should throw an exception if no username (SCRAM-SHA-1)", - "uri": "mongodb://localhost/?authMechanism=SCRAM-SHA-1", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - }, - { - "description": "should recognize the mechanism (SCRAM-SHA-256)", - "uri": "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "admin" - }, - "options": { - "authmechanism": "SCRAM-SHA-256" - } - }, - { - "description": "should use the database when no authSource is specified (SCRAM-SHA-256)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "foo" - }, - "options": { - "authmechanism": "SCRAM-SHA-256" - } - }, - { - "description": "should accept valid authSource (SCRAM-SHA-256)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256&authSource=bar", - "hosts": null, - "valid": true, - "warning": false, - "auth": { - "username": "user", - "password": "password", - "db": "bar" - }, - "options": { - "authmechanism": "SCRAM-SHA-256" - } - }, - { - "description": "should throw an exception if no username (SCRAM-SHA-256)", - "uri": "mongodb://localhost/?authMechanism=SCRAM-SHA-256", - "hosts": null, - "valid": false, - "warning": false, - "auth": null, - "options": null - } - ] -} diff --git a/source/auth/tests/connection-string.yml b/source/auth/tests/connection-string.yml deleted file mode 100644 index be1dcccd13..0000000000 --- a/source/auth/tests/connection-string.yml +++ /dev/null @@ -1,367 +0,0 @@ -tests: - - - description: "should use the default source and mechanism" - uri: "mongodb://user:password@localhost" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "admin" - options: ~ - - - description: "should use the database when no authSource is specified" - uri: "mongodb://user:password@localhost/foo" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "foo" - options: ~ - - - description: "should use the authSource when specified" - uri: "mongodb://user:password@localhost/foo?authSource=bar" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "bar" - options: ~ - - - description: "should recognise the mechanism (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: ~ - db: "$external" - options: - authmechanism: "GSSAPI" - - - description: "should ignore the database (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/foo?authMechanism=GSSAPI" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: ~ - db: "$external" - options: - authmechanism: "GSSAPI" - - - description: "should accept valid authSource (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=$external" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: ~ - db: "$external" - options: - authmechanism: "GSSAPI" - - - description: "should accept generic mechanism property (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: ~ - db: "$external" - options: - authmechanism: "GSSAPI" - authmechanismproperties: - SERVICE_NAME: "other" - CANONICALIZE_HOST_NAME: true - - - description: "should accept the password (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: "password" - db: "$external" - options: - authmechanism: "GSSAPI" - - - description: "may support deprecated gssapiServiceName option (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&gssapiServiceName=other" - hosts: ~ - valid: true - warning: false - auth: - username: "user@DOMAIN.COM" - password: ~ - db: "$external" - options: - authmechanism: "GSSAPI" - authmechanismproperties: - SERVICE_NAME: "other" - - - description: "should throw an exception if authSource is invalid (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=foo" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should throw an exception if no username (GSSAPI)" - uri: "mongodb://localhost/?authMechanism=GSSAPI" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should recognize the mechanism (MONGODB-CR)" - uri: "mongodb://user:password@localhost/?authMechanism=MONGODB-CR" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "admin" - options: - authmechanism: "MONGODB-CR" - - - description: "should use the database when no authSource is specified (MONGODB-CR)" - uri: "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "foo" - options: - authmechanism: "MONGODB-CR" - - - description: "should use the authSource when specified (MONGODB-CR)" - uri: "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR&authSource=bar" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "bar" - options: - authmechanism: "MONGODB-CR" - - - description: "should throw an exception if no username is supplied (MONGODB-CR)" - uri: "mongodb://localhost/?authMechanism=MONGODB-CR" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should recognize the mechanism (MONGODB-X509)" - uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509" - hosts: ~ - valid: true - warning: false - auth: - username: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" - password: ~ - db: "$external" - options: - authmechanism: MONGODB-X509 - - - description: "should ignore the database (MONGODB-X509)" - uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509" - hosts: ~ - valid: true - warning: false - auth: - username: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" - password: ~ - db: "$external" - options: - authmechanism: MONGODB-X509 - - - description: "should accept valid authSource (MONGODB-X509)" - uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509&authSource=$external" - hosts: ~ - valid: true - warning: false - auth: - username: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" - password: ~ - db: "$external" - options: - authmechanism: MONGODB-X509 - - - description: "should recognize the mechanism with no username (MONGODB-X509)" - uri: "mongodb://localhost/?authMechanism=MONGODB-X509" - hosts: ~ - valid: true - warning: false - auth: - username: ~ - password: ~ - db: "$external" - options: - authmechanism: "MONGODB-X509" - - - description: "should throw an exception if supplied a password (MONGODB-X509)" - uri: "mongodb://user:password@localhost/?authMechanism=MONGODB-X509" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should throw an exception if authSource is invalid (MONGODB-X509)" - uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509&authSource=bar" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should recognize the mechanism (PLAIN)" - uri: "mongodb://user:password@localhost/?authMechanism=PLAIN" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "$external" - options: - authmechanism: "PLAIN" - - - description: "should use the database when no authSource is specified (PLAIN)" - uri: "mongodb://user:password@localhost/foo?authMechanism=PLAIN" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "foo" - options: - authmechanism: "PLAIN" - - - description: "should use the authSource when specified (PLAIN)" - uri: "mongodb://user:password@localhost/foo?authMechanism=PLAIN&authSource=bar" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "bar" - options: - authmechanism: "PLAIN" - - - description: "should throw an exception if no username (PLAIN)" - uri: "mongodb://localhost/?authMechanism=PLAIN" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should recognize the mechanism (SCRAM-SHA-1)" - uri: "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-1" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "admin" - options: - authmechanism: "SCRAM-SHA-1" - - - description: "should use the database when no authSource is specified (SCRAM-SHA-1)" - uri: "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "foo" - options: - authmechanism: "SCRAM-SHA-1" - - - description: "should accept valid authSource (SCRAM-SHA-1)" - uri: "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1&authSource=bar" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "bar" - options: - authmechanism: "SCRAM-SHA-1" - - - description: "should throw an exception if no username (SCRAM-SHA-1)" - uri: "mongodb://localhost/?authMechanism=SCRAM-SHA-1" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ - - - description: "should recognize the mechanism (SCRAM-SHA-256)" - uri: "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "admin" - options: - authmechanism: "SCRAM-SHA-256" - - - description: "should use the database when no authSource is specified (SCRAM-SHA-256)" - uri: "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "foo" - options: - authmechanism: "SCRAM-SHA-256" - - - description: "should accept valid authSource (SCRAM-SHA-256)" - uri: "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256&authSource=bar" - hosts: ~ - valid: true - warning: false - auth: - username: "user" - password: "password" - db: "bar" - options: - authmechanism: "SCRAM-SHA-256" - - - description: "should throw an exception if no username (SCRAM-SHA-256)" - uri: "mongodb://localhost/?authMechanism=SCRAM-SHA-256" - hosts: ~ - valid: false - warning: false - auth: ~ - options: ~ \ No newline at end of file diff --git a/source/auth/tests/legacy/connection-string.json b/source/auth/tests/legacy/connection-string.json new file mode 100644 index 0000000000..8982b61d5a --- /dev/null +++ b/source/auth/tests/legacy/connection-string.json @@ -0,0 +1,666 @@ +{ + "tests": [ + { + "description": "should use the default source and mechanism", + "uri": "mongodb://user:password@localhost", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "admin", + "mechanism": null, + "mechanism_properties": null + } + }, + { + "description": "should use the database when no authSource is specified", + "uri": "mongodb://user:password@localhost/foo", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "foo", + "mechanism": null, + "mechanism_properties": null + } + }, + { + "description": "should use the authSource when specified", + "uri": "mongodb://user:password@localhost/foo?authSource=bar", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "bar", + "mechanism": null, + "mechanism_properties": null + } + }, + { + "description": "should recognise the mechanism (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "mongodb" + } + } + }, + { + "description": "should ignore the database (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/foo?authMechanism=GSSAPI", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "mongodb" + } + } + }, + { + "description": "should accept valid authSource (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=$external", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "mongodb" + } + } + }, + { + "description": "should accept generic mechanism property (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "other", + "SERVICE_HOST": "example.com", + "CANONICALIZE_HOST_NAME": "forward" + } + } + }, + { + "description": "should accept forwardAndReverse hostname canonicalization (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forwardAndReverse", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "other", + "CANONICALIZE_HOST_NAME": "forwardAndReverse" + } + } + }, + { + "description": "should accept no hostname canonicalization (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:none", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": null, + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "other", + "CANONICALIZE_HOST_NAME": "none" + } + } + }, + { + "description": "must raise an error when the hostname canonicalization is invalid", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:invalid", + "valid": false + }, + { + "description": "should accept the password (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external", + "valid": true, + "credential": { + "username": "user@DOMAIN.COM", + "password": "password", + "source": "$external", + "mechanism": "GSSAPI", + "mechanism_properties": { + "SERVICE_NAME": "mongodb" + } + } + }, + { + "description": "must raise an error when the authSource is empty", + "uri": "mongodb://user:password@localhost/foo?authSource=", + "valid": false + }, + { + "description": "must raise an error when the authSource is empty without credentials", + "uri": "mongodb://localhost/admin?authSource=", + "valid": false + }, + { + "description": "should throw an exception if authSource is invalid (GSSAPI)", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=foo", + "valid": false + }, + { + "description": "should throw an exception if no username (GSSAPI)", + "uri": "mongodb://localhost/?authMechanism=GSSAPI", + "valid": false + }, + { + "description": "should recognize the mechanism (MONGODB-X509)", + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", + "valid": true, + "credential": { + "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", + "password": null, + "source": "$external", + "mechanism": "MONGODB-X509", + "mechanism_properties": null + } + }, + { + "description": "should ignore the database (MONGODB-X509)", + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509", + "valid": true, + "credential": { + "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", + "password": null, + "source": "$external", + "mechanism": "MONGODB-X509", + "mechanism_properties": null + } + }, + { + "description": "should accept valid authSource (MONGODB-X509)", + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509&authSource=$external", + "valid": true, + "credential": { + "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", + "password": null, + "source": "$external", + "mechanism": "MONGODB-X509", + "mechanism_properties": null + } + }, + { + "description": "should recognize the mechanism with no username (MONGODB-X509)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-X509", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-X509", + "mechanism_properties": null + } + }, + { + "description": "should recognize the mechanism with no username when auth source is explicitly specified (MONGODB-X509)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-X509&authSource=$external", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-X509", + "mechanism_properties": null + } + }, + { + "description": "should throw an exception if supplied a password (MONGODB-X509)", + "uri": "mongodb://user:password@localhost/?authMechanism=MONGODB-X509", + "valid": false + }, + { + "description": "should throw an exception if authSource is invalid (MONGODB-X509)", + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509&authSource=bar", + "valid": false + }, + { + "description": "should recognize the mechanism (PLAIN)", + "uri": "mongodb://user:password@localhost/?authMechanism=PLAIN", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "$external", + "mechanism": "PLAIN", + "mechanism_properties": null + } + }, + { + "description": "should use the database when no authSource is specified (PLAIN)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=PLAIN", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "foo", + "mechanism": "PLAIN", + "mechanism_properties": null + } + }, + { + "description": "should use the authSource when specified (PLAIN)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=PLAIN&authSource=bar", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "bar", + "mechanism": "PLAIN", + "mechanism_properties": null + } + }, + { + "description": "should throw an exception if no username (PLAIN)", + "uri": "mongodb://localhost/?authMechanism=PLAIN", + "valid": false + }, + { + "description": "should recognize the mechanism (SCRAM-SHA-1)", + "uri": "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-1", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "admin", + "mechanism": "SCRAM-SHA-1", + "mechanism_properties": null + } + }, + { + "description": "should use the database when no authSource is specified (SCRAM-SHA-1)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "foo", + "mechanism": "SCRAM-SHA-1", + "mechanism_properties": null + } + }, + { + "description": "should accept valid authSource (SCRAM-SHA-1)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1&authSource=bar", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "bar", + "mechanism": "SCRAM-SHA-1", + "mechanism_properties": null + } + }, + { + "description": "should throw an exception if no username (SCRAM-SHA-1)", + "uri": "mongodb://localhost/?authMechanism=SCRAM-SHA-1", + "valid": false + }, + { + "description": "should recognize the mechanism (SCRAM-SHA-256)", + "uri": "mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "admin", + "mechanism": "SCRAM-SHA-256", + "mechanism_properties": null + } + }, + { + "description": "should use the database when no authSource is specified (SCRAM-SHA-256)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "foo", + "mechanism": "SCRAM-SHA-256", + "mechanism_properties": null + } + }, + { + "description": "should accept valid authSource (SCRAM-SHA-256)", + "uri": "mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256&authSource=bar", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "bar", + "mechanism": "SCRAM-SHA-256", + "mechanism_properties": null + } + }, + { + "description": "should throw an exception if no username (SCRAM-SHA-256)", + "uri": "mongodb://localhost/?authMechanism=SCRAM-SHA-256", + "valid": false + }, + { + "description": "URI with no auth-related info doesn't create credential", + "uri": "mongodb://localhost/", + "valid": true, + "credential": null + }, + { + "description": "database in URI path doesn't create credentials", + "uri": "mongodb://localhost/foo", + "valid": true, + "credential": null + }, + { + "description": "authSource without username doesn't create credential (default mechanism)", + "uri": "mongodb://localhost/?authSource=foo", + "valid": true, + "credential": null + }, + { + "description": "should throw an exception if no username provided (userinfo implies default mechanism)", + "uri": "mongodb://@localhost.com/", + "valid": false + }, + { + "description": "should throw an exception if no username/password provided (userinfo implies default mechanism)", + "uri": "mongodb://:@localhost.com/", + "valid": false + }, + { + "description": "should recognise the mechanism (MONGODB-AWS)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-AWS", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-AWS", + "mechanism_properties": null + } + }, + { + "description": "should recognise the mechanism when auth source is explicitly specified (MONGODB-AWS)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-AWS&authSource=$external", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-AWS", + "mechanism_properties": null + } + }, + { + "description": "should throw an exception if username and no password (MONGODB-AWS)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-AWS", + "valid": false, + "credential": null + }, + { + "description": "should use username and password if specified (MONGODB-AWS)", + "uri": "mongodb://user%21%40%23%24%25%5E%26%2A%28%29_%2B:pass%21%40%23%24%25%5E%26%2A%28%29_%2B@localhost/?authMechanism=MONGODB-AWS", + "valid": true, + "credential": { + "username": "user!@#$%^&*()_+", + "password": "pass!@#$%^&*()_+", + "source": "$external", + "mechanism": "MONGODB-AWS", + "mechanism_properties": null + } + }, + { + "description": "should use username, password and session token if specified (MONGODB-AWS)", + "uri": "mongodb://user:password@localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token%21%40%23%24%25%5E%26%2A%28%29_%2B", + "valid": true, + "credential": { + "username": "user", + "password": "password", + "source": "$external", + "mechanism": "MONGODB-AWS", + "mechanism_properties": { + "AWS_SESSION_TOKEN": "token!@#$%^&*()_+" + } + } + }, + { + "description": "should throw an exception if username provided (MONGODB-AWS)", + "uri": "mongodb://user@localhost.com/?authMechanism=MONGODB-AWS", + "valid": false + }, + { + "description": "should throw an exception if username and password provided (MONGODB-AWS)", + "uri": "mongodb://user:pass@localhost.com/?authMechanism=MONGODB-AWS", + "valid": false + }, + { + "description": "should throw an exception if AWS_SESSION_TOKEN provided (MONGODB-AWS)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token", + "valid": false + }, + { + "description": "should recognise the mechanism with test environment (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "test" + } + } + }, + { + "description": "should recognise the mechanism when auth source is explicitly specified and with environment (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=ENVIRONMENT:test", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "test" + } + } + }, + { + "description": "should throw an exception if supplied a password (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if username is specified for test (MONGODB-OIDC)", + "uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if specified environment is not supported (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:invalid", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if neither environment nor callbacks specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception when unsupported auth property is specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=UnsupportedProperty:unexisted", + "valid": false, + "credential": null + }, + { + "description": "should recognise the mechanism with azure provider (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "foo" + } + } + }, + { + "description": "should accept a username with azure provider (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo", + "valid": true, + "credential": { + "username": "user", + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "foo" + } + } + }, + { + "description": "should accept a url-encoded TOKEN_RESOURCE (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster", + "valid": true, + "credential": { + "username": "user", + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "mongodb://test-cluster" + } + } + }, + { + "description": "should accept an un-encoded TOKEN_RESOURCE (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb://test-cluster", + "valid": true, + "credential": { + "username": "user", + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "mongodb://test-cluster" + } + } + }, + { + "description": "should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abcd%25ef%3Ag%26hi", + "valid": true, + "credential": { + "username": "user", + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "abcd%ef:g&hi" + } + } + }, + { + "description": "should url-encode a TOKEN_RESOURCE (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:a$b", + "valid": true, + "credential": { + "username": "user", + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "a$b" + } + } + }, + { + "description": "should accept a username and throw an error for a password with azure provider (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if no token audience is given for azure provider (MONGODB-OIDC)", + "uri": "mongodb://username@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure", + "valid": false, + "credential": null + }, + { + "description": "should recognise the mechanism with gcp provider (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:foo", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "gcp", + "TOKEN_RESOURCE": "foo" + } + } + }, + { + "description": "should throw an error for a username and password with gcp provider (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:foo", + "valid": false, + "credential": null + }, + { + "description": "should throw an error if not TOKEN_RESOURCE with gcp provider (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp", + "valid": false, + "credential": null + }, + { + "description": "should recognise the mechanism with k8s provider (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "ENVIRONMENT": "k8s" + } + } + }, + { + "description": "should throw an error for a username and password with k8s provider (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s", + "valid": false, + "credential": null + } + ] +} diff --git a/source/auth/tests/legacy/connection-string.yml b/source/auth/tests/legacy/connection-string.yml new file mode 100644 index 0000000000..2b98f0f8f2 --- /dev/null +++ b/source/auth/tests/legacy/connection-string.yml @@ -0,0 +1,480 @@ +--- +tests: +- description: should use the default source and mechanism + uri: mongodb://user:password@localhost + valid: true + credential: + username: user + password: password + source: admin + mechanism: + mechanism_properties: +- description: should use the database when no authSource is specified + uri: mongodb://user:password@localhost/foo + valid: true + credential: + username: user + password: password + source: foo + mechanism: + mechanism_properties: +- description: should use the authSource when specified + uri: mongodb://user:password@localhost/foo?authSource=bar + valid: true + credential: + username: user + password: password + source: bar + mechanism: + mechanism_properties: +- description: should recognise the mechanism (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: mongodb +- description: should ignore the database (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/foo?authMechanism=GSSAPI + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: mongodb +- description: should accept valid authSource (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=$external + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: mongodb +- description: should accept generic mechanism property (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: other + SERVICE_HOST: example.com + CANONICALIZE_HOST_NAME: forward +- description: should accept forwardAndReverse hostname canonicalization (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forwardAndReverse + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: other + CANONICALIZE_HOST_NAME: forwardAndReverse +- description: should accept no hostname canonicalization (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:none + valid: true + credential: + username: user@DOMAIN.COM + password: + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: other + CANONICALIZE_HOST_NAME: none +- description: must raise an error when the hostname canonicalization is invalid + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:invalid + valid: false +- description: should accept the password (GSSAPI) + uri: mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external + valid: true + credential: + username: user@DOMAIN.COM + password: password + source: "$external" + mechanism: GSSAPI + mechanism_properties: + SERVICE_NAME: mongodb +- description: must raise an error when the authSource is empty + uri: mongodb://user:password@localhost/foo?authSource= + valid: false +- description: must raise an error when the authSource is empty without credentials + uri: mongodb://localhost/admin?authSource= + valid: false +- description: should throw an exception if authSource is invalid (GSSAPI) + uri: mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authSource=foo + valid: false +- description: should throw an exception if no username (GSSAPI) + uri: mongodb://localhost/?authMechanism=GSSAPI + valid: false +- description: should recognize the mechanism (MONGODB-X509) + uri: mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509 + valid: true + credential: + username: CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry + password: + source: "$external" + mechanism: MONGODB-X509 + mechanism_properties: +- description: should ignore the database (MONGODB-X509) + uri: mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509 + valid: true + credential: + username: CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry + password: + source: "$external" + mechanism: MONGODB-X509 + mechanism_properties: +- description: should accept valid authSource (MONGODB-X509) + uri: mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509&authSource=$external + valid: true + credential: + username: CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry + password: + source: "$external" + mechanism: MONGODB-X509 + mechanism_properties: +- description: should recognize the mechanism with no username (MONGODB-X509) + uri: mongodb://localhost/?authMechanism=MONGODB-X509 + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-X509 + mechanism_properties: +- description: should recognize the mechanism with no username when auth source is + explicitly specified (MONGODB-X509) + uri: mongodb://localhost/?authMechanism=MONGODB-X509&authSource=$external + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-X509 + mechanism_properties: +- description: should throw an exception if supplied a password (MONGODB-X509) + uri: mongodb://user:password@localhost/?authMechanism=MONGODB-X509 + valid: false +- description: should throw an exception if authSource is invalid (MONGODB-X509) + uri: mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/foo?authMechanism=MONGODB-X509&authSource=bar + valid: false +- description: should recognize the mechanism (PLAIN) + uri: mongodb://user:password@localhost/?authMechanism=PLAIN + valid: true + credential: + username: user + password: password + source: "$external" + mechanism: PLAIN + mechanism_properties: +- description: should use the database when no authSource is specified (PLAIN) + uri: mongodb://user:password@localhost/foo?authMechanism=PLAIN + valid: true + credential: + username: user + password: password + source: foo + mechanism: PLAIN + mechanism_properties: +- description: should use the authSource when specified (PLAIN) + uri: mongodb://user:password@localhost/foo?authMechanism=PLAIN&authSource=bar + valid: true + credential: + username: user + password: password + source: bar + mechanism: PLAIN + mechanism_properties: +- description: should throw an exception if no username (PLAIN) + uri: mongodb://localhost/?authMechanism=PLAIN + valid: false +- description: should recognize the mechanism (SCRAM-SHA-1) + uri: mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-1 + valid: true + credential: + username: user + password: password + source: admin + mechanism: SCRAM-SHA-1 + mechanism_properties: +- description: should use the database when no authSource is specified (SCRAM-SHA-1) + uri: mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1 + valid: true + credential: + username: user + password: password + source: foo + mechanism: SCRAM-SHA-1 + mechanism_properties: +- description: should accept valid authSource (SCRAM-SHA-1) + uri: mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-1&authSource=bar + valid: true + credential: + username: user + password: password + source: bar + mechanism: SCRAM-SHA-1 + mechanism_properties: +- description: should throw an exception if no username (SCRAM-SHA-1) + uri: mongodb://localhost/?authMechanism=SCRAM-SHA-1 + valid: false +- description: should recognize the mechanism (SCRAM-SHA-256) + uri: mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256 + valid: true + credential: + username: user + password: password + source: admin + mechanism: SCRAM-SHA-256 + mechanism_properties: +- description: should use the database when no authSource is specified (SCRAM-SHA-256) + uri: mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256 + valid: true + credential: + username: user + password: password + source: foo + mechanism: SCRAM-SHA-256 + mechanism_properties: +- description: should accept valid authSource (SCRAM-SHA-256) + uri: mongodb://user:password@localhost/foo?authMechanism=SCRAM-SHA-256&authSource=bar + valid: true + credential: + username: user + password: password + source: bar + mechanism: SCRAM-SHA-256 + mechanism_properties: +- description: should throw an exception if no username (SCRAM-SHA-256) + uri: mongodb://localhost/?authMechanism=SCRAM-SHA-256 + valid: false +- description: URI with no auth-related info doesn't create credential + uri: mongodb://localhost/ + valid: true + credential: +- description: database in URI path doesn't create credentials + uri: mongodb://localhost/foo + valid: true + credential: +- description: authSource without username doesn't create credential (default mechanism) + uri: mongodb://localhost/?authSource=foo + valid: true + credential: +- description: should throw an exception if no username provided (userinfo implies + default mechanism) + uri: mongodb://@localhost.com/ + valid: false +- description: should throw an exception if no username/password provided (userinfo + implies default mechanism) + uri: mongodb://:@localhost.com/ + valid: false +- description: should recognise the mechanism (MONGODB-AWS) + uri: mongodb://localhost/?authMechanism=MONGODB-AWS + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-AWS + mechanism_properties: +- description: should recognise the mechanism when auth source is explicitly specified + (MONGODB-AWS) + uri: mongodb://localhost/?authMechanism=MONGODB-AWS&authSource=$external + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-AWS + mechanism_properties: +- description: should throw an exception if username and no password (MONGODB-AWS) + uri: mongodb://user@localhost/?authMechanism=MONGODB-AWS + valid: false + credential: +- description: should use username and password if specified (MONGODB-AWS) + uri: mongodb://user%21%40%23%24%25%5E%26%2A%28%29_%2B:pass%21%40%23%24%25%5E%26%2A%28%29_%2B@localhost/?authMechanism=MONGODB-AWS + valid: true + credential: + username: user!@#$%^&*()_+ + password: pass!@#$%^&*()_+ + source: "$external" + mechanism: MONGODB-AWS + mechanism_properties: +- description: should use username, password and session token if specified (MONGODB-AWS) + uri: mongodb://user:password@localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token%21%40%23%24%25%5E%26%2A%28%29_%2B + valid: true + credential: + username: user + password: password + source: "$external" + mechanism: MONGODB-AWS + mechanism_properties: + AWS_SESSION_TOKEN: token!@#$%^&*()_+ +- description: should throw an exception if username provided (MONGODB-AWS) + uri: mongodb://user@localhost.com/?authMechanism=MONGODB-AWS + valid: false +- description: should throw an exception if username and password provided (MONGODB-AWS) + uri: mongodb://user:pass@localhost.com/?authMechanism=MONGODB-AWS + valid: false +- description: should throw an exception if AWS_SESSION_TOKEN provided (MONGODB-AWS) + uri: mongodb://localhost/?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:token + valid: false +- description: should recognise the mechanism with test environment (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: test +- description: should recognise the mechanism when auth source is explicitly specified and with environment (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=ENVIRONMENT:test + valid: true + credential: + username: + password: + source: "$external" + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: test +- description: should throw an exception if supplied a password (MONGODB-OIDC) + uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test + valid: false + credential: +- description: should throw an exception if username is specified for test (MONGODB-OIDC) + uri: mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test + valid: false + credential: +- description: should throw an exception if specified environment is not supported (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:invalid + valid: false + credential: +- description: should throw an exception if neither environment nor callbacks specified (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC + valid: false + credential: +- description: should throw an exception when unsupported auth property is specified (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=UnsupportedProperty:unexisted + valid: false + credential: +- description: should recognise the mechanism with azure provider (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo + valid: true + credential: + username: null + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: foo +- description: should accept a username with azure provider (MONGODB-OIDC) + uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo + valid: true + credential: + username: user + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: foo +- description: should accept a url-encoded TOKEN_RESOURCE (MONGODB-OIDC) + uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster + valid: true + credential: + username: user + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: 'mongodb://test-cluster' +- description: should accept an un-encoded TOKEN_RESOURCE (MONGODB-OIDC) + uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb://test-cluster + valid: true + credential: + username: user + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: 'mongodb://test-cluster' +- description: should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC) + uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abcd%25ef%3Ag%26hi + valid: true + credential: + username: user + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: 'abcd%ef:g&hi' +- description: should url-encode a TOKEN_RESOURCE (MONGODB-OIDC) + uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:a$b + valid: true + credential: + username: user + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: azure + TOKEN_RESOURCE: a$b +- description: should accept a username and throw an error for a password with azure provider (MONGODB-OIDC) + uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo + valid: false + credential: null +- description: should throw an exception if no token audience is given for azure provider (MONGODB-OIDC) + uri: mongodb://username@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure + valid: false + credential: null +- description: should recognise the mechanism with gcp provider (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:foo + valid: true + credential: + username: null + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: gcp + TOKEN_RESOURCE: foo +- description: should throw an error for a username and password with gcp provider + (MONGODB-OIDC) + uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:foo + valid: false + credential: null +- description: should throw an error if not TOKEN_RESOURCE with gcp provider (MONGODB-OIDC) + uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp + valid: false + credential: null +- description: should recognise the mechanism with k8s provider (MONGODB-OIDC) + uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s + valid: true + credential: + username: null + password: null + source: $external + mechanism: MONGODB-OIDC + mechanism_properties: + ENVIRONMENT: k8s +- description: should throw an error for a username and password with k8s provider + (MONGODB-OIDC) + uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s + valid: false + credential: null diff --git a/source/auth/tests/mongodb-aws.md b/source/auth/tests/mongodb-aws.md new file mode 100644 index 0000000000..74faa3ad85 --- /dev/null +++ b/source/auth/tests/mongodb-aws.md @@ -0,0 +1,197 @@ +# MongoDB AWS + +Drivers MUST test the following scenarios: + +1. `Regular Credentials`: Auth via an `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` pair +2. `EC2 Credentials`: Auth from an EC2 instance via temporary credentials assigned to the machine +3. `ECS Credentials`: Auth from an ECS instance via temporary credentials assigned to the task +4. `Assume Role`: Auth via temporary credentials obtained from an STS AssumeRole request +5. `Assume Role with Web Identity`: Auth via temporary credentials obtained from an STS AssumeRoleWithWebIdentity + request +6. `AWS Lambda`: Auth via environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`. +7. Caching of AWS credentials fetched by the driver. + +For brevity, this section gives the values ``, `` and `` in place of a valid access +key ID, secret access key and session token (also known as a security token). Sample values are below. + +```text +AccessKeyId=AKIAI44QH8DHBEXAMPLE +SecretAccessKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +Token=AQoDYXdzEJr... +``` + +## Testing custom credential providers + +If the driver supports custom AWS credential providers, the driver MUST test the following: + +### 1. Custom Credential Provider Authenticates + +Scenarios 1-6 from the previous section with a user provided `AWS_CREDENTIAL_PROVIDER` auth mechanism property. This +credentials MAY be obtained from the default credential provider from the AWS SDK. If the default provider does not +cover all scenarios above, those not covered MAY be skipped. In these tests the driver MUST also assert that the user +provided credential provider was called in each test. This may be via a custom function or object that wraps the calls +to the custom provider and asserts that it was called at least once. For test scenarios where the drivers tools scripts +put the credentials in the MONGODB_URI, drivers MAY extract the credentials from the URI and return the AWS credentials +directly from the custom provider instead of using the AWS SDK default provider. + +### 2. Custom Credential Provider Authentication Precedence + +#### Case 1: Credentials in URI Take Precedence *Removed* + +#### Case 2: Custom Provider Takes Precedence Over Environment Variables + +Run this test in an environment with AWS credentials configured as environment variables (e.g. `AWS_ACCESS_KEY_ID`, +`AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`) + +Create a `MongoClient` configured to use AWS auth. Example: `mongodb://localhost:27017/?authMechanism=MONGODB-AWS`. + +Configure a custom credential provider to pass valid AWS credentials. The provider must track if it was called. + +Expect authentication to succeed and the custom credential provider was called. + +## Regular credentials + +Drivers MUST be able to authenticate when a valid access key id and secret access key pair are present in the +environment. Drivers MUST provide the --nouri option to aws_tester.py in drivers-evergreen-tools for this test. + +```text +mongodb://localhost/?authMechanism=MONGODB-AWS +``` + +## EC2 Credentials + +Drivers MUST be able to authenticate from an EC2 instance via temporary credentials assigned to the machine. A sample +URI on an EC2 machine would be: + +```text +mongodb://localhost/?authMechanism=MONGODB-AWS +``` + +> [!NOTE] +> No username, password or session token is passed into the URI. Drivers MUST query the EC2 instance endpoint to obtain +> these credentials. + +## ECS instance + +Drivers MUST be able to authenticate from an ECS container via temporary credentials. A sample URI in an ECS container +would be: + +```text +mongodb://localhost/?authMechanism=MONGODB-AWS +``` + +> [!NOTE] +> No username, password or session token is passed into the URI. Drivers MUST query the ECS container endpoint to obtain +> these credentials. + +## AssumeRole + +Drivers MUST be able to authenticate using temporary credentials returned from an assume role request. These temporary +credentials consist of an access key ID, a secret access key, and a security token present in the environment. Drivers +MUST provide the --nouri option to aws_tester.py in drivers-evergreen-tools for this test. A sample URI would be: + +```text +mongodb://localhost/?authMechanism=MONGODB-AWS +``` + +## Assume Role with Web Identity + +Drivers MUST be able to authentiate using a valid OIDC token and associated role ARN taken from environment variables, +respectively: + +```text +AWS_WEB_IDENTITY_TOKEN_FILE +AWS_ROLE_ARN +AWS_ROLE_SESSION_NAME (optional) +``` + +A sample URI in for a web identity test would be: + +```text +mongodb://localhost/?authMechanism=MONGODB-AWS +``` + +Drivers MUST test with and without AWS_ROLE_SESSION_NAME set. + +> [!NOTE] +> No username, password or session token is passed into the URI. + +Drivers MUST check the environment variables listed above and make an +[AssumeRoleWithWebIdentity request](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) +to obtain credentials. + +## AWS Lambda + +Drivers MUST be able to authenticate via an access key ID, secret access key and optional session token taken from the +environment variables, respectively: + +```text +AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY +AWS_SESSION_TOKEN +``` + +Sample URIs both with and without optional session tokens set are shown below. Drivers MUST test both cases. + +```bash +# without a session token +export AWS_ACCESS_KEY_ID="" +export AWS_SECRET_ACCESS_KEY="" + +URI="mongodb://localhost/?authMechanism=MONGODB-AWS" +``` + +```bash +# with a session token +export AWS_ACCESS_KEY_ID="" +export AWS_SECRET_ACCESS_KEY="" +export AWS_SESSION_TOKEN="" + +URI="mongodb://localhost/?authMechanism=MONGODB-AWS" +``` + +> [!NOTE] +> No username, password or session token is passed into the URI. Drivers MUST check the environment variables listed +> above for these values. If the session token is set Drivers MUST use it. + +## Cached Credentials + +Drivers MUST ensure that they are testing the ability to cache credentials. Drivers will need to be able to query and +override the cached credentials to verify usage. To determine whether to run the cache tests, the driver can check for +the absence of the AWS_ACCESS_KEY_ID environment variable and of credentials in the URI. + +1. Clear the cache. +2. Create a new client. +3. Ensure that a `find` operation adds credentials to the cache. +4. Override the cached credentials with an "Expiration" that is within one minute of the current UTC time. +5. Create a new client. +6. Ensure that a `find` operation updates the credentials in the cache. +7. Poison the cache with an invalid access key id. +8. Create a new client. +9. Ensure that a `find` operation results in an error. +10. Ensure that the cache has been cleared. +11. Ensure that a subsequent `find` operation succeeds. +12. Ensure that the cache has been set. + +If the drivers's language supports dynamically setting environment variables, add the following tests. Note that if +integration tests are run in parallel for the driver, then these tests must be run as unit tests interacting with the +auth provider directly instead of using a client. + +1. Clear the cache. +2. Create a new client. +3. Ensure that a `find` operation adds credentials to the cache. +4. Set the AWS environment variables based on the cached credentials. +5. Clear the cache. +6. Create a new client. +7. Ensure that a `find` operation succeeds and does not add credentials to the cache. +8. Set the AWS environment variables to invalid values. +9. Create a new client. +10. Ensure that a `find` operation results in an error. +11. Clear the AWS environment variables. +12. Clear the cache. +13. Create a new client. +14. Ensure that a `find` operation adds credentials to the cache. +15. Set the AWS environment variables to invalid values. +16. Create a new client. +17. Ensure that a `find` operation succeeds. +18. Clear the AWS environment variables. diff --git a/source/auth/tests/mongodb-oidc.md b/source/auth/tests/mongodb-oidc.md new file mode 100644 index 0000000000..affb3e6efa --- /dev/null +++ b/source/auth/tests/mongodb-oidc.md @@ -0,0 +1,593 @@ +# MongoDB OIDC + +## Local Testing + +See the detailed instructions in +[drivers-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md) +for how to set up your environment for OIDC testing. + +______________________________________________________________________ + +## Unified Spec Tests + +Drivers MUST run the unified spec tests in all supported OIDC environments. Drivers MUST set the placeholder +authMechanism properties (`ENVIRONMENT` and `TOKEN_RESOURCE`, if applicable). These will typically be read from +environment variables set by the test runner, e,g. `AZUREOIDC_RESOURCE`. + +______________________________________________________________________ + +## Machine Authentication Flow Prose Tests + +Drivers MUST run these tests in all supported OIDC environments: + +- A callback that reads the token file for `ENVIRONMENT:test`. A callback enables testing additional behaviors. Tests + and assertions limited to a callback are noted with `[callback-only]`. +- `ENVIRONMENT:test` +- `ENVIRONMENT:gcp` +- `ENVIRONMENT:azure` +- `ENVIRONMENT:k8s` + +The token file `ENVIRONMENT:test` is located in `OIDC_TOKEN_DIR` set by +[drivers-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md) +scripts. + +Drivers MUST implement all prose tests in this section. Unless otherwise noted, all `MongoClient` instances MUST be +configured with `retryReads=false`. + +> [!NOTE] +> For test cases that create fail points, drivers MUST use a unique `appName` to prevent interaction with other +> environment processes, and explicitly remove the fail point to prevent interaction between test runs. + +After setting up your OIDC +[environment](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md), +source the `secrets-export.sh` file and use the associated env variables in your tests. + +### Callback Authentication + +**1.1 Callback is called during authentication** + +- Create an OIDC configured client. +- Perform a `find` operation that succeeds. +- `[callback-only]` Assert that the callback was called 1 time. +- Close the client. + +**1.2 Callback is called once for multiple connections** + +- Create an OIDC configured client. +- Start 10 threads and run 100 `find` operations in each thread that all succeed. +- `[callback-only]` Assert that the callback was called 1 time. +- Close the client. + +### (2) `[callback-only]` OIDC Callback Validation + +**2.1 Valid Callback Inputs** + +- Create an OIDC configured client with an OIDC callback that validates its inputs and returns a valid access token. +- Perform a `find` operation that succeeds. +- Assert that the OIDC callback was called with the appropriate inputs, including the timeout parameter if possible. +- Close the client. + +**2.2 OIDC Callback Returns Null** + +- Create an OIDC configured client with an OIDC callback that returns `null`. +- Perform a `find` operation that fails. +- Close the client. + +**2.3 OIDC Callback Returns Missing Data** + +- Create an OIDC configured client with an OIDC callback that returns data not conforming to the `OIDCCredential` with + missing fields. +- Perform a `find` operation that fails. +- Close the client. + +**2.4 Invalid Client Configuration with Callback** + +- Create an OIDC configured client with an OIDC callback and auth mechanism property `ENVIRONMENT:test`. +- Assert it returns a client configuration error upon client creation, or client connect if your driver validates on + connection. + +**2.5 Invalid use of ALLOWED_HOSTS** + +- Create an OIDC configured client with auth mechanism properties `{"ENVIRONMENT": "azure", "ALLOWED_HOSTS": []}`. +- Assert it returns a client configuration error upon client creation, or client connect if your driver validates on + connection. + +### (3) Authentication Failure + +**3.1 Authentication failure with cached tokens fetch a new token and retry auth** + +- Create an OIDC configured client. +- Poison the *Client Cache* with an invalid access token. +- Perform a `find` operation that succeeds. +- `[callback-only]` Assert that the callback was called 1 time. +- Close the client. + +**3.2 `[callback-only]` Authentication failures without cached tokens return an error** + +- Create an OIDC configured client with an OIDC callback that always returns invalid access tokens. +- Perform a `find` operation that fails. +- Assert that the callback was called 1 time. +- Close the client. + +**3.3 Unexpected error code does not clear the cache** + +- Create an OIDC configured client. +- Set a fail point for `saslStart` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "saslStart" + ], + errorCode: 20 // IllegalOperation + } +} +``` + +- Perform a `find` operation that fails. +- `[callback-only]` Assert that the callback has been called once. +- Perform a `find` operation that succeeds. +- `[callback-only]` Assert that the callback has been called once. +- Close the client. + +### (4) Reauthentication + +#### 4.1 Reauthentication Succeeds + +- Create an OIDC configured client. +- Set a fail point for `find` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a `find` operation that succeeds. +- `[callback-only]` Assert that the callback was called 2 times (once during the connection handshake, and again during + reauthentication). +- Close the client. + +#### `[callback-only]` 4.2 Read Commands Fail If Reauthentication Fails + +- Create a `MongoClient` whose OIDC callback returns one good token and then bad tokens after the first call. +- Perform a `find` operation that succeeds. +- Set a fail point for `find` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a `find` operation that fails. +- Assert that the callback was called 2 times. +- Close the client. + +#### `[callback-only]` 4.3 Write Commands Fail If Reauthentication Fails + +- Create a `MongoClient` whose OIDC callback returns one good token and then bad tokens after the first call. +- Perform an `insert` operation that succeeds. +- Set a fail point for `insert` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "insert" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a `insert` operation that fails. +- Assert that the callback was called 2 times. +- Close the client. + +#### 4.4 Speculative Authentication should be ignored on Reauthentication + +- Create an OIDC configured client. +- Populate the *Client Cache* with a valid access token to enforce Speculative Authentication. + - This may be done by authenticating a temporary OIDC configured client and copying the cached token. +- Perform an `insert` operation that succeeds. +- Assert that the callback was not called. +- Assert there were no `saslStart` commands executed. +- Set a fail point for `insert` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "insert" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform an `insert` operation that succeeds. +- `[callback-only]` Assert that the callback was called once. +- Assert there were `saslStart` commands executed. +- Close the client. + +#### 4.5 Reauthentication Succeeds when a Session is involved + +- Create an OIDC configured client. +- Set a fail point for `find` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Start a new session. +- In the started session perform a `find` operation that succeeds. +- `[callback-only]` Assert that the callback was called 2 times (once during the connection handshake, and again during + reauthentication). +- Close the session and the client. + +## (5) Azure Tests + +Drivers MUST only run the Azure tests when testing on an Azure VM. See instructions in +[Drivers Evergreen Tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/azure/README.md) +for test setup. + +# 5.1 Azure With No Username + +- Create an OIDC configured client with `ENVIRONMENT:azure` and a valid `TOKEN_RESOURCE` and no username. +- Perform a `find` operation that succeeds. +- Close the client. + +# 5.2 Azure with Bad Username + +- Create an OIDC configured client with `ENVIRONMENT:azure` and a valid `TOKEN_RESOURCE` and a username of `"bad"`. +- Perform a `find` operation that fails. +- Close the client. + +______________________________________________________________________ + +## Human Authentication Flow Prose Tests + +Drivers that support the [Human Authentication Flow](../auth.md#human-authentication-flow) MUST implement all prose +tests in this section. Unless otherwise noted, all `MongoClient` instances MUST be configured with `retryReads=false`. + +The human workflow tests MUST only be run when `OIDC_TOKEN_DIR` is set. + +> [!NOTE] +> For test cases that create fail points, drivers MUST either use a unique `appName` or explicitly remove the fail point +> after the test to prevent interaction between test cases. + +Drivers MUST be able to authenticate against a server configured with either one or two configured identity providers. + +Unless otherwise specified, use `MONGODB_URI_SINGLE` and the `test_user1` token in the `OIDC_TOKEN_DIR` as the +"access_token", and a dummy "refresh_token" for all tests. + +When using an explicit username for the client, we use the token name and the domain name given by `OIDC_DOMAIN`, e.g. +`test_user1@${OIDC_DOMAIN}`. + +### (1) OIDC Human Callback Authentication + +Drivers MUST be able to authenticate using OIDC callback(s) when there is one principal configured. + +**1.1 Single Principal Implicit Username** + +- Create an OIDC configured client. +- Perform a `find` operation that succeeds. +- Close the client. + +**1.2 Single Principal Explicit Username** + +- Create an OIDC configured client with `MONGODB_URI_SINGLE` and a username of `test_user1@${OIDC_DOMAIN}`. +- Perform a `find` operation that succeeds. +- Close the client. + +**1.3 Multiple Principal User 1** + +- Create an OIDC configured client with `MONGODB_URI_MULTI` and username of `test_user1@${OIDC_DOMAIN}`. +- Perform a `find` operation that succeeds. +- Close the client. + +**1.4 Multiple Principal User 2** + +- Create an OIDC configured client with `MONGODB_URI_MULTI` and username of `test_user2@${OIDC_DOMAIN}`. that reads the + `test_user2` token file. +- Perform a `find` operation that succeeds. +- Close the client. + +**1.5 Multiple Principal No User** + +- Create an OIDC configured client with `MONGODB_URI_MULTI` and no username. +- Assert that a `find` operation fails. +- Close the client. + +**1.6 Allowed Hosts Blocked** + +- Create an OIDC configured client with an `ALLOWED_HOSTS` that is an empty list. +- Assert that a `find` operation fails with a client-side error. +- Close the client. +- Create a client that uses the URL `mongodb://localhost/?authMechanism=MONGODB-OIDC&ignored=example.com`, a human + callback, and an `ALLOWED_HOSTS` that contains `["example.com"]`. +- Assert that a `find` operation fails with a client-side error. +- Close the client. + +**1.7 Allowed Hosts in Connection String Ignored** + +- Create an OIDC configured client with the connection string: + `mongodb+srv://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=ALLOWED_HOSTS:%5B%22example.com%22%5D` + and a Human Callback. +- Assert that the creation of the client raises a configuration error. + +**1.8 Machine IdP with Human Callback** + +This test MUST only be run when `OIDC_IS_LOCAL` is set. This indicates that the server is local and not using Atlas. In +this case, `MONGODB_URI_SINGLE` will be configured with a human user `test_user1`, and a machine user `test_machine`. +This test uses the machine user with a human callback, ensuring that the missing `clientId` in the +`PrincipalStepRequest` response is handled by the driver. + +- Create an OIDC configured client with `MONGODB_URI_SINGLE` and a username of `test_machine` that uses the + `test_machine` token. +- Perform a find operation that succeeds. +- Close the client. + +### (2) OIDC Human Callback Validation + +**2.1 Valid Callback Inputs** + +- Create an OIDC configured client with a human callback that validates its inputs and returns a valid access token. +- Perform a `find` operation that succeeds. Verify that the human callback was called with the appropriate inputs, + including the timeout parameter if possible. +- Close the client. + +**2.2 Human Callback Returns Missing Data** + +- Create an OIDC configured client with a human callback that returns data not conforming to the `OIDCCredential` with + missing fields. +- Perform a `find` operation that fails. +- Close the client. + +**2.3 Refresh Token Is Passed To The Callback** + +- Create a `MongoClient` with a human callback that checks for the presence of a refresh token. +- Perform a find operation that succeeds. +- Set a fail point for `find` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 + } +} +``` + +- Perform a `find` operation that succeeds. +- Assert that the callback has been called twice. +- Assert that the refresh token was provided to the callback once. + +### (3) Speculative Authentication + +**3.1 Uses speculative authentication if there is a cached token** + +- Create an OIDC configured client with a human callback that returns a valid token. +- Set a fail point for `find` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + closeConnection: true + } +} +``` + +- Perform a `find` operation that fails. +- Set a fail point for `saslStart` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "saslStart" + ], + errorCode: 18 + } +} +``` + +- Perform a `find` operation that succeeds. +- Close the client. + +**3.2 Does not use speculative authentication if there is no cached token** + +- Create an OIDC configured client with a human callback that returns a valid token. +- Set a fail point for `saslStart` commands of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "saslStart" + ], + errorCode: 18 + } +} +``` + +- Perform a `find` operation that fails. +- Close the client. + +### (4) Reauthentication + +**4.1 Succeeds** + +- Create an OIDC configured client and add an event listener. The following assumes that the driver does not emit + `saslStart` or `saslContinue` events. If the driver does emit those events, ignore/filter them for the purposes of + this test. +- Perform a `find` operation that succeeds. +- Assert that the human callback has been called once. +- Clear the listener state if possible. +- Force a reauthenication using a fail point of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform another find operation that succeeds. +- Assert that the human callback has been called twice. +- Assert that the ordering of list started events is \[`find`\], , `find`. Note that if the listener stat could not be + cleared then there will and be extra `find` command. +- Assert that the list of command succeeded events is \[`find`\]. +- Assert that a `find` operation failed once during the command execution. +- Close the client. + +**4.2 Succeeds no refresh** + +- Create an OIDC configured client with a human callback that does not return a refresh token. +- Perform a `find` operation that succeeds. +- Assert that the human callback has been called once. +- Force a reauthenication using a fail point of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find" + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a `find` operation that succeeds. +- Assert that the human callback has been called twice. +- Close the client. + +**4.3 Succeeds after refresh fails** + +- Create an OIDC configured client with a callback that returns the `test_user1` access token and a bad refresh token. +- Perform a `find` operation that succeeds. +- Assert that the human callback has been called once. +- Force a reauthenication using a fail point of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find", + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a `find` operation that succeeds. +- Assert that the human callback has been called 2 times. +- Close the client. + +**4.4 Fails** + +- Create an OIDC configured client that returns invalid refresh tokens and returns invalid access tokens after the first + access. +- Perform a find operation that succeeds (to force a speculative auth). +- Assert that the human callback has been called once. +- Force a reauthenication using a failCommand of the form: + +```javascript +{ + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: [ + "find", + ], + errorCode: 391 // ReauthenticationRequired + } +} +``` + +- Perform a find operation that fails. +- Assert that the human callback has been called three times. +- Close the client. diff --git a/source/auth/tests/unified/mongodb-oidc-no-retry.json b/source/auth/tests/unified/mongodb-oidc-no-retry.json new file mode 100644 index 0000000000..b32ada172a --- /dev/null +++ b/source/auth/tests/unified/mongodb-oidc-no-retry.json @@ -0,0 +1,428 @@ +{ + "description": "MONGODB-OIDC authentication with retry disabled", + "schemaVersion": "1.19", + "runOnRequirements": [ + { + "minServerVersion": "7.0", + "auth": true, + "authMechanism": "MONGODB-OIDC", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client0", + "uriOptions": { + "authMechanism": "MONGODB-OIDC", + "authMechanismProperties": { + "$$placeholder": 1 + }, + "retryReads": false, + "retryWrites": false, + "appName": "mongodb-oidc-no-retry" + }, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collName" + } + } + ], + "initialData": [ + { + "collectionName": "collName", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "A read operation should succeed", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collName", + "filter": {} + } + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "A write operation should succeed", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collName", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Read commands should reauthenticate and retry when a ReauthenticationRequired error happens", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 391, + "appName": "mongodb-oidc-no-retry" + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collName", + "filter": {} + } + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collName", + "filter": {} + } + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Write commands should reauthenticate and retry when a ReauthenticationRequired error happens", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 391, + "appName": "mongodb-oidc-no-retry" + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collName", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collName", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Handshake with cached token should use speculative authentication", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true, + "appName": "mongodb-oidc-no-retry" + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslStart" + ], + "errorCode": 18, + "appName": "mongodb-oidc-no-retry" + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collName", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collName", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Handshake without cached token should not use speculative authentication", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslStart" + ], + "errorCode": 18, + "appName": "mongodb-oidc-no-retry" + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": 1 + } + }, + "expectError": { + "errorCode": 18 + } + } + ] + } + ] +} diff --git a/source/auth/tests/unified/mongodb-oidc-no-retry.yml b/source/auth/tests/unified/mongodb-oidc-no-retry.yml new file mode 100644 index 0000000000..dba2ae8d1d --- /dev/null +++ b/source/auth/tests/unified/mongodb-oidc-no-retry.yml @@ -0,0 +1,235 @@ +--- +description: "MONGODB-OIDC authentication with retry disabled" +schemaVersion: "1.19" +runOnRequirements: +- minServerVersion: "7.0" + auth: true + authMechanism: "MONGODB-OIDC" + serverless: forbid +createEntities: +- client: + id: &failPointClient failPointClient + useMultipleMongoses: false +- client: + id: client0 + uriOptions: + authMechanism: "MONGODB-OIDC" + # The $$placeholder document should be replaced by auth mechanism + # properties that enable OIDC auth on the target cloud platform. For + # example, when running the test on EC2, replace the $$placeholder + # document with {"ENVIRONMENT": "test"}. + authMechanismProperties: { $$placeholder: 1 } + retryReads: false + retryWrites: false + appName: &appName mongodb-oidc-no-retry + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent +- database: + id: database0 + client: client0 + databaseName: test +- collection: + id: collection0 + database: database0 + collectionName: collName +initialData: +- collectionName: collName + databaseName: test + documents: [] +tests: +- description: A read operation should succeed + operations: + - name: find + object: collection0 + arguments: + filter: {} + expectResult: [] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collName + filter: {} + - commandSucceededEvent: + commandName: find +- description: A write operation should succeed + operations: + - name: insertOne + object: collection0 + arguments: + document: + _id: 1 + x: 1 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + insert: collName + documents: + - _id: 1 + x: 1 + - commandSucceededEvent: + commandName: insert +- description: Read commands should reauthenticate and retry when a ReauthenticationRequired error happens + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 391 # ReauthenticationRequired + appName: *appName + - name: find + object: collection0 + arguments: + filter: {} + expectResult: [] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collName + filter: {} + - commandFailedEvent: + commandName: find + - commandStartedEvent: + command: + find: collName + filter: {} + - commandSucceededEvent: + commandName: find +- description: Write commands should reauthenticate and retry when a ReauthenticationRequired error happens + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + errorCode: 391 # ReauthenticationRequired + appName: *appName + - name: insertOne + object: collection0 + arguments: + document: + _id: 1 + x: 1 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + insert: collName + documents: + - _id: 1 + x: 1 + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + command: + insert: collName + documents: + - _id: 1 + x: 1 + - commandSucceededEvent: + commandName: insert +- description: Handshake with cached token should use speculative authentication + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + closeConnection: true + appName: *appName + - name: insertOne + object: collection0 + arguments: + document: + _id: 1 + x: 1 + expectError: + isClientError: true + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslStart + errorCode: 18 + appName: *appName + - name: insertOne + object: collection0 + arguments: + document: + _id: 1 + x: 1 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + insert: collName + documents: + - _id: 1 + x: 1 + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + command: + insert: collName + documents: + - _id: 1 + x: 1 + - commandSucceededEvent: + commandName: insert +- description: Handshake without cached token should not use speculative authentication + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslStart + errorCode: 18 + appName: *appName + - name: insertOne + object: collection0 + arguments: + document: + _id: 1 + x: 1 + expectError: + errorCode: 18 diff --git a/source/benchmarking/benchmarking.md b/source/benchmarking/benchmarking.md new file mode 100644 index 0000000000..8a50644ed1 --- /dev/null +++ b/source/benchmarking/benchmarking.md @@ -0,0 +1,772 @@ +# Performance Benchmarking + +- Status: Accepted +- Minimum Server Version: N/A + +## Abstract + +This document describes a standard benchmarking suite for MongoDB drivers. + +## Overview + +### Name and purpose + +Driver performance will be measured by the MongoDB Driver Performance Benchmark (AKA "DriverBench"). It will provide +both "horizontal" insights into how individual language driver performance evolves over time and "vertical" insights +into relative performance of different drivers. + +We do expect substantial performance differences between language families (e.g. static vs. dynamic or compiled vs. +virtual-machine-based). However we still expect "vertical" comparison within language families to expose outlier +behavior that might be amenable to optimization. + +### Task Hierarchy + +The benchmark consists of a number of micro-benchmarks tasks arranged into groups of increasing complexity. This allows +us to better isolate areas within drivers that are faster or slower. + +- BSON -- BSON encoding/decoding tasks, to explore BSON codec efficiency +- Single-Doc -- single-document insertion and query tasks, to explore basic wire protocol efficiency +- Multi-Doc -- multi-document insertion and query tasks, to explore batch-write and cursor chunking efficiency +- Parallel -- multi-process/thread ETL tasks, to explore concurrent operation efficiency + +### Measurement + +In addition to timing data, all micro-benchmark tasks will be measured in terms of "megabytes/second" (MB/s) of +documents processed, with higher scores being better. (In this document, "megabyte" refers to the SI decimal unit, i.e. +1,000,000 bytes.) This makes cross-benchmark comparisons easier. + +To avoid various types of measurement skew, tasks will be measured over numerous iterations. Each iteration will have a +"scale" -- the number of similar operations performed -- that will vary by task. The final score for a task will be the +median score of the iterations. Other quantiles will be recorded for diagnostic analysis. + +### Data sets + +Data sets will vary by micro-benchmark. In some cases, they it will be a synthetically generated document inserted +repeatedly (with different `_id` fields) to construct an overall corpus of documents. In other cases, data sets will be +synthetic line-delimited JSON files or mock binary files. + +### Composite scores + +Micro-benchmark scores will be combined into a composite for each weight class ("BSONBench", "SingleBench", etc.) and +for read and write operations ("ReadBench" and "WriteBench"). The read and write scores will be combined into an +aggregate composite score ("DriverBench"). The compositing formula in the DriverBench uses simple averages with equal +weighting. + +### Versioning + +DriverBench will have vX.Y versioning. Minor updates and clarifications will increment "Y" and should have little impact +on score comparison. Major changes, such as changing score weights, MongoDB version tested against, or hardware used, +will increment "X" to indicate that older version scores are unlikely to be comparable. + +## Benchmark execution phases and measurement + +All micro-benchmark tasks will be conducted via a number of iterations. Each iteration will be timed and will generally +include a large number of individual driver operations. + +We break up the measurement this way to better isolate the benchmark from external volatility. If we consider the +problem of benchmarking an operation over many iterations, such as 100,000 document insertions, we want to avoid two +extreme forms of measurement: + +- measuring a single insertion 100,000 times -- in this case, the timing code is likely to be a greater proportion of + executed code, which could routinely evict the insertion code from CPU caches or mislead a JIT optimizer and throw + off results +- measuring 100,000 insertions one time -- in this case, the longer the timer runs, the higher the likelihood that an + external event occurs that affects the time of the run + +Therefore, we choose a middle ground: + +- measuring the same 1000 insertions over 100 iterations -- each timing run includes enough operations that insertion + code dominates timing code; unusual system events are likely to affect only a fraction of the 100 timing + measurements + +With 100 timings of inserting the same 1000 documents, we build up a statistical distribution of the operation timing, +allowing a more robust estimate of performance than a single measurement. (In practice, the number of iterations could +exceed 100, but 100 is a reasonable minimum goal.) + +Because a timing distribution is bounded by zero on one side, taking the mean would allow large positive outlier +measurements to skew the result substantially. Therefore, for the benchmark score, we use the median timing measurement, +which is robust in the face of outliers. + +Each benchmark is structured into discrete setup/execute/teardown phases. Phases are as follows, with specific details +given in a subsequent section: + +- setup -- (ONCE PER MICRO-BENCHMARK) something to do once before any benchmarking, e.g. construct a client object, load + test data, insert data into a collection, etc. +- before task -- (ONCE PER ITERATION) something to do before every task iteration, e.g. drop a collection, or reload + test data (if the test run modifies it), etc. +- do task -- (ONCE PER ITERATION) smallest amount of code necessary to execute the task; e.g. insert 1000 documents one + by one into the database, or retrieve 1000 document of test data from the database, etc. +- after task -- (ONCE PER ITERATION) something to do after every task iteration (if necessary) +- teardown -- (ONCE PER MICRO-BENCHMARK) something done once after all benchmarking is complete (if necessary); e.g. + drop the test database + +The wall-clock execution time of each "do task" phase will be recorded. We use wall clock time to model user experience +and as a lowest-common denominator across languages and threading models. Iteration timing should be done with a +high-resolution monotonic timer (or best language approximation). + +Unless otherwise specified, the number of iterations to measure per micro-benchmark is variable: + +- iterations should loop for at least 1 minute cumulative execution time +- iterations should stop after 100 iterations or 5 minutes cumulative execution time, whichever is shorter + +This balances measurement stability with a timing cap to ensure all micro-benchmarks can complete in a reasonable time. +Languages with JIT compilers may do warm up iterations for which timings are discarded. + +For each micro-benchmark, the 10th, 25th, 50th, 75th, 90th, 95th, 98th and 99th percentiles will be recorded using the +following algorithm: + +- Given a 0-indexed array A of N iteration wall clock times +- Sort the array into ascending order (i.e. shortest time first) +- Let the index i for percentile p in the range [1,100] be defined as: `i = int(N * p / 100) - 1` + +*N.B. This is the [Nearest Rank](https://en.wikipedia.org/wiki/Percentile#The_Nearest_Rank_method) algorithm, chosen for +its utter simplicity given that it needs to be implemented identically across multiple languages for every driver.* + +The 50th percentile (i.e. the median) will be used for score composition. Other percentiles will be stored for +visualizations and analysis (e.g. a "candlestick" chart showing benchmark volatility over time). + +Each task will have defined for it an associated size in megabytes (MB). The score for micro-benchmark composition will +be the task size in MB divided by the median wall clock time. + +## Micro-benchmark definitions + +Datasets are available in the `data` directory adjacent to this spec. + +Note: The term "LDJSON" means "line-delimited JSON", which should be understood to mean a collection of UTF-8 encoded +JSON documents (without embedded CR or LF characters), separated by a single LF character. (Some Internet definition of +line-delimited JSON use CRLF delimiters, but this benchmark uses only LF.) + +### BSON micro-benchmarks + +Datasets are in the `extended_bson` tarball. + +BSON tests focus on BSON encoding and decoding; they are client-side only and do not involve any transmission of data to +or from the benchmark server. When appropriate, data sets will be stored on disk as +[extended strict JSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json). For drivers that don't +support extended JSON, a BSON analogue will be provided as well. + +BSON micro-benchmarks include: + +- Flat BSON Encoding and Flat BSON Decoding -- shallow documents with only common BSON field types +- Deep BSON Encoding and Deep BSON Decoding -- deeply nested documents with only common BSON field types +- Full BSON Encoding and Full BSON Decoding -- shallow documents with all possible BSON field types + +#### Flat BSON Encoding + +Summary: This benchmark tests driver performance encoding documents with top level key/value pairs involving the most +commonly-used BSON types. + +Dataset: The dataset, designated FLAT_BSON (ftnt4 Disk file `flat_bson.json`), will be synthetically generated and +consist of an extended JSON document with a single `_id` key with an object ID value plus 24 top level keys/value pairs +of the following types: string, Int32, Int64, Double, Boolean. (121 total key/value pairs) Keys will be random ASCII +strings of length 8. String data will be random ASCII strings of length 80. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (7531 +bytes) times 10,000 operations, which equals 75,310,000 bytes or 75.31 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the FLAT_BSON dataset into memory as a language-appropriate document types. For languages like C without a document type, the raw JSON string for each document should be used instead. | +| Before task | n/a | +| Do task | Encode the FLAT_BSON document to a BSON byte-string. Repeat this 10,000 times. | +| After task | n/a | +| Teardown | n/a | + +#### Flat BSON Decoding + +Summary: This benchmark tests driver performance decoding documents with top level key/value pairs involving the most +commonly-used BSON types. + +Dataset: The dataset, designated FLAT_BSON, will be synthetically generated and consist of an extended JSON document +with a single `_id` key with an object ID value plus 24 top level keys/value pairs of each of the following types: +string, Int32, Int64, Double, Boolean. (121 total key/value pairs) Keys will be random ASCII strings of length 8. String +data will be random ASCII strings of length 80. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (7531 +bytes) times 10,000 operations, which equals 75,310,000 bytes or 75.31 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the FLAT_BSON dataset into memory as a language-appropriate document types. For languages like C without a document type, the raw JSON string for each document should be used instead. Encode it to a BSON byte-string. | +| Before task | n/a | +| Do task | Decode the BSON byte-string to a language-appropriate document type. Repeat this 10,000 times. For languages like C without a document type, decode to extended JSON instead. | +| After task | n/a | +| Teardown | n/a | + +#### Deep BSON Encoding + +Summary: This benchmark tests driver performance encoding documents with deeply nested key/value pairs involving +subdocuments, strings, integers, doubles and booleans. + +Dataset: The dataset, designated DEEP_BSON (disk file `deep_bson.json`), will be synthetically generated and consist of +an extended JSON document representing a balanced binary tree of depth 6, with "left" and "right" keys at each level +containing a sub-document until the final level, where each leaf contains two string fields, "leftValue" and +"rightValue", each holding a random 8-character ASCII string (126 total key/value pairs). + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2284 +bytes) times 10,000 operations, which equals 22,840,000 bytes or 22.84 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the DEEP_BSON dataset into memory as a language-appropriate document type. For languages like C without a document type, the raw JSON string for each document should be used instead. | +| Before task | n/a | +| Do task | Encode the DEEP_BSON document to a BSON byte-string. Repeat this 10,000 times. | +| After task | n/a | +| Teardown | n/a | + +#### Deep BSON Decoding + +Summary: This benchmark tests driver performance decoding documents with deeply nested key/value pairs involving +subdocuments, strings, integers, doubles and booleans. + +Dataset: The dataset, designated DEEP_BSON, will be synthetically generated and consist of an extended JSON document +representing a balanced binary tree of depth 6, with "left" and "right" keys at each level containing a sub-document +until the final level, where each leaf contains two string fields, "leftValue" and "rightValue", each holding a random +8-character ASCII string (126 total key/value pairs). + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2284 +bytes) times 10,000 operations, which equals 22,840,000 bytes or 22.84 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the DEEP_BSON dataset into memory as a language-appropriate document types. For languages like C without a document type, the raw JSON string for each document should be used instead. Encode it to a BSON byte-string. | +| Before task | n/a | +| Do task | Decode the BSON byte-string to a language-appropriate document type. Repeat this 10,000 times. For languages like C without a document type, decode to extended JSON instead. | +| After task | n/a | +| Teardown | n/a | + +#### Full BSON Encoding + +Summary: This benchmark tests driver performance encoding documents with top level key/value pairs involving the full +range of BSON types. + +Dataset: The dataset, designated FULL_BSON (disk file `full_bson.json`), will be synthetically generated and consist of +an extended JSON document with a single `_id` key with an object ID value plus 6 each of the following types: string, +double, Int64, Int32, boolean, minkey, maxkey, array, binary data, UTC datetime, regular expression, Javascript code, +Javascript code with context, and timestamp. (91 total keys.) Keys (other than `_id`) will be random ASCII strings of +length 8. Strings values will be random ASCII strings with length 80. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (5734 +bytes) times 10,000 operations, which equals 57,340,000 bytes or 57.34 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the FULL_BSON dataset into memory as a language-appropriate document type. For languages like C without a document type, the raw JSON string for each document should be used instead. | +| Before task | n/a | +| Do task | Encode the FULL_BSON document to a BSON byte-string. Repeat this 10,000 times. | +| After task | n/a | +| Teardown | n/a | + +#### Full BSON Decoding + +Summary: This benchmark tests driver performance decoding documents with top level key/value pairs involving the full +range of BSON types. + +Dataset: The dataset, designated FULL_BSON, will be synthetically generated and consist of an extended JSON document +with a single `_id` key with an object ID value plus 6 each of the following types: string, double, Int64, Int32, +boolean, minkey, maxkey, array, binary data, UTC datetime, regular expression, Javascript code, Javascript code with +context, and timestamp. (91 total keys.) Keys (other than `_id`) will be random ASCII strings of length 8. Strings +values will be random ASCII strings with length 80. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (5734 +bytes) times 10,000 operations, which equals 57,340,000 bytes or 57.34 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the FULL_BSON dataset into memory as a language-appropriate document types. For languages like C without a document type, the raw JSON string for each document should be used instead. Encode it to a BSON byte-string. | +| Before task | n/a | +| Do task | Decode the BSON byte-string to a language-appropriate document type. Repeat this 10,000 times. For languages like C without a document type, decode to extended JSON instead. | +| After task | n/a | +| Teardown | n/a | + +### Single-Doc Benchmarks + +Datasets are in the `single_and_multi_document` tarball. + +Single-doc tests focus on single-document read and write operations. They are designed to give insights into the +efficiency of the driver's implementation of the basic wire protocol. + +The data will be stored as strict JSON with no extended types. + +Single-doc micro-benchmarks include: + +- Run command +- Find one by ID +- Small doc insertOne +- Large doc insertOne + +#### Run command + +Summary: This benchmark tests driver performance sending a command to the database and reading a response. + +Dataset: n/a + +Dataset size: While there is no external dataset, for score calculation purposes use 130,000 bytes (10,000 x the size of +a BSON {hello:true} command). + +*N.B. We use {hello:true} rather than {hello:1} to ensure a consistent command size.* + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Construct whatever language-appropriate objects (Database, etc.) would be required to send a command. | +| Before task | n/a | +| Do task | Run the command {hello:true} 10,000 times, reading (and discarding) the result each time. | +| After task | n/a | +| Teardown | n/a | + +#### Find one by ID + +Summary: This benchmark tests driver performance sending an indexed query to the database and reading a single document +in response. + +Dataset: The dataset, designated TWEET (disk file `tweet.json`), consists of a sample tweet stored as strict JSON. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (1622 +bytes) times 10,000 operations, which equals 16,220,000 bytes or 16.22 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the TWEET document into memory as a language-appropriate document type (or JSON string for C). Construct a Collection object for the `corpus` collection to use for querying. Insert the document 10,000 times to the `perftest` database in the `corpus` collection using sequential `_id` values. (1 to 10,000) | +| Before task | n/a | +| Do task | For each of the 10,000 sequential `_id` numbers, issue a find command for that `_id` on the `corpus` collection and retrieve the single-document result. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc insertOne + +Summary: This benchmark tests driver performance inserting a single, small document to the database. + +Dataset: The dataset, designated SMALL_DOC (disk file `small_doc.json`), consists of a JSON document with an encoded +length of approximately 250 bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 10,000 operations, which equals 2,750,000 bytes or 2.75 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C). | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Insert the document with the insertOne CRUD method. DO NOT manually add an `_id` field; leave it to the driver or database. Repeat this 10,000 times. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Large doc insertOne + +Summary: This benchmark tests driver performance inserting a single, large document to the database. + +Dataset: The dataset, designated LARGE_DOC (disk file `large_doc.json`), consists of a JSON document with an encoded +length of approximately 2,500,000 bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2,731,089 +bytes) times 10 operations, which equals 27,310,890 bytes or 27.31 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the LARGE_DOC dataset into memory as a language-appropriate document type (or JSON string for C). | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Insert the document with the insertOne CRUD method. DO NOT manually add an `_id` field; leave it to the driver or database. Repeat this 10 times. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +### Multi-Doc Benchmarks + +Datasets are in the `single_and_multi_document` tarball. + +Multi-doc benchmarks focus on multiple-document read and write operations. They are designed to give insight into the +efficiency of the driver's implementation of bulk/batch operations such as bulk writes and cursor reads. + +Multi-doc micro-benchmarks include: + +- Find many and empty the cursor +- Small doc bulk insert +- Large doc bulk insert +- Small doc Collection BulkWrite insert +- Large doc Collection BulkWrite insert +- Small doc Client BulkWrite insert +- Large doc Client BulkWrite insert +- Small doc Client BulkWrite Mixed Operations +- Small doc Collection BulkWrite Mixed Operations +- GridFS upload +- GridFS download + +#### Find many and empty the cursor + +Summary: This benchmark tests driver performance retrieving multiple documents from a query. + +Dataset: The dataset, designated TWEET consists of a sample tweet stored as strict JSON. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (1622 +bytes) times 10,000 operations, which equals 16,220,000 bytes or 16.22 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the TWEET dataset into memory as a language-appropriate document type (or JSON string for C). Construct a Collection object for the `corpus` collection to use for querying. Insert the document 10,000 times to the `perftest` database in the `corpus` collection. (Let the driver generate `_id`s). | +| Before task | n/a | +| Do task | Issue a find command on the `corpus` collection with an empty filter expression. Retrieve (and discard) all documents from the cursor. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc bulk insert + +Summary: This benchmark tests driver performance inserting multiple small documents to the database using `insertMany`. + +Dataset: The dataset, designated SMALL_DOC consists of a JSON document with an encoded length of approximately 250 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 10,000 operations, which equals 2,750,000 bytes or 2.75 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C). | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `insertMany` with 10,000 copies of the document. DO NOT manually add an `_id` field; leave it to the driver or database. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Large doc bulk insert + +Summary: This benchmark tests driver performance inserting multiple large documents to the database using `insertMany`. + +Dataset: The dataset, designated LARGE_DOC consists of a JSON document with an encoded length of approximately 2,500,000 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2,731,089 +bytes) times 10 operations, which equals 27,310,890 bytes or 27.31 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the LARGE_DOC dataset into memory as a language-appropriate document type (or JSON string for C). | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `insertMany` with 10 copies of the document. DO NOT manually add an `_id` field; leave it to the driver or database. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc Collection BulkWrite insert + +Summary: This benchmark tests driver performance inserting multiple small documents to the database using the +`Collection::bulkWrite` operation. + +Dataset: The dataset, designated SMALL_DOC consists of a JSON document with an encoded length of approximately 250 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 10,000 operations, which equals 2,750,000 bytes or 2.75 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C). Create a list of insert models for 10,000 copies of the document, allowing the driver or database to auto-assign the `_id` field. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `Collection::bulkWrite` with the list of insert models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Large doc Collection BulkWrite insert + +Summary: This benchmark tests driver performance inserting multiple large documents to the database using the +`Collection::bulkWrite` operation. + +Dataset: The dataset, designated LARGE_DOC consists of a JSON document with an encoded length of approximately 2,500,000 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2,731,089 +bytes) times 10 operations, which equals 27,310,890 bytes or 27.31 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the LARGE_DOC dataset into memory as a language-appropriate document type (or JSON string for C). Create a list of insert models for 10 copies of the document, allowing the driver or database to auto-assign the `_id` field. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `Collection::bulkWrite` with the list of insert models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc Client BulkWrite insert + +Summary: This benchmark tests driver performance inserting multiple small documents to the database using the +`Client::bulkWrite` operation. + +Dataset: The dataset, designated SMALL_DOC consists of a JSON document with an encoded length of approximately 250 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 10,000 operations, which equals 2,750,000 bytes or 2.75 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C). Create a list of insert models for 10,000 copies of the document in the same namespace ("perftest.corpus"), allowing the driver or database to auto-assign the `_id` field. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `Client::bulkWrite` with the list of insert models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Large doc Client BulkWrite insert + +Summary: This benchmark tests driver performance inserting multiple large documents to the database using the +`Client::bulkWrite` operation. + +Dataset: The dataset, designated LARGE_DOC consists of a JSON document with an encoded length of approximately 2,500,000 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (2,731,089 +bytes) times 10 operations, which equals 27,310,890 bytes or 27.31 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the LARGE_DOC dataset into memory as a language-appropriate document type (or JSON string for C). Create a list of insert models for 10 copies of the document in the same namespace ("perftest.corpus"), allowing the driver or database to auto-assign the `_id` field. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion.
| +| Do task | Do an ordered `Client::bulkWrite` with the list of insert models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc Client BulkWrite Mixed Operations + +Summary: This benchmark tests driver performance of a `Client::bulkWrite` operation with small documents and mixed +operations (e.g. insert, replace and delete). + +Dataset: The dataset, designated SMALL_DOC consists of a JSON document with an encoded length of approximately 250 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 20,000 operations (insert + replace), which equals 5,500,000 bytes or 5.5 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C).
Create a list of 10 numbered collection names (`corpus_1`, `corpus_2`, ...)
Create a list of write models for 10,000 document copies. For each document (where i goes from 0 to 9,999), create three write models with the namespace set to the collection name found at index `i % 10` in your collection names list:
1. An insert model.
2. A replace model (using an empty filter and a copy of the document).
3. A delete model (using an empty filter).
Notes:
- DO NOT manually add an `_id` field; leave it to the driver or database. You should have 30,000 write models in your list. | +| Before task | Drop the `perftest` database, create it again and create the collections found in your numbered collection names list. | +| Do task | Do an ordered `Client::bulkWrite` with the list of write models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### Small doc Collection BulkWrite Mixed Operations + +Summary: This benchmark tests driver performance of a `Collection::bulkWrite` operation with small documents and mixed +operations (e.g. insert, replace and delete). + +Dataset: The dataset, designated SMALL_DOC consists of a JSON document with an encoded length of approximately 250 +bytes. + +Dataset size: For score purposes, the dataset size for a task is the size of the single-document source file (275 bytes) +times 20,000 operations (insert + replace), which equals 5,500,000 bytes or 5.5 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Load the SMALL_DOC dataset into memory as a language-appropriate document type (or JSON string for C). Create a list of write models for 10,000 copies of the document where for each copy of the document add the following models:
1. insert model.
2. replace model with a copy and using an empty filter.
3. delete model using empty filter.
DO NOT manually add an `_id` field; leave it to the driver or database. You should have 30,000 write models in your list. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. Construct a Collection object for the `corpus` collection to use for insertion. | +| Do task | Do an ordered `Collection::bulkWrite` with the list of write models. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### GridFS upload + +Summary: This benchmark tests driver performance uploading a GridFS file from memory. + +Dataset: The dataset, designated GRIDFS_LARGE (disk file `gridfs_large.bin`), consists of a single file containing about +50 MB of random data. We use a large file to ensure multiple database round-trips even if chunks are are sent in +batches. + +Dataset size: For score purposes, the dataset size for a task is the size of the source file (52,428,800 bytes) times 1 +operation or 52.43 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. rop the `perftest` database. Load the GRIDFS_LARGE file as a string or other language-appropriate type for binary octet data. | +| Before task | Drop the default GridFS bucket. Insert a 1-byte file into the bucket. (This ensures the bucket collections and indices have been created.) Construct a GridFSBucket object to use for uploads. | +| Do task | Upload the GRIDFS_LARGE data as a GridFS file. Use whatever upload API is most natural for each language (e.g. open_upload_stream(), write the data to the stream and close the stream). | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### GridFS download + +Summary: This benchmark tests driver performance downloading a GridFS file to memory. + +Dataset: The dataset, designated GRIDFS_LARGE, consists of a single file containing about 50 MB of random data. We use a +large file to ensure multiple database round-trips even if chunks are are sent in batches. + +Dataset size: For score purposes, the dataset size for a task is the size of the source file (52,428,800 bytes) times 1 +operation or 52.43 MB. + +| Phase | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Upload the GRIDFS_LARGE file to the default gridFS bucket with the name "gridfstest". Record the `_id` of the uploaded file. | +| Before task | Construct a GridFSBucket object to use for downloads. | +| Do task | Download the "gridfstest" file by its `_id`. Use whatever download API is most natural for each language (e.g. open_download_stream(), read from the stream into a variable). Discard the downloaded data. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +### Parallel + +Datasets are in the `parallel` tarball. + +Parallel tests simulate ETL operations from disk to database or vice-versa. They are designed to be implemented using a +language's preferred approach to concurrency and thus stress how drivers handle concurrency. These intentionally involve +overhead above and beyond the driver itself to simulate -- however loosely -- the sort of "real-world" pressures that a +drivers would be under during concurrent operation. + +They are intended for directional indication of which languages perform best for this sort of pseudo-real-world +activity, but are not intended to represent real-world performance claims. + +Drivers teams are expected to treat these as a competitive "shoot-out" to surface optimal ETL patterns for each language +(e.g. multi-thread, multi-process, asynchronous I/O, etc.). + +Parallel micro-benchmarks include: + +- LDJSON multi-file import +- LDJSON multi-file export +- GridFS multi-file upload +- GridFS multi-file download + +#### LDJSON multi-file import + +Summary: This benchmark tests driver performance importing documents from a set of LDJSON files. + +Dataset: The dataset, designated LDJSON_MULTI (disk directory `ldjson_multi`), consists of 100 LDJSON files, each +containing 5,000 JSON documents. Each document should be about 1000 bytes. + +Dataset size: For score purposes, the dataset size for a task is the total size of all source files: 565,000,000 bytes +or 565 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. | +| Before task | Drop the `corpus` collection. Create an empty `corpus` collection with the `create` command. | +| Do task | Do an unordered insert of all 500,000 documents in the dataset into the `corpus` collection as fast as possible. Data must be loaded from disk during this phase. Concurrency is encouraged. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### LDJSON multi-file export + +Summary: This benchmark tests driver performance exporting documents to a set of LDJSON files. + +Dataset: The dataset, designated LDJSON_MULTI, consists of 100 LDJSON files, each containing 5,000 JSON documents. Each +document should be about 1000 bytes. + +Dataset size: For score purposes, the dataset size for a task is the total size of all source files: 565,000,000 bytes +or 565 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Drop the `corpus` collection. Do an unordered insert of all 500,000 documents in the dataset into the `corpus` collection. | +| Before task | Construct whatever objects, threads, etc. are required for exporting the dataset. | +| Do task | Dump all 500,000 documents in the dataset into 100 LDJSON files of 5,000 documents each as fast as possible. Data must be completely written/flushed to disk during this phase. Concurrency is encouraged. The order and distribution of documents across files does not need to match the original LDJSON_MULTI files. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### GridFS multi-file upload + +Summary: This benchmark tests driver performance uploading files from disk to GridFS. + +Dataset: The dataset, designated GRIDFS_MULTI (disk directory `gridfs_multi`), consists of 50 files, each of 5MB. This +file size corresponds roughly to the output of a (slightly dated) digital camera. Thus the task approximates uploading +50 "photos". + +Dataset size: For score purposes, the dataset size for a task is the total size of all source files: 262,144,000 bytes +or 262.144 MB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Construct a MongoClient object. Drop the `perftest` database. | +| Before task | Drop the default GridFS bucket in the `perftest` database. Construct a GridFSBucket object for the default bucket in `perftest` to use for uploads. Insert a 1-byte file into the bucket (to initialize indexes). | +| Do task | Upload all 50 files in the GRIDFS_MULTI dataset (reading each from disk). Concurrency is encouraged. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +#### GridFS multi-file download + +Summary: This benchmark tests driver performance downloading files from GridFS to disk. + +Dataset: The dataset, designated GRIDFS_MULTI, consists of 50 files, each of 5MB. This file size corresponds roughly to +the output of a (slightly dated) digital camera. Thus the task approximates downloading 50 "photos". + +Dataset size: For score purposes, the dataset size for a task is the total size of all source files: 262,144,000 bytes +or 262.144 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Construct a MongoClient object. Drop the `perftest` database. Construct a temporary directory for holding downloads. Drop the default GridFS bucket in the `perftest` database. Upload the 50 file dataset to the default GridFS bucket in `perftest`. | +| Before task | Delete all files in the temporary folder for downloads. Construct a GridFSBucket object to use for downloads from the default bucket in `perftest`. | +| Do task | Download all 50 files in the GRIDFS_MULTI dataset, saving each to a file in the temporary folder for downloads. Data must be completely written/flushed to disk during this phase. Concurrency is encouraged. | +| After task | n/a | +| Teardown | Drop the `perftest` database. | + +## Composite score calculation + +Every micro-benchmark has a score equal to the 50th percentile (median) of sampled timings, expressed as Megabytes +(1,000,000 bytes) per second where the micro-benchmark "database size" given in each section above is divided by the +50th percentile of measured wall-clock times. + +From these micro-benchmarks, the following composite scores must be calculated: + +| Composite Name | Compositing formula | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| BSONBench | Average of all BSON micro-benchmarks | +| SingleBench | Average of all Single-doc micro-benchmarks, except "Run Command" | +| MultiBench | Average of all Multi-doc micro-benchmarks | +| ParallelBench | Average of all Parallel micro-benchmarks | +| ReadBench | Average of "Find one", "Find many and empty cursor", "GridFS download", "LDJSON multi-file export", and "GridFS multi-file download" microbenchmarks | +| WriteBench | Average of "Small doc insertOne", "Large doc insertOne", "Small doc bulk insert", "Large doc bulk insert", "Small doc Collection BulkWrite insert", "Large doc Collection BulkWrite insert", "Small doc Client BulkWrite insert", "Large doc Client BulkWrite insert", "Small doc Client BulkWrite Mixed Operations", "Small doc Collection BulkWrite Mixed Operations", "GridFS upload", "LDJSON multi-file import", and "GridFS multi-file upload" micro-benchmarks | +| DriverBench | Average of ReadBench and WriteBench | + +At least for this first DriverBench version, scores are combined with simple averages. In addition, the BSONBench scores +do not factor into the overall DriverBench scores, as encoding and decoding are inherent in all other tasks. + +## Benchmark platform, configuration and environments + +### Benchmark Client + +TBD: spec Amazon instance size; describe in general terms how language clients will be run independently; same AWS zone +as server + +All operations must be run with write concern "w:1". + +### Benchmark Server + +TBD: spec Amazon instance size; describe configuration (e.g. no auth, journal, pre-alloc sizes?, WT with compression to +minimize disk I/O impact?); same AWS zone as client + +### Score Server + +TBD: spec system to hold scores over time + +### Datasets + +TBD: generated datasets should be park in S3 or somewhere for retrieval by URL + +## Changelog + +- 2026-06-02: Correct DEEP_BSON dataset size to 2284 bytes (22,840,000 bytes / 22.84 MB) + +- 2026-01-21: Update `deep_bson.json` for compatibility with strong typed benchmarks. + +- 2024-12-23: Add Client and Collection BulkWrite benchmarks + +- 2024-01-22: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2021-04-06: Update run command test to use `hello` command + +- 2016-08-13: + + - Update corpus files to allow much greater compression of data + - Updated LDJSON corpus size to reflect revisions to the test data + - Published data files on GitHub and updated instructions on how to find datasets + - RunCommand and query benchmark can create collection objects during setup rather than before task. (No change on + actual benchmark.) + +- 2016-01-06: + + - Clarify that `bulk insert` means `insert_many` + - Clarify that "create a collection" means using the `create` command + - Add omitted "upload files" step to setup for GridFS multi-file download; also clarify that steps should be using the + default bucket in the `perftest` database + +- 2015-12-23: + + - Rename benchmark names away from MMA/weight class names + - Split BSON encoding and decoding micro-benchmarks + - Rename BSON micro-benchmarks to better match dataset names + - Move "Run Command" micro-benchmark out of composite + - Reduced amount of data held in memory and sent to/from the server to decrease memory pressure and increase number of + iterations in a reasonable time (e.g. file sizes and number of documents in certain datasets changed) + - Create empty collections/indexes during the `before` phase when appropriate + - Updated data set sizes to account for changes in the source file structure/size diff --git a/source/benchmarking/benchmarking.rst b/source/benchmarking/benchmarking.rst index c09e4c7dd6..485f7c6452 100644 --- a/source/benchmarking/benchmarking.rst +++ b/source/benchmarking/benchmarking.rst @@ -1,1169 +1,4 @@ -======================================= -MongoDB Driver Performance Benchmarking -======================================= -:Title: MongoDB Driver Performance Benchmarking -:Author: David Golden -:Minimum Server Version: N/A -:Last Modified: Aug 13, 2016 -:Version: 1.3 - -.. contents:: - -Abstract -======== - -This document describes a standard benchmarking suite for MongoDB drivers. - -Overview -======== - -Name and purpose ----------------- - -Driver performance will be measured by the MongoDB Driver Performance -Benchmark (AKA "DriverBench"). It will provide both "horizontal" insights -into how individual language driver performance evolves over time and -"vertical" insights into relative performance of different drivers. - -We do expect substantial performance differences between language families -(e.g. static vs. dynamic or compiled vs. virtual-machine-based). However we -still expect "vertical" comparison within language families to expose outlier -behavior that might be amenable to optimization. - -Task Hierarchy --------------- - -The benchmark consists of a number of micro-benchmarks tasks arranged -into groups of increasing complexity. This allows us to better isolate -areas within drivers that are faster or slower. - -- BSON -- BSON encoding/decoding tasks, to explore BSON codec efficiency -- Single-Doc -- single-document insertion and query tasks, to explore - basic wire protocol efficiency -- Multi-Doc -- multi-document insertion and query tasks, to explore - batch-write and cursor chunking efficiency -- Parallel -- multi-process/thread ETL tasks, to explore concurrent - operation efficiency - -Measurement ------------ - -In addition to timing data, all micro-benchmark tasks will be measured in -terms of "megabytes/second" (MB/s) of documents processed, with higher scores -being better. (In this document, "megabyte" refers to the SI decimal unit, -i.e. 1,000,000 bytes.) This makes cross-benchmark comparisons easier. - -To avoid various types of measurement skew, tasks will be measured over -numerous iterations. Each iteration will have a "scale" -- the number of -similar operations performed -- that will vary by task. The final score for a -task will be the median score of the iterations. Other quantiles will be -recorded for diagnostic analysis. - -Data sets ---------- - -Data sets will vary by micro-benchmark. In some cases, they it will be a -synthetically generated document inserted repeatedly (with different '\_id' -fields) to construct an overall corpus of documents. In other cases, data sets -will be synthetic line-delimited JSON files or mock binary files. - -Composite scores ----------------- - -Micro-benchmark scores will be combined into a composite for each weight class -("BSONBench", "SingleBench", etc.) and for read and write operations -("ReadBench" and ("WriteBench"). The read and write scores will be combined -into an aggregate composite score ("DriverBench"). The compositing formula in -the DriverBench uses simple averages with equal weighting. - -Versioning ----------- - -DriverBench will have vX.Y versioning. Minor updates and clarifications will -increment "Y" and should have little impact on score comparison. Major -changes, such as changing score weights, MongoDB version tested against, or -hardware used, will increment "X" to indicate that older version scores are -unlikely to be comparable. - -Benchmark execution phases and measurement -========================================== - -All micro-benchmark tasks will be conducted via a number of iterations. Each -iteration will be timed and will generally include a large number of -individual driver operations. - -We break up the measurement this way to better isolate the benchmark from -external volatility. If we consider the problem of benchmarking an operation -over many iterations, such as 100,000 document insertions, we want to avoid -two extreme forms of measurement: - -- measuring a single insertion 100,000 times -- in this case, the timing - code is likely to be a greater proportion of executed code, which - could routinely evict the insertion code from CPU caches or mislead a - JIT optimizer and throw off results -- measuring 100,000 insertions one time -- in this case, the longer the - timer runs, the higher the likelihood that an external event occurs - that affects the time of the run - -Therefore, we choose a middle ground: - -- measuring the same 1000 insertions over 100 iterations -- each timing - run includes enough operations that insertion code dominates timing - code; unusual system events are likely to affect only a fraction of - the 100 timing measurements - -With 100 timings of inserting the same 1000 documents, we build up a -statistical distribution of the operation timing, allowing a more robust -estimate of performance than a single measurement. (In practice, the -number of iterations could exceed 100, but 100 is a reasonable minimum -goal.) - -Because a timing distribution is bounded by zero on one side, taking the -mean would allow large positive outlier measurements to skew the result -substantially. Therefore, for the benchmark score, we use the median -timing measurement, which is robust in the face of outliers. - -Each benchmark is structured into discrete setup/execute/teardown -phases. Phases are as follows, with specific details given in a -subsequent section: - -- setup -- (ONCE PER MICRO-BENCHMARK) something to do once before any - benchmarking, e.g. construct a client object, load test data, insert - data into a collection, etc. -- before task -- (ONCE PER ITERATION) something to do before every task - iteration, e.g. drop a collection, or reload test data (if the test - run modifies it), etc. -- do task -- (ONCE PER ITERATION) smallest amount of code necessary to - execute the task; e.g. insert 1000 documents one by one into the - database, or retrieve 1000 document of test data from the database, - etc. -- after task -- (ONCE PER ITERATION) something to do after every task - iteration (if necessary) -- teardown -- (ONCE PER MICRO-BENCHMARK) something done once after all - benchmarking is complete (if necessary); e.g. drop the test database - -The wall-clock execution time of each "do task" phase will be recorded. -We use wall clock time to model user experience and as a lowest-common -denominator across languages and threading models. Iteration timing -should be done with a high-resolution monotonic timer (or best language -approximation). - -Unless otherwise specified, the number of iterations to measure per -micro-benchmark is variable: - -- iterations should loop for at least 1 minute cumulative execution - time -- iterations should stop after 100 iterations or 5 minutes cumulative - execution time, whichever is shorter - -This balances measurement stability with a timing cap to ensure all -micro-benchmarks can complete in a reasonable time. Languages with JIT -compilers may do warm up iterations for which timings are discarded. - -For each micro-benchmark, the 10th, 25th, 50th, 75th, 90th, 95th, 98th -and 99th percentiles will be recorded using the following -algorithm: - -- Given a 0-indexed array A of N iteration wall clock times -- Sort the array into ascending order (i.e. shortest time first) -- Let the index i for percentile p in the range [1,100] be defined as: - i = int(N \* p / 100) - 1 - -*N.B. This is the `Nearest Rank`_ algorithm, chosen for its utter simplicity -given that it needs to be implemented identically across multiple languages for -every driver.* - -.. _Nearest Rank: https://en.wikipedia.org/wiki/Percentile#The_Nearest_Rank_method - -The 50th percentile (i.e. the median) will be used for score -composition. Other percentiles will be stored for visualizations and -analysis (e.g. a "candlestick" chart showing benchmark volatility over -time). - -Each task will have defined for it an associated size in megabytes (MB). -The score for micro-benchmark composition will be the task size in MB -divided by the median wall clock time. - -Micro-benchmark definitions -=========================== - -Datasets are available in the `data` directory adjacent to this spec. - -Note: The term "LDJSON" means "line-delimited JSON", which should be -understood to mean a collection of UTF-8 encoded JSON documents (without -embedded CR or LF characters), separated by a single LF character. (Some -Internet definition of line-delimited JSON use CRLF delimiters, but this -benchmark uses only LF.) - -BSON micro-benchmarks ---------------------- - -Datasets are in the 'extended\_bson' tarball. - -BSON tests focus on BSON encoding and decoding; they are client-side only and -do not involve any transmission of data to or from the benchmark server. When -appropriate, data sets will be stored on disk as `extended strict JSON`_. For -drivers that don't support extended JSON, a BSON analogue will be provided as -well. - -.. _extended strict JSON: https://docs.mongodb.org/manual/reference/mongodb-extended-json - -BSON micro-benchmarks include: - -- Flat BSON Encoding and Flat BSON Decoding -- shallow documents with - only common BSON field types -- Deep BSON Encoding and Deep BSON Decoding -- deeply nested documents - with only common BSON field types -- Full BSON Encoding and Full BSON Decoding -- shallow documents with - all possible BSON field types - -Flat BSON Encoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance encoding documents with -top level key/value pairs involving the most commonly-used BSON types. - -Dataset: The dataset, designated FLAT\_BSON (ftnt4 Disk file -'flat\_bson.json'), will be synthetically generated and consist of an extended -JSON document with a single \_id key with an object ID value plus 24 top level -keys/value pairs of the following types: string, Int32, Int64, Double, -Boolean. (121 total key/value pairs) Keys will be random ASCII strings of -length 8. String data will be random ASCII strings of length 80. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (7531 bytes) times 10,000 -operations, which equals 75,310,000 bytes or 75.31 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the FLAT\_BSON dataset into | -| | memory as a language-appropriate | -| | document types. For languages like | -| | C without a document type, the raw | -| | JSON string for each document should | -| | be used instead. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Encode the FLAT\_BSON document to a | -| | BSON byte-string. Repeat this 10,000 | -| | times. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Flat BSON Decoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance decoding documents with -top level key/value pairs involving the most commonly-used BSON types. - -Dataset: The dataset, designated FLAT\_BSON, will be synthetically -generated and consist of an extended JSON document with a single -\_id key with an object ID value plus 24 top level keys/value pairs of -each of the following types: string, Int32, Int64, Double, Boolean. -(121 total key/value pairs) Keys will be random ASCII strings of -length 8. String data will be random ASCII strings of length 80. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (7531 bytes) times 10,000 -operations, which equals 75,310,000 bytes or 75.31 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the FLAT\_BSON dataset into | -| | memory as a language-appropriate | -| | document types. For languages like | -| | C without a document type, the raw | -| | JSON string for each document should | -| | be used instead. Encode it to a | -| | BSON byte-string. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Decode the BSON byte-string to a | -| | language-appropriate document type. | -| | Repeat this 10,000 times. For | -| | languages like C without a document | -| | type, decode to extended JSON | -| | instead. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Deep BSON Encoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance encoding documents with -deeply nested key/value pairs involving subdocuments, strings, integers, -doubles and booleans. - -Dataset: The dataset, designated DEEP\_BSON (disk file 'deep\_bson.json'), -will be synthetically generated and consist of an extended JSON document -representing a balanced binary tree of depth 6, with "left" and "right" keys -at each level containing a sub-document until the final level, which will -contain a random ASCII string of length 8 (126 total key/value pairs). - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (1964 bytes) times 10,000 -operations, which equals 19,640,000 bytes or 19.64 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the DEEP\_BSON dataset into | -| | memory as a language-appropriate | -| | document type. For languages like C | -| | without a document type, the raw | -| | JSON string for each document should | -| | be used instead. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Encode the DEEP\_BSON document to a | -| | BSON byte-string. Repeat this 10,000 | -| | times. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Deep BSON Decoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance decoding documents with -deeply nested key/value pairs involving subdocuments, strings, integers, -doubles and booleans. - -Dataset: The dataset, designated DEEP\_BSON, will be synthetically generated -and consist of an extended JSON document representing a balanced binary tree -of depth 6, with "left" and "right" keys at each level containing a -sub-document until the final level, which will contain a random ASCII string -of length 8 (126 total key/value pairs). - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (1964 bytes) times 10,000 -operations, which equals 19,640,000 bytes or 19.64 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the DEEP\_BSON dataset into | -| | memory as a language-appropriate | -| | document types. For languages like | -| | C without a document type, the raw | -| | JSON string for each document should | -| | be used instead. Encode it to a | -| | BSON byte-string. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Decode the BSON byte-string to a | -| | language-appropriate document type. | -| | Repeat this 10,000 times. For | -| | languages like C without a document | -| | type, decode to extended JSON | -| | instead. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Full BSON Encoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance encoding documents with -top level key/value pairs involving the full range of BSON types. - -Dataset: The dataset, designated FULL\_BSON (disk file 'full\_bson.json'), -will be synthetically generated and consist of an extended JSON document with -a single \_id key with an object ID value plus 6 each of the following types: -string, double, Int64, Int32, boolean, minkey, maxkey, array, binary data, UTC -datetime, regular expression, Javascript code, Javascript code with context, -and timestamp. (91 total keys.) Keys (other than \_id) will be random ASCII -strings of length 8. Strings values will be random ASCII strings with length -80. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (5734 bytes) times 10,000 -operations, which equals 57,340,000 bytes or 57.34 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the FULL\_BSON dataset into | -| | memory as a language-appropriate | -| | document type. For languages like | -| | C without a document type, the raw | -| | JSON string for each document should | -| | be used instead. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Encode the FULL\_BSON document to a | -| | BSON byte-string. Repeat this 10,000 | -| | times. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Full BSON Decoding -~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance decoding documents with -top level key/value pairs involving the full range of BSON types. - -Dataset: The dataset, designated FULL\_BSON, will be synthetically -generated and consist of an extended JSON document with a single -\_id key with an object ID value plus 6 each of the following types: -string, double, Int64, Int32, boolean, minkey, maxkey, array, binary -data, UTC datetime, regular expression, Javascript code, Javascript code -with context, and timestamp. (91 total keys.) Keys (other than \_id) -will be random ASCII strings of length 8. Strings values will be random -ASCII strings with length 80. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (5734 bytes) times 10,000 -operations, which equals 57,340,000 bytes or 57.34 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Load the FULL\_BSON dataset into | -| | memory as a language-appropriate | -| | document types. For languages like | -| | C without a document type, the raw | -| | JSON string for each document should | -| | be used instead. Encode it to a | -| | BSON byte-string. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Decode the BSON byte-string to a | -| | language-appropriate document type. | -| | Repeat this 10,000 times. For | -| | languages like C without a document | -| | type, decode to extended JSON | -| | instead. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Single-Doc Benchmarks ---------------------- - -Datasets are in the 'single\_and\_multi\_document' tarball. - -Single-doc tests focus on single-document read and write operations. -They are designed to give insights into the efficiency of the driver's -implementation of the basic wire protocol. - -The data will be stored as strict JSON with no extended types. - -Single-doc micro-benchmarks include: - -- Run command -- Find one by ID -- Small doc insertOne -- Large doc insertOne - -Run command -~~~~~~~~~~~ - -Summary: This benchmark tests driver performance sending a command to -the database and reading a response. - -Dataset: n/a - -Dataset size: While there is no external dataset, for score calculation -purposes use 160,000 bytes (10,000 x the size of a BSON {ismaster:true} -command). - -*N.B. We use {ismaster:true} rather than {ismaster:1} -to ensure a consistent command size.* - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Construct whatever | -| | language-appropriate objects | -| | (Database, etc.) would be required | -| | to send a command. | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Run the command {ismaster:true} | -| | 10,000 times, reading (and | -| | discarding) the result each time. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | n/a | -+--------------------------------------+--------------------------------------+ - -Find one by ID -~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance sending an indexed -query to the database and reading a single document in response. - -Dataset: The dataset, designated TWEET (disk file 'tweet.json'), consists of -a sample tweet stored as strict JSON. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (1622 bytes) times 10,000 -operations, which equals 16,220,000 bytes or 16.22 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | TWEET document into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). Construct a | -| | Collection object for the 'corpus' | -| | collection to use for querying. | -| | Insert the document 10,000 times to | -| | the 'perftest' database in the | -| | 'corpus' collection using sequential | -| | \_id values. (1 to 10,000) | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | For each of the 10,000 | -| | sequential \_id numbers, issue a | -| | find command for that \_id on the | -| | 'corpus' collection and retrieve the | -| | single-document result. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Small doc insertOne -~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance inserting a single, -small document to the database. - -Dataset: The dataset, designated SMALL\_DOC (disk file 'small\_doc.json'), -consists of a JSON document with an encoded length of approximately 250 bytes. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (275 bytes) times 10,000 -operations, which equals 2,750,000 bytes or 2.75 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | SMALL\_DOC dataset into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the 'corpus' collection. | -| | Create an empty 'corpus' collection | -| | with the 'create' command. | -| | Construct a Collection object for | -| | the 'corpus' collection to use for | -| | insertion. | -+--------------------------------------+--------------------------------------+ -| Do task | Insert the document with the | -| | insertOne CRUD method. | -| | DO NOT manually add an \_id field; | -| | leave it to the driver or | -| | database. Repeat this 10,000 times. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Large doc insertOne -~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance inserting a single, -large document to the database. - -Dataset: The dataset, designated LARGE\_DOC (disk file 'large\_doc.json'), -consists of a JSON document with an encoded length of approximately 2,500,000 -bytes. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (2,731,089 bytes) times 10 -operations, which equals 27,310,890 bytes or 27.31 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | LARGE\_DOC dataset into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the 'corpus' collection. | -| | Create an empty 'corpus' collection | -| | with the 'create' command. | -| | Construct a Collection object for | -| | the 'corpus' collection to use for | -| | insertion. | -+--------------------------------------+--------------------------------------+ -| Do task | Insert the document with the | -| | insertOne CRUD method. | -| | DO NOT manually add an \_id field; | -| | leave it to the driver or database. | -| | Repeat this 10 times. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Multi-Doc Benchmarks --------------------- - -Datasets are in the 'single\_and\_multi\_document' tarball. - -Multi-doc benchmarks focus on multiple-document read and write -operations. They are designed to give insight into the efficiency of -the driver's implementation of bulk/batch operations such as bulk writes -and cursor reads. - -Multi-doc micro-benchmarks include: - -- Find many and empty the cursor -- Small doc bulk insert -- Large doc bulk insert -- GridFS upload -- GridFS download - -Find many and empty the cursor -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance retrieving multiple -documents from a query. - -Dataset: The dataset, designated TWEET consists of a sample tweet -stored as strict JSON. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (1622 bytes) times 10,000 -operations, which equals 16,220,000 bytes or 16.22 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | TWEET dataset into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). Construct a | -| | Collection object for the 'corpus' | -| | collection to use for | -| | querying. Insert the document 10,000 | -| | times to the 'perftest' database in | -| | the 'corpus' collection. (Let the | -| | driver generate \_ids). | -+--------------------------------------+--------------------------------------+ -| Before task | n/a | -+--------------------------------------+--------------------------------------+ -| Do task | Issue a find command on the 'corpus' | -| | collection with an empty filter | -| | expression. Retrieve (and discard) | -| | all documents from the cursor. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Small doc bulk insert -~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance inserting multiple, -small documents to the database. - -Dataset: The dataset, designated SMALL\_DOC consists of a JSON document -with an encoded length of approximately 250 bytes. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (275 bytes) times 10,000 -operations, which equals 2,750,000 bytes or 2.75 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | SMALL\_DOC dataset into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the 'corpus' collection. Create | -| | an empty 'corpus' collection with | -| | the 'create' command. Construct a | -| | Collection object for the 'corpus' | -| | collection to use for insertion. | -+--------------------------------------+--------------------------------------+ -| Do task | Do an ordered 'insert\_many' with | -| | 10,000 copies of the document. | -| | DO NOT manually add an \_id field; | -| | leave it to the driver or database. | -| | | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Large doc bulk insert -~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance inserting multiple, -large documents to the database. - -Dataset: The dataset, designated LARGE\_DOC consists of a JSON document -with an encoded length of approximately 2,500,000 bytes. - -Dataset size: For score purposes, the dataset size for a task is the -size of the single-document source file (2,731,089 bytes) times 10 -operations, which equals 27,310,890 bytes or 27.31 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. Drop | -| | the 'perftest' database. Load the | -| | LARGE\_DOC dataset into memory as a | -| | language-appropriate document type | -| | (or JSON string for C). | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the 'corpus' collection. Create | -| | an empty 'corpus' collection with | -| | the 'create' command. Construct a | -| | Collection object for the 'corpus' | -| | collection to use for insertion. | -+--------------------------------------+--------------------------------------+ -| Do task | Do an ordered 'insert\_many' with 10 | -| | copies of the document. | -| | DO NOT manually add an \_id field; | -| | leave it to the driver or database. | -| | | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -GridFS upload -~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance uploading a GridFS file -from memory. - -Dataset: The dataset, designated GRIDFS\_LARGE (disk file -'gridfs\_large.bin'), consists of a single file containing about 50 MB of -random data. We use a large file to ensure multiple database round-trips even -if chunks are are sent in batches. - -Dataset size: For score purposes, the dataset size for a task is the -size of the source file (52,428,800 bytes) times 1 operation or 52.43 -MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. Load | -| | the GRIDFS\_LARGE file as a string | -| | or other language-appropriate type | -| | for binary octet data. | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the default GridFS bucket. | -| | Insert a 1-byte file into the | -| | bucket. (This ensures the bucket | -| | collections and indices have been | -| | created.) | -| | Construct | -| | a GridFSBucket object to use for | -| | uploads. | -+--------------------------------------+--------------------------------------+ -| Do task | Upload the GRIDFS\_LARGE data as a | -| | GridFS file. Use whatever upload | -| | API is most natural for each | -| | language (e.g. | -| | open\_upload\_stream(), write the | -| | data to the stream and close the | -| | stream). | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -GridFS download -~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance downloading a GridFS -file to memory. - -Dataset: The dataset, designated GRIDFS\_LARGE, consists of a single -file containing about 50 MB of random data. We use a large file to -ensure multiple database round-trips even if chunks are are sent in -batches. - -Dataset size: For score purposes, the dataset size for a task is the -size of the source file (52,428,800 bytes) times 1 operation or 52.43 -MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. | -| | Upload the GRIDFS\_LARGE file to | -| | the default gridFS bucket with the | -| | name "gridfstest". Record the | -| | \_id of the uploaded file. | -+--------------------------------------+--------------------------------------+ -| Before task | Construct a GridFSBucket object to | -| | use for downloads. | -+--------------------------------------+--------------------------------------+ -| Do task | Download the "gridfstest" file by | -| | its \_id. Use whatever download API | -| | is most natural for each language | -| | (e.g. open\_download\_stream(), read | -| | from the stream into a variable). | -| | Discard the downloaded data. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Parallel --------- - -Datasets are in the 'parallel' tarball. - -Parallel tests simulate ETL operations from disk to database or -vice-versa. They are designed to be implemented using a language's -preferred approach to concurrency and thus stress how drivers handle -concurrency. These intentionally involve overhead above and beyond the -driver itself to simulate -- however loosely -- the sort of "real-world" -pressures that a drivers would be under during concurrent operation. - -They are intended for directional indication of which languages perform -best for this sort of pseudo-real-world activity, but are not intended -to represent real-world performance claims. - -Drivers teams are expected to treat these as a competitive "shoot-out" -to surface optimal ETL patterns for each language (e.g. multi-thread, -multi-process, asynchronous I/O, etc.). - -Parallel micro-benchmarks include: - -- LDJSON multi-file import -- LDJSON multi-file export -- GridFS multi-file upload -- GridFS multi-file download - -LDJSON multi-file import -~~~~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance importing documents -from a set of LDJSON files. - -Dataset: The dataset, designated LDJSON\_MULTI (disk directory -'ldjson\_multi'), consists of 100 LDJSON files, each containing 5,000 JSON -documents. Each document should be about 1000 bytes. - -Dataset size: For score purposes, the dataset size for a task is the -total size of all source files: 565,000,000 bytes or 565 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. | -| | | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the 'corpus' collection. | -| | Create an empty 'corpus' collection | -| | with the 'create' command. | -+--------------------------------------+--------------------------------------+ -| Do task | Do an unordered insert of all | -| | 500,000 documents in the dataset | -| | into the 'corpus' collection as fast | -| | as possible. Data must be loaded | -| | from disk during this phase. | -| | Concurrency is encouraged. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -LDJSON multi-file export -~~~~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance exporting documents to -a set of LDJSON files. - -Dataset: The dataset, designated LDJSON\_MULTI, consists of 100 LDJSON -files, each containing 5,000 JSON documents. Each document should be -about 1000 bytes. - -Dataset size: For score purposes, the dataset size for a task is the -total size of all source files: 565,000,000 bytes or 565 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. Drop | -| | the 'corpus' collection. Do an | -| | unordered insert of all 500,000 | -| | documents in the dataset into the | -| | 'corpus' collection. | -| | | -+--------------------------------------+--------------------------------------+ -| Before task | Construct whatever objects, threads, | -| | etc. are required for exporting the | -| | dataset. | -| | | -+--------------------------------------+--------------------------------------+ -| Do task | Dump all 500,000 documents in the | -| | dataset into 100 LDJSON files of | -| | 5,000 documents each as fast as | -| | possible. Data must be completely | -| | written/flushed to disk during this | -| | phase. Concurrency is encouraged. | -| | The order and distribution of | -| | documents across files does not need | -| | to match the original LDJSON\_MULTI | -| | files. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -GridFS multi-file upload -~~~~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance uploading files from -disk to GridFS. - -Dataset: The dataset, designated GRIDFS\_MULTI (disk directory -'gridfs\_multi'), consists of 50 files, each of 5MB. This file size -corresponds roughly to the output of a (slightly dated) digital camera. Thus -the task approximates uploading 50 "photos". - -Dataset size: For score purposes, the dataset size for a task is the -total size of all source files: 262,144,000 bytes or 262.144 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ -| Before task | Drop the default GridFS bucket in | -| | the 'perftest' database. Construct | -| | a GridFSBucket object for the | -| | default bucket in 'perftest' to use | -| | for uploads. Insert a 1-byte file | -| | into the bucket (to initialize | -| | indexes). | -+--------------------------------------+--------------------------------------+ -| Do task | Upload all 50 files in the | -| | GRIDFS\_MULTI dataset (reading each | -| | from disk). Concurrency is | -| | encouraged. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -GridFS multi-file download -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Summary: This benchmark tests driver performance downloading files from -GridFS to disk. - -Dataset: The dataset, designated GRIDFS\_MULTI, consists of 50 files, -each of 5MB. This file size corresponds roughly to the output of a -(slightly dated) digital camera. Thus the task approximates downloading -50 "photos". - -Dataset size: For score purposes, the dataset size for a task is the -total size of all source files: 262,144,000 bytes or 262.144 MB. - -Phases: - -+--------------------------------------+--------------------------------------+ -| Setup | Construct a MongoClient object. | -| | Drop the 'perftest' database. | -| | Construct a temporary directory for | -| | holding downloads. Drop the default | -| | GridFS bucket in the 'perftest' | -| | database. Upload the 50 file | -| | dataset to the default GridFS bucket | -| | in 'perftest'. | -+--------------------------------------+--------------------------------------+ -| Before task | Delete all files in the temporary | -| | folder for downloads. Construct a | -| | GridFSBucket object to use for | -| | downloads from the default bucket in | -| | 'perftest'. | -+--------------------------------------+--------------------------------------+ -| Do task | Download all 50 files in the | -| | GRIDFS\_MULTI dataset, saving each | -| | to a file in the temporary folder | -| | for downloads. Data must be | -| | completely written/flushed to disk | -| | during this phase. Concurrency is | -| | encouraged. | -+--------------------------------------+--------------------------------------+ -| After task | n/a | -+--------------------------------------+--------------------------------------+ -| Teardown | Drop the 'perftest' database. | -+--------------------------------------+--------------------------------------+ - -Composite score calculation -=========================== - -Every micro-benchmark has a score equal to the 50th percentile (median) -of sampled timings, expressed as Megabytes (1,000,000 bytes) per second -where the micro-benchmark "database size" given in each section above is -divided by the 50th percentile of measured wall-clock times. - -From these micro-benchmarks, the following composite scores must be -calculated: - -+--------------------------------------+--------------------------------------+ -| Composite name | Compositing formula | -+--------------------------------------+--------------------------------------+ -| BSONBench | Average of all BSON micro-benchmarks | -+--------------------------------------+--------------------------------------+ -| SingleBench | Average of all Single-doc | -| | micro-benchmarks, except "Run | -| | Command" | -+--------------------------------------+--------------------------------------+ -| MultiBench | Average of all Multi-doc | -| | micro-benchmarks | -+--------------------------------------+--------------------------------------+ -| ParallelBench | Average of all Parallel | -| | micro-benchmarks | -+--------------------------------------+--------------------------------------+ -| ReadBench | Average of "Find one", "Find many | -| | and empty cursor", "GridFS | -| | download", "LDJSON multi-file | -| | export", and "GridFS multi-file | -| | download" microbenchmarks | -+--------------------------------------+--------------------------------------+ -| WriteBench | Average of "Small doc insertOne", | -| | "Large doc insertOne", "Small doc | -| | bulk insert", "Large doc bulk | -| | insert", "GridFS upload", "LDJSON | -| | multi-file import", and "GridFS | -| | multi-file upload" micro-benchmarks | -+--------------------------------------+--------------------------------------+ -| DriverBench | Average of ReadBench and WriteBench | -+--------------------------------------+--------------------------------------+ - -At least for this first DriverBench version, scores are combined with -simple averages. In addition, the BSONBench scores do not factor into -the overall DriverBench scores, as encoding and decoding are inherent in -all other tasks. - -Benchmark platform, configuration and environments -================================================== - -Benchmark Client ----------------- - -TBD: spec Amazon instance size; describe in general terms how language -clients will be run independently; same AWS zone as server - -All operations must be run with write concern "w:1". - -Benchmark Server ----------------- - -TBD: spec Amazon instance size; describe configuration (e.g. no auth, -journal, pre-alloc sizes?, WT with compression to minimize disk I/O -impact?); same AWS zone as client - -Score Server ------------- - -TBD: spec system to hold scores over time - -Datasets --------- - -TBD: generated datasets should be park in S3 or somewhere for retrieval -by URL - -FAQ -=== - -Question? - -Answer. - -Change log -========== - -V1.3 (Aug 13, 2016) - -- Update corpus files to allow much greater compression of data -- Updated LDJSON corpus size to reflect revisions to the test data -- Published data files on GitHub and updated instructions on how to - find datasets -- RunCommand and query benchmark can create collection objects during - setup rather than before task. (No change on actual benchmark.) - -v1.2 (Jan 6, 2016) - -- Clarify that 'bulk insert' means 'insert\_many' -- Clarify that "create a collection" means using the 'create' command -- Add omitted "upload files" step to setup for GridFS multi-file - download; also clarify that steps should be using the default bucket - in the 'perftest' database - -v1.1 (Dec 23, 2015) - -- Rename benchmark names away from MMA/weight class names -- Split BSON encoding and decoding micro-benchmarks -- Rename BSON micro-benchmarks to better match dataset names -- Move "Run Command" micro-benchmark out of composite -- Reduced amount of data held in memory and sent to/from the server to - decrease memory pressure and increase number of iterations in a - reasonable time (e.g. file sizes and number of documents in certain - datasets changed) -- Create empty collections/indexes during the 'before' phase when - appropriate -- Updated data set sizes to account for changes in the source file - structure/size +.. note:: + This specification has been converted to Markdown and renamed to + `benchmarking.md `_. diff --git a/source/benchmarking/data/extended_bson.tgz b/source/benchmarking/data/extended_bson.tgz index aec31638c6..1e0950cec3 100644 Binary files a/source/benchmarking/data/extended_bson.tgz and b/source/benchmarking/data/extended_bson.tgz differ diff --git a/source/benchmarking/data/extended_bson_legacy.tgz b/source/benchmarking/data/extended_bson_legacy.tgz new file mode 100644 index 0000000000..aec31638c6 Binary files /dev/null and b/source/benchmarking/data/extended_bson_legacy.tgz differ diff --git a/source/benchmarking/odm-benchmarking.md b/source/benchmarking/odm-benchmarking.md new file mode 100644 index 0000000000..41103e10c7 --- /dev/null +++ b/source/benchmarking/odm-benchmarking.md @@ -0,0 +1,397 @@ +# ODM Performance Benchmarking + +- Version: 1.0 +- Status: In progress +- Minimum Server Version: N/A + +## Abstract + +This document describes a standard benchmarking suite for MongoDB ODMs (Object Document Mappers). Much of this +document's structure and content is taken from the existing MongoDB driver benchmarking suite for consistency. + +## Overview + +### Name and purpose + +ODM performance will be measured by the MongoDB ODM Performance Benchmark. It will provide both "horizontal" insights +into how individual ODM performance evolves over time and two types of "vertical" insights: the relative performance of +different ODMs, and the relative performance of ODMs and their associated language drivers. + +We expect substantial performance differences between ODMs based on both their language families (e.g. static vs. +dynamic or compiled vs. virtual-machine-based) as well as their inherent design (e.g. web frameworks such as Django vs. +application-agnostic such as Mongoose). Within families of ODMs that use similar design and language families, these +comparisons could be used to identify potential areas of performance improvement. + +### Task Hierarchy + +The benchmark suite consists of two groups of small, independent benchmarks. This allows us to better isolate areas +within ODMs that are faster or slower. + +- Flat models -- reading and writing flat models of various sizes, to explore basic operation efficiency +- Nested models -- reading and writing nested models of various sizes, to explore basic operation efficiency for complex + data + +The suite is intentionally kept small for several reasons: + +- ODM feature sets vary significantly across libraries, limiting the number of benchmarks that can be run across the + entire collection of extant ODMs. +- Several popular MongoDB ODMs are actively maintained by third-parties, such as Mongoose. By limiting the benchmarking + suite to a minimal set of representative tests that are easy to implement, we encourage adoption of the suite by + these third-party maintainers. + +### Measurement + +In addition to latency data, all benchmark tasks will be measured in terms of "megabytes/second" (MB/s) of documents +processed, with higher scores being better. (In this document, "megabyte" refers to the SI decimal unit, i.e. 1,000,000 +bytes.) This makes cross-benchmark comparisons easier. + +To avoid various types of measurement skew, tasks will be measured over several iterations. Each iteration will have a +number of operations performed per iteration that depends on the task being benchmarked. The final score for a task will +be the median score of the iterations. A range of percentiles will also be recorded for diagnostic analysis. + +### Data sets + +Data sets will vary by task. In most cases, data sets will be synthetic line-delimited JSON files to be constructed by +the ODM being benchmarked into the appropriate model. Some tasks will require additional modifications to these +constructed models, such as adding generated ObjectIds. See the +[Benchmark task definitions](#benchmark-task-definitions) section for details. + +### Versioning + +The MongoDB ODM Performance Benchmark will have vX.Y versioning. Minor updates and clarifications will increment "Y" and +MUST have little impact on score comparison. Major changes, such as task modifications, MongoDB version tested against, +or hardware used, MUST increment "X" to indicate that older version scores are unlikely to be comparable. + +## Benchmark execution phases and measurement + +All benchmark tasks will be conducted via a number of iterations. Each iteration will be timed and will generally +include a large number of individual ODM operations. + +The measurement is broken up this way to better isolate the benchmark from external volatility. If we consider the +problem of benchmarking an operation over many iterations, such as 100,000 model insertions, we want to avoid two +extreme forms of measurement: + +- measuring a single insertion 100,000 times -- in this case, the timing code is likely to be a greater proportion of + executed code, which could routinely evict the insertion code from CPU caches or mislead a JIT optimizer and throw + off results +- measuring 100,000 insertions one time -- in this case, the longer the timer runs, the higher the likelihood that an + external event occurs that affects the time of the run + +Therefore, we choose a middle ground: + +- measuring the same 10,000 insertions over 10 iterations -- each timing run includes enough operations that insertion + code dominates timing code; unusual system events are likely to affect only a fraction of the 10 timing measurements + +With 10 timings of inserting the same 10,000 models, we build up a statistical distribution of the operation timing, +allowing a more robust estimate of performance than a single measurement. (In practice, the number of iterations could +exceed 10, but 10 is a reasonable minimum goal.) + +Because a timing distribution is bounded by zero on one side, taking the mean would allow large positive outlier +measurements to skew the result substantially. Therefore, for the benchmark score, we use the median timing measurement, +which is robust in the face of outliers. + +Each benchmark is structured into discrete setup/execute/teardown phases. Phases are as follows, with specific details +given in a subsequent section: + +- setup -- (ONCE PER TASK) something to do once before any benchmarking, e.g. construct a model object, load test data, + insert data into a collection, etc. +- before operation -- (ONCE PER ITERATION) something to do before every task iteration, e.g. drop a collection, or + reload test data (if the test run modifies it), etc. +- do operation -- (ONCE PER ITERATION) smallest amount of code necessary to execute the task; e.g. insert 10,000 models + one by one into the database, or retrieve 10,000 models of test data from the database, etc. +- after operation -- (ONCE PER ITERATION) something to do after every task iteration (if necessary) +- teardown -- (ONCE PER TASK) something done once after all benchmarking is complete (if necessary); e.g. drop the test + database + +The wall-clock execution time of each "do operation" phase will be recorded. We use wall clock time to model user +experience and as a lowest-common denominator across ODMs. Iteration timing SHOULD be done with a high-resolution +monotonic timer (or best language approximation). + +Unless otherwise specified, the number of iterations to measure per task is variable: + +- iterations MUST loop for at least 30 seconds cumulative execution time +- once this 30 second minimum execution time is reached, iterations SHOULD stop after at least 10 iterations or 1 minute + cumulative execution time, whichever is shorter + +This balances measurement stability with a timing cap to ensure all tasks can complete in a reasonable time. + +For each task, the 10th, 25th, 50th, 75th, 90th, 95th, 98th and 99th percentiles will be recorded using the following +algorithm: + +- Given a 0-indexed array `A` of `N` iteration wall clock times +- Sort the array into ascending order (i.e. shortest time first) +- Let the index `i` for percentile `p` in the range [1,100] be defined as: `i = int(N * p / 100) - 1` + +*N.B. This is the [Nearest Rank](https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method) algorithm, chosen for +its utter simplicity given that it needs to be implemented identically across a wide variety of ODMs and languages.* + +The 50th percentile (i.e. the median) will be used for score composition. Other percentiles will be stored for +visualizations and analysis. + +Each task will have defined for it an associated size in megabytes (MB). This size will be calculated using the task's +dataset size and the number of documents processed per iteration. The benchmarking score for each task will be the task +size in MB divided by the median wall clock time. + +## Benchmark task definitions + +Datasets are available in the `odm-data` directory adjacent to this spec. + +Note: The term "LDJSON" means "line-delimited JSON", which should be understood to mean a collection of UTF-8 encoded +JSON documents (without embedded CR or LF characters), separated by a single LF character. (Some Internet definition of +line-delimited JSON use CRLF delimiters, but this benchmark uses only LF.) + +### Flat models + +Datasets are in the `flat_models.tgz` tarball. + +Flat model tests focus on flatly-structured model reads and writes across data sizes. They are designed to give insights +into the efficiency of the ODM's implementation of basic data operations. + +The data will be stored as strict JSON with no extended types. These JSON representations MUST be converted into +equivalent models as part of each benchmark task. + +Flat model benchmark tasks include: + +- Small model creation +- Small model update +- Small model find by filter +- Small model find foreign key by filter (if joins are supported) +- Large model creation +- Large model update + +#### Small model creation + +Summary: This benchmark tests ODM performance creating a single small model. + +Dataset: The dataset (SMALL_DOC) is contained within `small_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 250 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `small_doc` source file (250 bytes) times 10,000 +operations, which equals 2,250,000 bytes or 2.5 MB. + +This benchmark uses a comparable dataset to the driver `small doc insertOne` benchmark, allowing for direct comparisons. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the SMALL_DOC dataset into memory. | +| Before task | n/a. | +| Do task | Create an ODM-appropriate model instance for the SMALL_DOC document and save it to the database. Repeat this 10,000 times. | +| After task | Drop the collection associated with the SMALL_DOC model. | +| Teardown | n/a. | + +#### Small model update + +Summary: This benchmark tests ODM performance updating fields on a single small model. + +Dataset: The dataset (SMALL_DOC) is contained within `small_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 250 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `updated_value` string file (13 bytes) times +10,000 operations, which equals 130,000 bytes or 130 KB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the SMALL_DOC dataset into memory as an ODM-appropriate model object. Save 10,000 instances into the database. | +| Before task | n/a. | +| Do task | Update the `field1` field for each instance of the model to equal `updated_value` in an ODM-appropriate manner. | +| After task | n/a. | +| Teardown | Drop the collection associated with the SMALL_DOC model. | + +#### Small model find by filter + +Summary: This benchmark tests ODM performance finding documents using a basic filter. + +Dataset: The dataset (SMALL_DOC) is contained within `small_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 250 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `small_doc` source file (250 bytes) times 10,000 +operations, which equals 2,250,000 bytes or 2.5 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Load the SMALL_DOC dataset into memory as an ODM-appropriate model object. Insert 10,000 instances into the database, saving the `_id` field for each into a list. | +| Before task | n/a. | +| Do task | For each of the 10,000 `_id` values, perform a filter operation to find the corresponding SMALL_DOC model. | +| After task | n/a. | +| Teardown | Drop the collection associated with the SMALL_DOC model. | + +#### Small model find foreign key by filter + +Summary: This benchmark tests ODM performance finding documents by foreign keys. This benchmark MUST only be run by ODMs +that support join ($lookup) operations. + +Dataset: The dataset (SMALL_DOC) is contained within `small_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 250 bytes. An additional model (FOREIGN_KEY) representing the foreign key, +consisting of only a string field called `name`, MUST also be created. + +Dataset size: For scoring purposes, the dataset size is the size of the `small_doc` source file (250 bytes) times 10,000 +operations, which equals 2,250,000 bytes or 2.5 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the SMALL_DOC dataset into memory as an ODM-appropriate model object. For each SMALL_DOC model, create and assign a FOREIGN_KEY instance to the `field_fk` field. Insert 10,000 instances of both models into the database, saving the inserted `_id` field for each FOREIGN_KEY into a list. | +| Before task | n/a. | +| Do task | For each of the 10,000 FOREIGN_KEY `_id` values, perform a filter operation in an ODM-appropriate manner to find the corresponding SMALL_DOC model. | +| After task | n/a. | +| Teardown | Drop the collections associated with the SMALL_DOC and FOREIGN_KEY models. | + +#### Large model creation + +Summary: This benchmark tests ODM performance creating a single large model. + +Dataset: The dataset (LARGE_DOC) is contained within `large_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 3.2 MB. + +Dataset size: For scoring purposes, the dataset size is the size of the `large_doc` source file (3,226,020 bytes) times +10,000 operations, which equals 32,260,200,000 bytes or 32.2 GB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the LARGE_DOC dataset into memory. | +| Before task | n/a. | +| Do task | Create an ODM-appropriate model instance for the LARGE_DOC document and save it to the database. Repeat this 10,000 times. | +| After task | Drop the collection associated with the LARGE_DOC model. | +| Teardown | n/a. | + +#### Large model update + +Summary: This benchmark tests ODM performance updating fields on a single large model. + +Dataset: The dataset (LARGE_DOC) is contained within `large_doc.json` and consists of a sample document stored as strict +JSON with an encoded length of approximately 3.2 MB. + +Dataset size: For scoring purposes, the dataset size is the size of the `updated_value` string file (13 bytes) times +10,000 operations, which equals 130,000 bytes or 130 KB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the LARGE_DOC dataset into memory as an ODM-appropriate model object. Save 10,000 instances into the database. | +| Before task | n/a. | +| Do task | Update the `field1` field for each instance of the model to `updated_value` in an ODM-appropriate manner. | +| After task | Drop the collection associated with the LARGE_DOC model. | +| Teardown | n/a. | + +### Nested models + +Datasets are in the `nested_models.tgz` tarball. + +Nested model tests focus performing reads and writes on models containing nested (embedded) documents. They are designed +to give insights into the efficiency of operations on the more complex data structures enabled by the document model. + +The data will be stored as strict JSON with no extended types. These JSON representations MUST be converted into +equivalent ODM models as part of each benchmark task. + +Nested model benchmark tasks include: + +- Large model creation +- Large model update nested field +- Large model find nested field by filter +- Large model find nested array field by filter + +#### Large model creation + +Summary: This benchmark tests ODM performance creating a single large nested model. + +Dataset: The dataset (LARGE_DOC_NESTED) is contained within `large_doc_nested.json` and consists of a sample document +stored as strict JSON with an encoded length of approximately 8,000 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `large_doc_nested` source file (8,000 bytes) +times 10,000 operations, which equals 80,000,000 bytes or 80 MB. + +| Phase | Description | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the LARGE_DOC_NESTED dataset into memory. | +| Before task | n/a. | +| Do task | Create an ODM-appropriate model instance for the LARGE_DOC_NESTED document and save it to the database. Repeat this 10,000 times. | +| After task | Drop the collection associated with the LARGE_DOC_NESTED model. | +| Teardown | n/a. | + +#### Large model update nested + +Summary: This benchmark tests ODM performance updating nested fields on a single large model. + +Dataset: The dataset (LARGE_DOC_NESTED) is contained within `large_doc_nested.json` and consists of a sample document +stored as strict JSON with an encoded length of approximately 8,000 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `updated_value` string file (13 bytes) times +10,000 operations, which equals 130,000 bytes or 130 KB. + +| Phase | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the LARGE_DOC_NESTED dataset into memory as an ODM-appropriate model object. Save 10,000 instances into the database. | +| Before task | n/a. | +| Do task | Update the value of the `embedded_str_doc_1.field1` field to `updated_value` in an ODM-appropriate manner for each instance of the model. | +| After task | Drop the collection associated with the LARGE_DOC_NESTED model. | +| Teardown | n/a. | + +#### Large nested model find nested by filter + +Summary: This benchmark tests ODM performance finding nested documents using a basic filter. + +Dataset: The dataset (LARGE_DOC_NESTED) is contained within `large_doc_nested.json` and consists of a sample document +stored as strict JSON with an encoded length of approximately 8,000 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `large_doc_nested` source file (8,000 bytes) +times 10,000 operations, which equals 80,000,000 bytes or 80 MB. + +| Phase | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Setup | Load the LARGE_DOC_NESTED dataset into memory as an ODM-appropriate model object. Insert 10,000 instances into the database, saving the value of the `unique_id` field for each model's `embedded_str_doc_1` nested model into a list. | +| Before task | n/a. | +| Do task | For each of the 10,000 `embedded_str_doc_1.unique_id` values, perform a filter operation to search for the parent LARGE_DOC_NESTED model. | +| After task | n/a. | +| Teardown | Drop the collection associated with the LARGE_DOC_NESTED model. | + +#### Large nested model find nested array by filter + +Summary: This benchmark tests ODM performance finding nested document arrays using a basic filter. + +Dataset: The dataset (LARGE_DOC_NESTED) is contained within `large_doc_nested.json` and consists of a sample document +stored as strict JSON with an encoded length of approximately 8,000 bytes. + +Dataset size: For scoring purposes, the dataset size is the size of the `large_doc_nested` source file (8,000 bytes) +times 10,000 operations, which equals 80,000,000 bytes or 80 MB. + +| Phase | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Setup | Load the LARGE_DOC_NESTED dataset into memory as an ODM-appropriate model object. Insert 10,000 instances into the database, saving the value of the `unique_id` field for the first item in each model's `embedded_str_doc_array` nested model into a list. | +| Before task | n/a. | +| Do task | For each of the 10,000 `unique_id` values, perform a filter operation to search for the parent LARGE_DOC_NESTED model. | +| After task | n/a. | +| Teardown | Drop the collection associated with the LARGE_DOC_NESTED model. | + +## Benchmark platform, configuration and environments + +### Benchmark Client + +The benchmarks SHOULD be run with the most recent stable version of the ODM and the newest version of the driver it +supports. + +### Benchmark Server + +The MongoDB ODM Performance Benchmark MUST be run against a MongoDB replica set of size 1 patch-pinned to the latest +stable database version without authentication or SSL enabled. This database version SHOULD be updated as needed to +better differentiate between ODM performance changes and server performance changes. The Benchmark MUST be run on the +established internal performance distro for the sake of consistency. + +### Benchmark placement and scheduling + +The MongoDB ODM Performance Benchmark SHOULD be placed in one of two places. For first-party ODMs, the Benchmark SHOULD +be placed within the ODM's test directory as an independent test suite. For third-party ODMs, if the external +maintainers do not wish to have the Benchmark included as part of the in-repo test suite, it SHOULD be included in the +ODM performance testing repository created explicitly for this purpose. + +Due to the relatively long runtime of the benchmarks, including them as part of an automated suite that runs against +every PR is not recommended. Instead, scheduling benchmark runs on a regular cadence is the recommended method of +automating this suite of tests. + +## ODM-specific benchmarking + +As discussed earlier in this document, ODM feature sets vary significantly across libraries. Many ODMs have features +unique to them or their niche in the wider ecosystem, which makes specifying concrete benchmark test cases for every +possible API unfeasible. Instead, ODM authors SHOULD determine what mainline use cases of their library are not covered +by the benchmarks specified above and expand this testing suite with additional benchmarks to cover those areas. + +## Changelog + +- 2025-11-14: Release initial 1.0 version diff --git a/source/benchmarking/odm-data/flat_models.tgz b/source/benchmarking/odm-data/flat_models.tgz new file mode 100644 index 0000000000..b779c30b3c Binary files /dev/null and b/source/benchmarking/odm-data/flat_models.tgz differ diff --git a/source/benchmarking/odm-data/nested_models.tgz b/source/benchmarking/odm-data/nested_models.tgz new file mode 100644 index 0000000000..596b59e622 Binary files /dev/null and b/source/benchmarking/odm-data/nested_models.tgz differ diff --git a/source/bson-binary-encrypted/binary-encrypted.md b/source/bson-binary-encrypted/binary-encrypted.md new file mode 100644 index 0000000000..31dab4870d --- /dev/null +++ b/source/bson-binary-encrypted/binary-encrypted.md @@ -0,0 +1,144 @@ +# BSON Binary Encrypted + +- Status: Accepted +- Minimum Server Version: 4.2 + +______________________________________________________________________ + +## Abstract + +Client side encryption requires a new binary subtype to store (1) encrypted ciphertext with metadata, and (2) binary +markings indicating what values must be encrypted in a document. (1) is stored in the server, but (2) is only used in +the communication protocol between libmongocrypt and mongocryptd described in +[Driver Spec: Client Side Encryption Encryption](../client-side-encryption/client-side-encryption.md). + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +This spec introduces a new BSON binary subtype with value 6. The binary has multiple formats determined by the first +byte, but are all related to client side encryption. The first byte indicates the type and layout of the remaining data. + +All values are represented in little endian. The payload is generally optimized for storage size. The exception is the +intent-to-encrypt markings which are only used between libmongocrypt and mongocryptd and never persisted. + +```typescript +struct { + uint8 subtype; + [more data - see individual type definitions] +} +``` + +| | | | +| -------- | -------- | -------------------- | +| **Name** | **Type** | **Description** | +| subtype | uint8 | Type of blob format. | + +| | | | +| -------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| **Type** | **Name** | **Blob Description** | +| 0 | Intent-to-encrypt marking. | Contains unencrypted data that will be encrypted (by libmongocrypt) along with metadata describing how to encrypt it. | +| 1 | AEAD_AES_CBC_HMAC_SHA512 deterministic ciphertext. | The metadata and encrypted data for deterministic encrypted data. | +| 2 | AEAD_AES_CBC_HMAC_SHA512 randomized ciphertext. | The metadata and encrypted data for random encrypted data. | + +### Type 0: Intent-to-encrypt marking + +```typescript +struct { + uint8 subtype = 0; + [ bson ]; +} +``` + +bson is the raw bytes of the following BSON document: + +| | | | | +| -------- | ------------- | -------- | --------------------------------------------------------------------------------------------- | +| **Name** | **Long Name** | **Type** | **Description** | +| v | value | any | Value to encrypt. | +| a | algorithm | int32 | Encryption algorithm to use. Same as fle_blob_subtype: 1 for deterministic, 2 for randomized. | +| ki | keyId | UUID | Optional. Used to query the key vault by \_id. If omitted, then "ka" must be specified. | +| ka | keyAltName | string | Optional. Used to query the key vault by keyAltName. If omitted, then "ki" must be specified. | + +### Types 1 and 2: Ciphertext + +```typescript +struct { + uint8 subtype = (1 or 2); + uint8 key_uuid[16]; + uint8 original_bson_type; + uint8 ciphertext[ciphertext_length]; +} +``` + +| | | +| ------------------ | ------------------------------------------------------------------- | +| **Name** | **Description** | +| subtype | Type of blob format and encryption algorithm used. | +| key_uuid[16] | The value of \_id for the key used to decrypt the ciphertext. | +| original_bson_type | The byte representing the original BSON type of the encrypted data. | +| ciphertext[] | The encrypted ciphertext (includes IV prepended). | + +## Test Plan + +Covered in [Driver Spec: Client Side Encryption Encryption](../client-side-encryption/client-side-encryption.md). + +## Design Rationale + +### Why not use a new BSON type? + +An alternative to using a new binary subtype would be introducing a new BSON type. This would be a needless backwards +breaking change. Since FLE is largely a client side feature, it should be possible to store encrypted data in old +servers. + +Plus, encrypted ciphertext is inherently a binary blob. Packing metadata inside isolates all of the encryption related +data into one BSON value that can be treated as an opaque blob in most contexts. + +### Why not use separate BSON binary subtypes instead of a nested subtype? + +If we used separate subtypes, we'd need to reserve three (and possibly more in the future) of our 124 remaining +subtypes. + + + +### Why are intent-to-encrypt markings needed? + +Intent-to-encrypt markings provide a simple way for mongocryptd to communicate what values need to be encrypted to +libmongocrypt. Alternatively, mongocryptd could respond with a list of field paths. But field paths are difficult to +make unambiguous, and even the query language is not always consistent. + +### What happened to the "key vault alias"? + +In an earlier revision of this specification the notion of a "key vault alias". The key vault alias identified one of +possibly many key vaults that stored the key to decrypt the ciphertext. However, enforcing one key vault is a reasonable +restriction for users. Users can migrate from one key vault to another without ciphertext data including a key vault +alias. If we find a future need for multiple key vaults, we can easily introduce a new format with the fle_blob_subtype. + +Why distinguish between "deterministic" and "randomized" when they contain the same fields? + +Deterministic and randomized ciphertext supports different behavior. Deterministic ciphertext supports exact match +queries but randomized does not. + +### Why is the original BSON type not encrypted? + +Exposing the underlying BSON type gives some validation of the data that is encrypted. A JSONSchema on the server can +validate that the underlying encrypted BSON type is correct. + +## Reference Implementation + +libmongocrypt and mongocryptd will be the reference implementation of how BSON binary subtype 6 is used. + +## Security Implication + +It would be a very bad security flaw if intent-to-encrypt markings were confused with ciphertexts. This could lead to a +marking inadvertently being stored on a server – meaning that plaintext is stored where ciphertext should have been. + +Therefore, the leading byte of the BSON binary subtype distinguishes between marking and ciphertext. + +## Changelog + +- 2024-03-04: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter and create changelog. diff --git a/source/bson-binary-uuid/uuid.md b/source/bson-binary-uuid/uuid.md new file mode 100644 index 0000000000..3f1e886783 --- /dev/null +++ b/source/bson-binary-uuid/uuid.md @@ -0,0 +1,299 @@ +# BSON Binary UUID + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +The Java, C#, and Python drivers natively support platform types for UUID, all of which by default encode them to and +decode them from BSON binary subtype 3. However, each encode the bytes in a different order from the others. To improve +interoperability, BSON binary subtype 4 was introduced and defined the byte order according to +[RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.2), and a mechanism to configure each driver to encode UUIDs +this way was added to each driver. The legacy representation remained as the default for each driver. + +This specification moves MongoDB drivers further towards the standard UUID representation by requiring an application +relying on native UUID support to explicitly specify the representation it requires. + +Drivers that support native UUID types will additionally create helpers on their BsonBinary class that will aid in +conversion to and from the platform native UUID type. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**UUID** + +A Universally Unique IDentifier + +**BsonBinary** + +An object that wraps an instance of a BSON binary value + +### Naming Deviations + +All drivers MUST name operations, objects, and parameters as defined in the following sections. + +The following deviations are permitted: + +- Drivers can use the platform's name for a UUID. For instance, in C# the platform class is Guid, whereas in Java it is + UUID. +- Drivers can use a "to" prefix instead of an "as" prefix for the BsonBinary method names. + +### Explicit encoding and decoding + +Any driver with a native UUID type MUST add the following UuidRepresentation enumeration, and associated methods to its +BsonBinary (or equivalent) class: + +```typescript +/** + +enum UuidRepresentation { + + /** + * An unspecified representation of UUID. Essentially, this is the null + * representation value. This value is not required for languages that + * have better ways of indicating, or preventing use of, a null value. + */ + UNSPECIFIED("unspecified"), + + /** + * The canonical representation of UUID according to RFC 4122, + * section 4.1.2 + * + * It encodes as BSON binary subtype 4 + */ + STANDARD("standard"), + + /** + * The legacy representation of UUID used by the C# driver. + * + * In this representation the order of bytes 0-3 are reversed, the + * order of bytes 4-5 are reversed, and the order of bytes 6-7 are + * reversed. + * + * It encodes as BSON binary subtype 3 + */ + C_SHARP_LEGACY("csharpLegacy"), + + /** + * The legacy representation of UUID used by the Java driver. + * + * In this representation the order of bytes 0-7 are reversed, and the + * order of bytes 8-15 are reversed. + * + * It encodes as BSON binary subtype 3 + */ + JAVA_LEGACY("javaLegacy"), + + /** + * The legacy representation of UUID used by the Python driver. + * + * As with STANDARD, this representation conforms with RFC 4122, section + * 4.1.2 + * + * It encodes as BSON binary subtype 3 + */ + PYTHON_LEGACY("pythonLegacy") +} + +class BsonBinary { + /* + * Construct from a UUID using the standard UUID representation + * [Specification] This constructor SHOULD be included but MAY be + * omitted if it creates backwards compatibility issues + */ + constructor(Uuid uuid) + + /* + * Construct from a UUID using the given UUID representation. + * + * The representation must not be equal to UNSPECIFIED + */ + constructor(Uuid uuid, UuidRepresentation representation) + + /* + * Decode a subtype 4 binary to a UUID, erroring when the subtype is not 4. + */ + Uuid asUuid() + + /* + * Decode a subtype 3 or 4 to a UUID, according to the UUID + * representation, erroring when subtype does not match the + * representation. + */ + Uuid asUuid(UuidRepresentation representation) +} +``` + +### Implicit decoding and encoding + +A new driver for a language with a native UUID type MUST NOT implicitly encode from or decode to the native UUID type. +Rather, explicit conversion MUST be used as described in the previous section. + +Drivers that already do such implicit encoding and decoding SHOULD support a URI option, uuidRepresentation, which +controls the default behavior of the UUID codec. Alternatively, a driver MAY specify the UUID representation via global +state. + +| Value | Default? | Encode to | Decode subtype 4 to | Decode subtype 3 to | +| ------------ | -------- | ------------------------------------------------- | ------------------- | ------------------- | +| unspecified | yes | raise error | BsonBinary | BsonBinary | +| standard | no | BSON binary subtype 4 | native UUID | BsonBinary | +| csharpLegacy | no | BSON binary subtype 3 with C# legacy byte order | BsonBinary | native UUID | +| javaLegacy | no | BSON binary subtype 3 with Java legacy byte order | BsonBinary | native UUID | +| pythonLegacy | no | BSON binary subtype 3 with standard byte order | BsonBinary | native UUID | + +For scenarios where the application makes the choice (e.g. a POJO with a field of type UUID), or when serializers are +strongly typed and are constrained to always return values of a certain type, the driver will raise an exception in +cases where otherwise it would be required to decode to a different type (e.g. BsonBinary instead of UUID or vice +versa). + +Note also that none of the above applies when decoding to strictly typed maps, e.g. a `Map` like Java +or .NET's BsonDocument class. In those cases the driver is always decoding to BsonBinary, and applications would use the +asUuid methods to explicitly convert from BsonBinary to UUID. + +### Implementation Notes + +Since changing the default UUID representation can reasonably be considered a backwards-breaking change, drivers that +implement the full specification should stage implementation according to semantic versioning guidelines. Specifically, +support for this specification can be added to a minor release, but with several exceptions: + +The default UUID representation should be left as is (e.g. JAVA_LEGACY for the Java driver) rather than be changed to +UNSPECIFIED. In a subsequent major release, the default UUID representation can be changed to UNSPECIFIED (along with +appropriate documentation indicating the backwards-breaking change). Drivers MUST document this in a prior minor +release. + +## Test Plan + +The test plan consists of a series of prose tests. They all operate on the same UUID, with the String representation of +"00112233-4455-6677-8899-aabbccddeeff". + +### Explicit encoding + +1. Create a BsonBinary instance with the given UUID + - Assert that the BsonBinary instance's subtype is equal to 4 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" +2. Create a BsonBinary instance with the given UUID and UuidRepresentation equal to STANDARD + - Assert that the BsonBinary instance's subtype is equal to 4 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" +3. Create a BsonBinary instance with the given UUID and UuidRepresentation equal to JAVA_LEGACY + - Assert that the BsonBinary instance's subtype is equal to 3 and data equal to the hex-encoded string + "7766554433221100FFEEDDCCBBAA9988" +4. Create a BsonBinary instance with the given UUID and UuidRepresentation equal to CSHARP_LEGACY + - Assert that the BsonBinary instance's subtype is equal to 3 and data equal to the hex-encoded string + "33221100554477668899AABBCCDDEEFF" +5. Create a BsonBinary instance with the given UUID and UuidRepresentation equal to PYTHON_LEGACY + - Assert that the BsonBinary instance's subtype is equal to 3 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" +6. Create a BsonBinary instance with the given UUID and UuidRepresentation equal to UNSPECIFIED + - Assert that an error is raised + +### Explicit Decoding + +1. Create a BsonBinary instance with subtype equal to 4 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" + 1. Assert that a call to BsonBinary.asUuid() returns the given UUID + 2. Assert that a call to BsonBinary.asUuid(STANDARD) returns the given UUID + 3. Assert that a call to BsonBinary.asUuid(UNSPECIFIED) raises an error + 4. Assert that a call to BsonBinary.asUuid(JAVA_LEGACY) raises an error + 5. Assert that a call to BsonBinary.asUuid(CSHARP_LEGACY) raises an error + 6. Assert that a call to BsonBinary.asUuid(PYTHON_LEGACY) raises an error +2. Create a BsonBinary instance with subtype equal to 3 and data equal to the hex-encoded string + "7766554433221100FFEEDDCCBBAA9988" + 1. Assert that a call to BsonBinary.asUuid() raises an error + 2. Assert that a call to BsonBinary.asUuid(STANDARD) raised an error + 3. Assert that a call to BsonBinary.asUuid(UNSPECIFIED) raises an error + 4. Assert that a call to BsonBinary.asUuid(JAVA_LEGACY) returns the given UUID +3. Create a BsonBinary instance with subtype equal to 3 and data equal to the hex-encoded string + "33221100554477668899AABBCCDDEEFF" + 1. Assert that a call to BsonBinary.asUuid() raises an error + 2. Assert that a call to BsonBinary.asUuid(STANDARD) raised an error + 3. Assert that a call to BsonBinary.asUuid(UNSPECIFIED) raises an error + 4. Assert that a call to BsonBinary.asUuid(CSHARP_LEGACY) returns the given UUID +4. Create a BsonBinary instance with subtype equal to 3 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" + 1. Assert that a call to BsonBinary.asUuid() raises an error + 2. Assert that a call to BsonBinary.asUuid(STANDARD) raised an error + 3. Assert that a call to BsonBinary.asUuid(UNSPECIFIED) raises an error + 4. Assert that a call to BsonBinary.asUuid(PYTHON_LEGACY) returns the given UUID + +### Implicit encoding + +1. Set the uuidRepresentation of the client to "javaLegacy". Insert a document with an "\_id" key set to the given + native UUID value. + - Assert that the actual value inserted is a BSON binary with subtype 3 and data equal to the hex-encoded string + "7766554433221100FFEEDDCCBBAA9988" +2. Set the uuidRepresentation of the client to "charpLegacy". Insert a document with an "\_id" key set to the given + native UUID value. + - Assert that the actual value inserted is a BSON binary with subtype 3 and data equal to the hex-encoded string + "33221100554477668899AABBCCDDEEFF" +3. Set the uuidRepresentation of the client to "pythonLegacy". Insert a document with an "\_id" key set to the given + native UUID value. + - Assert that the actual value inserted is a BSON binary with subtype 3 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" +4. Set the uuidRepresentation of the client to "standard". Insert a document with an "\_id" key set to the given native + UUID value. + - Assert that the actual value inserted is a BSON binary with subtype 4 and data equal to the hex-encoded string + "00112233445566778899AABBCCDDEEFF" +5. Set the uuidRepresentation of the client to "unspecified". Insert a document with an "\_id" key set to the given + native UUID value. + - Assert that a BSON serialization exception is thrown + +### Implicit Decoding + +1. Set the uuidRepresentation of the client to "javaLegacy". Insert a document containing two fields. The "standard" + field should contain a BSON Binary created by creating a BsonBinary instance with the given UUID and the STANDARD + UuidRepresentation. The "legacy" field should contain a BSON Binary created by creating a BsonBinary instance with + the given UUID and the JAVA_LEGACY UuidRepresentation. Find the document. + + 1. Assert that the value of the "standard" field is of type BsonBinary and is equal to the inserted value. + 2. Assert that the value of the "legacy" field is of the native UUID type and is equal to the given UUID + + Repeat this test with the uuidRepresentation of the client set to "csharpLegacy" and "pythonLegacy". + +2. Set the uuidRepresentation of the client to "standard". Insert a document containing two fields. The "standard" field + should contain a BSON Binary created by creating a BsonBinary instance with the given UUID and the STANDARD + UuidRepresentation. The "legacy" field should contain a BSON Binary created by creating a BsonBinary instance with + the given UUID and the PYTHON_LEGACY UuidRepresentation. Find the document. + + 1. Assert that the value of the "standard" field is of the native UUID type and is equal to the given UUID + 2. Assert that the value of the "legacy" field is of type BsonBinary and is equal to the inserted value. + +3. Set the uuidRepresentation of the client to "unspecified". Insert a document containing two fields. The "standard" + field should contain a BSON Binary created by creating a BsonBinary instance with the given UUID and the STANDARD + UuidRepresentation. The "legacy" field should contain a BSON Binary created by creating a BsonBinary instance with + the given UUID and the PYTHON_LEGACY UuidRepresentation. Find the document. + + 1. Assert that the value of the "standard" field is of type BsonBinary and is equal to the inserted value + 2. Assert that the value of the "legacy" field is of type BsonBinary and is equal to the inserted value. + + Repeat this test with the uuidRepresentation of the client set to "csharpLegacy" and "pythonLegacy". + +Note: the assertions will be different in the release prior to the major release, to avoid breaking changes. Adjust +accordingly! + +## Q & A + +### What's the rationale for the deviations allowed by the specification? + +In short, the C# driver has existing behavior that make it infeasible to work the same as other drivers. + +The C# driver has a global serialization registry. Since it's global and not per-MongoClient, it's not feasible to +override the UUID representation on a per-MongoClient basis, since doing so would require a per-MongoClient registry. +Instead, the specification allows for a global override so that the C# driver can implement the specification. + +Additionally, the C# driver has an existing configuration parameter that controls the behavior of BSON readers and +writers at a level below the serializers. This configuration affects the semantics of the existing BsonBinary class in a +way that doesn't allow for the constructor(UUID) mentioned in the specification. For this reason, that constructor is +specified as optional. + +## Changelog + +- 2024-08-01: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter. diff --git a/source/bson-binary-vector/bson-binary-vector.md b/source/bson-binary-vector/bson-binary-vector.md new file mode 100644 index 0000000000..b398ffabf3 --- /dev/null +++ b/source/bson-binary-vector/bson-binary-vector.md @@ -0,0 +1,272 @@ +# BSON Binary Subtype 9 - Vector + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This document describes the subtype of the Binary BSON type used for efficient storage and retrieval of vectors. Vectors +here refer to densely packed arrays of numbers, all of the same type. + +## Motivation + +These representations correspond to the numeric types supported by popular numerical libraries for vector processing, +such as NumPy, PyTorch, TensorFlow and Apache Arrow. Storing and retrieving vector data using the same densely packed +format used by these libraries can result in significant memory savings and processing efficiency. + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +This specification introduces a new BSON binary subtype, the vector, with value `9`. + +Drivers SHOULD provide idiomatic APIs to translate between arrays of numbers and this BSON Binary specification. + +### Data Types (dtypes) + +Each vector can take one of multiple data types (dtypes). The following table lists the dtypes implemented. + +| Vector data type | Alias | Bits per vector element | [Arrow Data Type](https://arrow.apache.org/docs/cpp/api/datatype.html) (for illustration) | +| ---------------- | ---------- | ----------------------- | ----------------------------------------------------------------------------------------- | +| `0x03` | INT8 | 8 | INT8 | +| `0x27` | FLOAT32 | 32 | FLOAT | +| `0x10` | PACKED_BIT | 1 `*` | BOOL | + +`*` A Binary Quantized (PACKED_BIT) Vector is a vector of 0s and 1s (bits), but it is represented in memory as a list of +integers in [0, 255]. So, for example, the vector `[0, 255]` would be shorthand for the 16-bit vector +`[0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1]`. The idea is that each number (a uint8) can be stored as a single byte. Of course, +some languages, Python for one, do not have an uint8 type, so must be represented as an int in memory, but not on disk. + +### Byte padding + +As not all data types have a bit length equal to a multiple of 8, and hence do not fit squarely into a certain number of +bytes, a second piece of metadata, the "padding" is included. This instructs the driver of the number of bits in the +final byte that are to be ignored. The least-significant bits are ignored. + +### Binary structure + +Following the binary subtype `9`, a two-element byte array of metadata precedes the packed numbers. + +- The first byte (dtype) describes its data type. The table above shows those that MUST be implemented. This table may + increase. dtype is an unsigned integer. + +- The second byte (padding) prescribes the number of bits to ignore in the final byte of the value. It is a non-negative + integer. It must be present, even in cases where it is not applicable, and set to zero. + +- The remainder contains the actual vector elements packed according to dtype. + +All values use the little-endian format. + +#### Example + +Let's take a vector `[238, 224]` of dtype PACKED_BIT (`\x10`) with a padding of `4`. + +In hex, it looks like this: `b"\x10\x04\xee\xe0"`: 1 byte for dtype, 1 for padding, and 1 for each uint8. + +We can visualize the binary representation like so: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1st byte: dtype (from list in previous table) 2nd byte: padding (values in [0,7])1st uint8: 2382nd uint8: 224
00010000000001001110111011100000
+ +Finally, after we remove the last 4 bits of padding, the actual bit vector has a length of 12 and looks like this! + +| 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | + +## API Guidance + +Drivers MUST implement methods for explicit encoding and decoding that adhere to the pattern described below while +following idioms of the language of the driver. + +### Encoding + +``` +Function from_vector(vector: Iterable, dtype: DtypeEnum, padding: Integer = 0) -> Binary + # Converts a numeric vector into a binary representation based on the specified dtype and padding. + + # :param vector: A sequence or iterable of numbers (either float or int) + # :param dtype: Data type for binary conversion (from DtypeEnum) + # :param padding: Optional integer specifying how many bits to ignore in the final byte + # :return: A binary representation of the vector + + Declare binary_data as Binary + + # Process each number in vector and convert according to dtype + For each number in vector + binary_element = convert_to_binary(number, dtype) + binary_data.append(binary_element) + End For + + # Apply padding to the binary data if needed + If padding > 0 + apply_padding(binary_data, padding) + End If + + Return binary_data +End Function +``` + +Note: If a driver chooses to implement a `Vector` type (or numerous) like that suggested in the Data Structure +subsection below, they MAY decide that `from_vector` that has a single argument, a Vector. + +### Decoding + +``` +Function as_vector() -> Vector + # Unpacks binary data (BSON or similar) into a Vector structure. + # This process involves extracting numeric values, the data type, and padding information. + + # :return: A BinaryVector containing the unpacked numeric values, dtype, and padding. + + Declare binary_vector as BinaryVector # Struct to hold the unpacked data + + # Extract dtype (data type) from the binary data + binary_vector.dtype = extract_dtype_from_binary() + + # Extract padding from the binary data + binary_vector.padding = extract_padding_from_binary() + + # Unpack the actual numeric values from the binary data according to the dtype + binary_vector.data = unpack_numeric_values(binary_vector.dtype) + + Return binary_vector +End Function +``` + +#### Validation + +Drivers MUST validate vector metadata and raise an exception if any invariant is violated: + +- When unpacking binary data into a FLOAT32 Vector structure, the length of the binary data following the dtype and + padding MUST be a multiple of 4 bytes. +- Padding MUST be 0 for all dtypes where padding doesn’t apply, and MUST be within [0, 7] for PACKED_BIT. +- A PACKED_BIT vector MUST NOT be empty if padding is in the range [1, 7]. + - For a PACKED_BIT vector with non-zero padding, ignored bits SHOULD be zero. + - When encoding, if ignored bits aren't zero, drivers SHOULD raise an exception, but drivers MAY leave them as-is if + backwards-compatibility is a concern. + - When decoding, drivers SHOULD raise an exception if decoding non-zero ignored bits, but drivers MAY choose not to + for backwards compatibility. + - Drivers SHOULD use the next major release to conform to ignored bits being zero. + +Drivers MUST perform this validation when a numeric vector and padding are provided through the API, and when unpacking +binary data (BSON or similar) into a Vector structure. + +#### Data Structures + +Drivers MAY find the following structures to represent the dtype and vector structure useful. + +``` +Enum Dtype + # Enum for data types (dtype) + + # FLOAT32: Represents packing of list of floats as float32 + # Value: 0x27 (hexadecimal byte value) + + # INT8: Represents packing of list of signed integers in the range [-128, 127] as signed int8 + # Value: 0x03 (hexadecimal byte value) + + # PACKED_BIT: Special case where vector values are 0 or 1, packed as unsigned uint8 in range [0, 255] + # Packed into groups of 8 (a byte) + # Value: 0x10 (hexadecimal byte value) + + # Documentation: + # Each value is a byte (length of one), a convenient choice for decoding. +End Enum + +Struct Vector + # Numeric vector with metadata for binary interoperability + + # Fields: + # data: Sequence of numeric values (either float or int) + # dtype: Data type of vector (from enum BinaryVectorDtype) + # padding: Number of bits to ignore in the final byte for alignment + + data # Sequence of float or int + dtype # Type: DtypeEnum + padding # Integer: Number of padding bits + End Struct +``` + +## Reference Implementation + +- PYTHON (PYTHON-4577) + +## Test Plan + +See the [README](tests/README.md) for tests. + +## FAQ + +- What MongoDB Server version does this apply to? + + - Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +- In PACKED_BIT, why would one choose to use integers in \[0, 256)? + + - This follows a well-established precedent for packing binary-valued arrays into bytes (8 bits), This technique is + widely used across different fields, such as data compression, communication protocols, and file formats, where + you want to store or transmit binary data more efficiently by grouping 8 bits into a single byte (uint8). For an + example in Python, see + [numpy.unpackbits](https://numpy.org/doc/2.0/reference/generated/numpy.unpackbits.html#numpy.unpackbits). + +- In PACKED_BIT, why are ignored bits recommended to be zero? + + - To ensure the same data representation has the same encoding. For drivers supporting comparison operations, this + avoids comparing different unused bits. + +## Changelog + +- 2025-06-23: In PACKED_BIT vectors, ignored bits MAY be zero for backwards-compatibility. Prose tests added. + +- 2025-04-08: In PACKED_BIT vectors, ignored bits must be zero. + +- 2025-03-07: Update tests to use Extended JSON representation of +/-Infinity. (DRIVERS-3095) + +- 2025-02-04: Update validation for decoding into a FLOAT32 vector. + +- 2024-11-01: BSON Binary Subtype 9 accepted DRIVERS-2926 (#1708) diff --git a/source/bson-binary-vector/tests/README.md b/source/bson-binary-vector/tests/README.md new file mode 100644 index 0000000000..3902aa10c9 --- /dev/null +++ b/source/bson-binary-vector/tests/README.md @@ -0,0 +1,124 @@ +# Testing Binary subtype 9: Vector + +The JSON files in this directory tree are platform-independent tests that drivers can use to prove their conformance to +the specification. + +These tests focus on the roundtrip of the list of numbers as input/output, along with their data type and byte padding. + +Additional tests exist in `bson_corpus/tests/binary.json` but do not sufficiently test the end-to-end process of Vector +to BSON. For this reason, drivers must create a bespoke test runner for the vector subtype. + +## Format + +The test data corpus consists of a JSON file for each data type (dtype). Each file contains a number of test cases, +under the top-level key "tests". Each test case pertains to a single vector. The keys provide the specification of the +vector. Valid cases also include the Canonical BSON format of a document {test_key: binary}. The "test_key" is common, +and specified at the top level. + +#### Top level keys + +Each JSON file contains three top-level keys. + +- `description`: human-readable description of what is in the file +- `test_key`: name used for key when encoding/decoding a BSON document containing the single BSON Binary for the test + case. Applies to *every* case. +- `tests`: array of test case objects, each of which have the following keys. Valid cases will also contain additional + binary and json encoding values. + +#### Keys of individual tests cases + +- `description`: string describing the test. +- `valid`: boolean indicating if the vector, dtype, and padding should be considered a valid input. +- `vector`: (required if valid is true) list of numbers +- `dtype_hex`: string defining the data type in hex (e.g. "0x10", "0x27") +- `dtype_alias`: (optional) string defining the data dtype, perhaps as Enum. +- `padding`: (optional) integer for byte padding. Defaults to 0. +- `canonical_bson`: (required if valid is true) an (uppercase) big-endian hex representation of a BSON byte string. + +## Required tests + +#### To prove correct in a valid case (`valid: true`), one MUST + +- encode a document from the numeric values, dtype, and padding, along with the "test_key", and assert this matches the + canonical_bson string. +- decode the canonical_bson into its binary form, and then assert that the numeric values, dtype, and padding all match + those provided in the JSON. + +Note: For floating point number types, exact numerical matches may not be possible. Drivers that natively support the +floating-point type being tested (e.g., when testing float32 vector values in a driver that natively supports float32), +MUST assert that the input float array is the same after encoding and decoding. + +#### To prove correct in an invalid case (`valid:false`), one MUST + +- if the vector field is present, raise an exception when attempting to encode a document from the numeric values, + dtype, and padding. +- if the canonical_bson field is present, raise an exception when attempting to deserialize it into the corresponding + numeric values, as the field contains corrupted data. + +## Prose Tests + +### Treatment of non-zero ignored bits + +All drivers MUST test encoding and decoding behavior according to their design and version. For drivers that haven't +been completed, raise exceptions in both cases. For those that have, update to this behavior according to semantic +versioning rules, and update tests accordingly. + +In both cases, [255], a single byte PACKED_BIT vector of length 1 (hence padding of 7) provides a good example to use, +as all of its bits are ones. + +#### 1. Encoding + +- Test encoding with non-zero ignored bits. Use the driver API that validates vector metadata. +- If the driver validates ignored bits are zero (preferred), expect an error. Otherwise expect the ignored bits are + preserved. + +```python +with pytest.raises(ValueError): + Binary.from_vector([0b11111111], BinaryVectorDtype.PACKED_BIT, padding=7) +``` + +### 2. Decoding + +- Test the behaviour of your driver when one attempts to decode from binary to vector. + - e.g. As of pymongo 4.14, a warning is raised. From 5.0, it will be an exception. + +```python +b = Binary(b'\x10\x07\xff', subtype=9) +with pytest.warns(): + Binary.as_vector(b) +``` + +Drivers MAY skip this test if they choose not to implement a `Vector` type. + +### 3. Comparison + +Once we can guarantee that all ignored bits are non-zero, then equality can be tested on the binary subtype. Until then, +equality is ambiguous, and depends on whether one compares by bits (uint1), or uint8. Drivers SHOULD test equality +behavior according to their design and version. + +For example, in `pymongo < 5.0`, we define equality of a BinaryVector by matching padding, dtype, and integer. This +means that two single bit vectors in which 7 bits are ignored do not match unless all bits match. This mirrors what the +server does. + +```python +b1 = Binary(b'\x10\x07\x80', subtype=9) # 1-bit vector with all 0 ignored bits. +b2 = Binary(b'\x10\x07\xff', subtype=9) # 1-bit vector with all 1 ignored bits. +b3 = Binary.from_vector([0b10000000], BinaryVectorDtype.PACKED_BIT, padding=7) # Same data as b1. + +v1 = Binary.as_vector(b1) +v2 = Binary.as_vector(b2) +v3 = Binary.as_vector(b3) + +assert b1 != b2 # Unequal at naive Binary level +assert v2 != v1 # Also chosen to be unequal at BinaryVector level as [255] != [128] +assert b1 == b3 # Equal at naive Binary level +assert v1 == v3 # Equal at the BinaryVector level +``` + +Drivers MAY skip this test if they choose not to implement a `Vector` type, or the type does not support comparison, or +the type cannot be constructed with non-zero ignored bits. + +## FAQ + +- What MongoDB Server version does this apply to? + - Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. diff --git a/source/bson-binary-vector/tests/float32.json b/source/bson-binary-vector/tests/float32.json new file mode 100644 index 0000000000..72dafce10f --- /dev/null +++ b/source/bson-binary-vector/tests/float32.json @@ -0,0 +1,65 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype FLOAT32", + "test_key": "vector", + "tests": [ + { + "description": "Simple Vector FLOAT32", + "valid": true, + "vector": [127.0, 7.0], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1C00000005766563746F72000A0000000927000000FE420000E04000" + }, + { + "description": "Vector with decimals and negative value FLOAT32", + "valid": true, + "vector": [127.7, -7.7], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1C00000005766563746F72000A0000000927006666FF426666F6C000" + }, + { + "description": "Empty Vector FLOAT32", + "valid": true, + "vector": [], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009270000" + }, + { + "description": "Infinity Vector FLOAT32", + "valid": true, + "vector": [{"$numberDouble": "-Infinity"}, 0.0, {"$numberDouble": "Infinity"} ], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "2000000005766563746F72000E000000092700000080FF000000000000807F00" + }, + { + "description": "FLOAT32 with padding", + "valid": false, + "vector": [127.0, 7.0], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 3, + "canonical_bson": "1C00000005766563746F72000A0000000927030000FE420000E04000" + }, + { + "description": "Insufficient vector data with 3 bytes FLOAT32", + "valid": false, + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "canonical_bson": "1700000005766563746F7200050000000927002A2A2A00" + }, + { + "description": "Insufficient vector data with 5 bytes FLOAT32", + "valid": false, + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "canonical_bson": "1900000005766563746F7200070000000927002A2A2A2A2A00" + } + ] +} diff --git a/source/bson-binary-vector/tests/int8.json b/source/bson-binary-vector/tests/int8.json new file mode 100644 index 0000000000..29524fb617 --- /dev/null +++ b/source/bson-binary-vector/tests/int8.json @@ -0,0 +1,57 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype INT8", + "test_key": "vector", + "tests": [ + { + "description": "Simple Vector INT8", + "valid": true, + "vector": [127, 7], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0, + "canonical_bson": "1600000005766563746F7200040000000903007F0700" + }, + { + "description": "Empty Vector INT8", + "valid": true, + "vector": [], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009030000" + }, + { + "description": "Overflow Vector INT8", + "valid": false, + "vector": [128], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + }, + { + "description": "Underflow Vector INT8", + "valid": false, + "vector": [-129], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + }, + { + "description": "INT8 with padding", + "valid": false, + "vector": [127, 7], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 3, + "canonical_bson": "1600000005766563746F7200040000000903037F0700" + }, + { + "description": "INT8 with float inputs", + "valid": false, + "vector": [127.77, 7.77], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + } + ] +} diff --git a/source/bson-binary-vector/tests/packed_bit.json b/source/bson-binary-vector/tests/packed_bit.json new file mode 100644 index 0000000000..7cc272e38b --- /dev/null +++ b/source/bson-binary-vector/tests/packed_bit.json @@ -0,0 +1,83 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype PACKED_BIT", + "test_key": "vector", + "tests": [ + { + "description": "Padding specified with no vector data PACKED_BIT", + "valid": false, + "vector": [], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 1, + "canonical_bson": "1400000005766563746F72000200000009100100" + }, + { + "description": "Simple Vector PACKED_BIT", + "valid": true, + "vector": [127, 7], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0, + "canonical_bson": "1600000005766563746F7200040000000910007F0700" + }, + { + "description": "PACKED_BIT with padding", + "valid": true, + "vector": [127, 8], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 3, + "canonical_bson": "1600000005766563746F7200040000000910037F0800" + }, + { + "description": "Empty Vector PACKED_BIT", + "valid": true, + "vector": [], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009100000" + }, + { + "description": "Overflow Vector PACKED_BIT", + "valid": false, + "vector": [256], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Underflow Vector PACKED_BIT", + "valid": false, + "vector": [-1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Vector with float values PACKED_BIT", + "valid": false, + "vector": [127.5], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Exceeding maximum padding PACKED_BIT", + "valid": false, + "vector": [1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 8, + "canonical_bson": "1500000005766563746F7200030000000910080100" + }, + { + "description": "Negative padding PACKED_BIT", + "valid": false, + "vector": [1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": -1 + } + ] +} diff --git a/source/bson-corpus/bson-corpus.md b/source/bson-corpus/bson-corpus.md new file mode 100644 index 0000000000..7126acbd7b --- /dev/null +++ b/source/bson-corpus/bson-corpus.md @@ -0,0 +1,363 @@ +# BSON Corpus + +- Status: Accepted +- Minimum Server Version: N/A + +## Abstract + +The official BSON specification does not include test data, so this pseudo-specification describes tests for BSON +encoding and decoding. It also includes tests for MongoDB's "Extended JSON" specification (hereafter abbreviated as +`extjson`). + +## Meta + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Motivation for Change + +To ensure correct operation, we want drivers to implement identical tests for important features. BSON (and `extjson`) +are critical for correct operation and data exchange, but historically had no common test corpus. This +pseudo-specification provides such tests. + +### Goals + +- Provide machine-readable test data files for BSON and `extjson` encoding and decoding. +- Cover all current and historical BSON types. +- Define test data patterns for three cases: + - conversion/roundtrip, + - decode errors, and + - parse errors. + +### Non-Goals + +- Replace or extend the official BSON spec at . +- Provide a formal specification for `extjson`. + +## Specification + +The specification for BSON lives at . The `extjson` format specification is +[here](../extended-json/extended-json.md). + +## Test Plan + +This test plan describes a general approach for BSON testing. Future BSON specifications (such as for new types like +Decimal128) may specialize or alter the approach described below. + +### Description of the BSON Corpus + +This BSON test data corpus consists of a JSON file for each BSON type, plus a `top.json` file for testing the overall, +enclosing document and a `multi-type.json` file for testing a document with all BSON types. There is also a +`multi-type-deprecated.json` that includes deprecated keys. + +#### Top level keys + +- `description`: human-readable description of what is in the file +- `bson_type`: hex string of the first byte of a BSON element (e.g. "0x01" for type "double"); this will be the + synthetic value "0x00" for "whole document" tests like `top.json`. +- `test_key`: (optional) name of a field in a single-BSON-type `valid` test case that contains the data type being + tested. +- `valid` (optional): an array of validity test cases (see below). +- `decodeErrors` (optional): an array of decode error cases (see below). +- `parseErrors` (optional): an array of type-specific parse error case (see below). +- `deprecated` (optional): this field will be present (and true) if the BSON type has been deprecated (i.e. Symbol, + Undefined and DBPointer) + +#### Validity test case keys + +Validity test cases include 'canonical' forms of BSON and Extended JSON that are deemed equivalent and may provide +additional cases or metadata for additional assertions. For each case, keys include: + +- `description`: human-readable test case label. +- `canonical_bson`: an (uppercase) big-endian hex representation of a BSON byte string. Be sure to mangle the case as + appropriate in any roundtrip tests. +- `canonical_extjson`: a string containing a Canonical Extended JSON document. Because this is itself embedded as a + *string* inside a JSON document, characters like quote and backslash are escaped. +- `relaxed_extjson`: (optional) a string containing a Relaxed Extended JSON document. Because this is itself embedded as + a *string* inside a JSON document, characters like quote and backslash are escaped. +- `degenerate_bson`: (optional) an (uppercase) big-endian hex representation of a BSON byte string that is technically + parseable, but not in compliance with the BSON spec. Be sure to mangle the case as appropriate in any roundtrip + tests. +- `degenerate_extjson`: (optional) a string containing an invalid form of Canonical Extended JSON that is still + parseable according to type-specific rules. (For example, "1e100" instead of "1E+100".) +- `converted_bson`: (optional) an (uppercase) big-endian hex representation of a BSON byte string. It may be present for + deprecated types. It represents a possible conversion of the deprecated type to a non-deprecated type, e.g. symbol + to string. +- `converted_extjson`: (optional) a string containing a Canonical Extended JSON document. Because this is itself + embedded as a *string* inside a JSON document, characters like quote and backslash are escaped. It may be present + for deprecated types and is the Canonical Extended JSON representation of `converted_bson`. +- `lossy` (optional) -- boolean; present (and true) iff `canonical_bson` can't be represented exactly with extended JSON + (e.g. NaN with a payload). + +#### Decode error case keys + +Decode error cases provide an invalid BSON document or field that should result in an error. For each case, keys +include: + +- `description`: human-readable test case label. +- `bson`: an (uppercase) big-endian hex representation of an invalid BSON string that should fail to decode correctly. + +#### Parse error case keys + +Parse error cases are type-specific and represent some input that can not be encoded to the `bson_type` under test. For +each case, keys include: + +- `description`: human-readable test case label. +- `string`: a text or numeric representation of an input that can't be parsed to a valid value of the given type. + +### Extended JSON encoding, escaping and ordering + +Because the `canonical_extjson` and other Extended JSON fields are embedded in a JSON document, all their JSON +metacharacters are escaped. Control characters and non-ASCII codepoints are represented with `\uXXXX`. Note that this +means that the corpus JSON will appear to have double-escaped characters `\\uXXXX`. This is by design to ensure that the +Extended JSON fields remain printable ASCII without embedded null characters to ensure maximum portability to different +language JSON or extended JSON decoders. + +There are legal differences in JSON representation that may complicate testing for particular codecs. The JSON in the +corpus may not resemble the JSON generated by a codec, even though they represent the same data. Some known differences +include: + +- JSON only requires certain characters to be escaped but allows any character to be escaped. +- The JSON format is *unordered* and whitespace (outside of strings) is not significant. + +Implementations using these tests MUST normalize JSON comparisons however necessary for effective comparison. + +### Language-specific differences + +Some programming languages may not be able to represent or transmit all types accurately. In such cases, implementations +SHOULD ignore (or modify) any tests which are not supported on that platform. + +### Testing validity + +To test validity of a case in the `valid` array, we consider up to five possible representations: + +- Canonical BSON (denoted herein as "cB") -- fully valid, spec-compliant BSON +- Degenerate BSON (denoted herein as "dB") -- invalid but still parseable BSON (bad array keys, regex options out of + order) +- Canonical Extended JSON (denoted herein as "cEJ") -- A string format based on the JSON standard that emphasizes type + preservation at the expense of readability and interoperability. +- Degenerate Extended JSON (denoted herin as "dEJ") -- An invalid form of Canonical Extended JSON that is still + parseable. (For example, "1e100" instead of "1E+100".) +- Relaxed Extended JSON (denoted herein as "rEJ") -- A string format based on the JSON standard that emphasizes + readability and interoperability at the expense of type preservation. + +Not all input types will exist for a given test case. + +There are two forms of BSON/Extended JSON codecs: ones that have a language-native "intermediate" representation and +ones that do not. + +For a codec *without* an intermediate representation (i.e. one that translates directly from BSON to JSON or back), the +following assertions MUST hold (function names are for clarity of illustration only): + +- for cB input: + - bson_to_canonical_extended_json(cB) = cEJ + - bson_to_relaxed_extended_json(cB) = rEJ (if rEJ exists) +- for cEJ input: + - json_to_bson(cEJ) = cB (unless lossy) +- for dB input (if it exists): + - bson_to_canonical_extended_json(dB) = cEJ + - bson_to_relaxed_extended_json(dB) = rEJ (if rEJ exists) +- for dEJ input (if it exists): + - json_to_bson(dEJ) = cB (unless lossy) +- for rEJ input (if it exists): + - bson_to_relaxed_extended_json( json_to_bson(rEJ) ) = rEJ + +For a codec that has a language-native representation, we want to test both conversion and round-tripping. For these +codecs, the following assertions MUST hold (function names are for clarity of illustration only): + +- for cB input: + - native_to_bson( bson_to_native(cB) ) = cB + - native_to_canonical_extended_json( bson_to_native(cB) ) = cEJ + - native_to_relaxed_extended_json( bson_to_native(cB) ) = rEJ (if rEJ exists) +- for cEJ input: + - native_to_canonical_extended_json( json_to_native(cEJ) ) = cEJ + - native_to_bson( json_to_native(cEJ) ) = cB (unless lossy) +- for dB input (if it exists): + - native_to_bson( bson_to_native(dB) ) = cB +- for dEJ input (if it exists): + - native_to_canonical_extended_json( json_to_native(dEJ) ) = cEJ + - native_to_bson( json_to_native(dEJ) ) = cB (unless lossy) +- for rEJ input (if it exists): + - native_to_relaxed_extended_json( json_to_native(rEJ) ) = rEJ + +Implementations MAY test assertions in an implementation-specific manner. + +### Testing decode errors + +The `decodeErrors` cases represent BSON documents that are sufficiently incorrect that they can't be parsed even with +liberal interpretation of the BSON schema (e.g. reading arrays with invalid keys is possible, even though technically +invalid, so they are *not* `decodeErrors`). + +Drivers SHOULD test that each case results in a decoding error. Implementations MAY test assertions in an +implementation-specific manner. + +### Testing parsing errors + +The interpretation of `parseErrors` is type-specific. The structure of test cases within `parseErrors` is described in +[Parse error case keys](#parse-error-case-keys). + +Drivers SHOULD test that each case results in a parsing error (e.g. parsing Extended JSON, constructing a language +type). Implementations MAY test assertions in an implementation-specific manner. + +#### Top-level Document (type 0x00) + +For type "0x00" (i.e. top-level documents), the `string` field contains input for an Extended JSON parser. Drivers MUST +parse the Extended JSON input using an Extended JSON parser and verify that doing so yields an error. Drivers that parse +Extended JSON into language types instead of directly to BSON MAY need to additionally convert the resulting language +type(s) to BSON to expect an error. + +Drivers SHOULD also parse the Extended JSON input using a regular JSON parser (not an Extended JSON one) and verify the +input is parsed successfully. This serves to verify that the `parseErrors` test cases are testing Extended JSON-specific +error conditions and that they do not have, for example, unintended syntax errors. + +Note: due to the generic nature of these tests, they may also be used to test Extended JSON parsing errors for various +BSON types appearing within a document. + +#### Binary (type 0x05) + +For type "0x05" (i.e. binary), the rules for handling `parseErrors` are the same as those for +[Top-level Document (type 0x00)](#top-level-document-type-0x00). + +#### Decimal128 (type 0x13) + +For type "0x13" (i.e. Decimal128), the `string` field contains input for a Decimal128 parser that converts string input +to a binary Decimal128 value (e.g. Decimal128 constructor). Drivers MUST assert that these strings cannot be +successfully converted to a binary Decimal128 value and that parsing the string produces an error. + +### Deprecated types + +The corpus files for deprecated types are provided for informational purposes. Implementations MAY ignore or modify them +to match legacy treatment of deprecated types. The `converted_bson` and `converted_extjson` fields MAY be used to test +conversion to a standard type or MAY be ignored. + +## Prose Tests + +The following tests have not yet been automated, but MUST still be tested. + +### 1. Prohibit null bytes in null-terminated strings when encoding BSON + +The BSON spec uses null-terminated strings to represent document field names and regex components (i.e. pattern and +flags/options). Drivers MUST assert that null bytes are prohibited in the following contexts when encoding BSON (i.e. +creating raw BSON bytes or constructing BSON-specific type classes): + +- Field name within a root document +- Field name within a sub-document +- Pattern for a regular expression +- Flags/options for a regular expression + +Depending on how drivers implement BSON encoding, they MAY expect an error when constructing a type class (e.g. BSON +Document or Regex class) or when encoding a language representation to BSON (e.g. converting a dictionary, which might +allow null bytes in its keys, to raw BSON bytes). + +## Implementation Notes + +### A tool for visualizing BSON + +The test directory includes a Perl script `bsonview`, which will decompose and highlight elements of a BSON document. It +may be used like this: + +```bash +echo "0900000010610005000000" | perl bsonview -x +``` + +### Notes for certain types + +#### Array + +Arrays can have degenerate BSON if the array indexes are not set as "0", "1", etc. + +#### Boolean + +The only valid values are 0 and 1. Other non-zero numbers MUST be interpreted as errors rather than "true" values. + +#### Binary + +The Base64 encoded text in the extended JSON representation MUST be padded. + +#### Code + +There are multiple ways to encode Unicode characters as a JSON document. Individual implementers may need to normalize +provided and generated extended JSON before comparison. + +#### Decimal + +NaN with payload can't be represented in extended JSON, so such conversions are lossy. + +#### Double + +There is not yet a way to represent Inf, -Inf or NaN in extended JSON. Even if a `$numberDouble` is added, it is +unlikely to support special values with payloads, so such doubles would be lossy when converted to extended JSON. + +String representation of doubles is fairly unportable so it's hard to provide a single string that all +platforms/languages will generate. Testers may need to normalize/modify the test cases. + +#### String + +There are multiple ways to encode Unicode characters as a JSON document. Individual implementers may need to normalize +provided and generated extended JSON before comparison. + +#### DBPointer + +This type is deprecated. The provided converted form (`converted_bson`) represents them as DBRef documents, but such +conversion is outside the scope of this spec. + +#### Symbol + +This type is deprecated. The provided converted form converts these to strings, but such conversion is outside the scope +of this spec. + +#### Undefined + +This type is deprecated. The provided converted form converts these to Null, but such conversion is outside the scope of +this spec. + +## Reference Implementation + +The Java, C# and Perl drivers. + +## Design Rationale + +### Use of extjson + +Testing conversion requires an "input" and an "output". With a BSON string as both input and output, we can only test +that it roundtrips correctly --we can't test that the decoded value visible to the language is correct. + +For example, a pathological encoder/decoder could invert Boolean true and false during decoding and encoding. The BSON +would roundtrip but the program would see the wrong values. + +Therefore, we need a separate, semantic description of the contents of a BSON string in a machine readable format. +Fortunately, we already have extjson as a means of doing so. The extended JSON strings contained within the tests adhere +to the Extended JSON Specification. + +### Repetition across cases + +Some validity cases may result in duplicate assertions across cases, particularly if the `degenerate_bson` field is +different in different cases, but the `canonical_bson` field is the same. This is by design so that each case stands +alone and can be confirmed to be internally consistent via the assertions. This makes for easier and safer test case +development. + +## Changelog + +- 2024-01-22: Migrated from reStructuredText to Markdown. + +- 2023-06-14: Add decimal128 Extended JSON parse tests for clamped zeros with very large exponents. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2021-09-09: Clarify error expectation rules for `parseErrors`. + +- 2021-09-02: Add spec and prose tests for prohibiting null bytes in null-terminated strings within document field names + and regular expressions. Clarify type-specific rules for `parseErrors`. + +- 2017-05-26: Revised to be consistent with Extended JSON spec 2.0: valid case fields have changed, as have the test + assertions. + +- 2017-01-23: Added `multi-type.json` to test encoding and decoding all BSON types within the same document. Amended all + extended JSON strings to adhere to the Extended JSON Specification. Modified the "Use of extjson" section of this + specification to note that canonical extended JSON is now used. + +- 2016-11-14: Removed "invalid flags" BSON Regexp case. + +- 2016-10-25: Added a "non-alphabetized flags" case to the BSON Regexp corpus file; decoders must be able to read + non-alphabetized flags, but encoders must emit alphabetized flags. Added an "invalid flags" case to the BSON Regexp + corpus file. diff --git a/source/bson-corpus/bson-corpus.rst b/source/bson-corpus/bson-corpus.rst index b3743b2681..a388d8533d 100644 --- a/source/bson-corpus/bson-corpus.rst +++ b/source/bson-corpus/bson-corpus.rst @@ -1,476 +1,4 @@ -=========== -BSON Corpus -=========== -:Title: BSON Corpus -:Author: Luke Lovett, David Golden -:Lead: Jeff Yemin -:Advisors: Craig Wilson -:Status: Approved -:Type: Standards -:Minimum Server Version: N/A -:Last Modified: July 20, 2017 -:Version: 2.0 - -.. contents:: - -Abstract -======== - -The official BSON specification does not include test data, so this -pseudo-specification describes tests for BSON encoding and decoding. It also -includes tests for MongoDB's "Extended JSON" specification (hereafter -abbreviated as ``extjson``). - -Meta -==== - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119`_. - -.. _RFC 2119: https://www.ietf.org/rfc/rfc2119.txt - -Motivation for Change -===================== - -To ensure correct operation, we want drivers to implement identical tests -for important features. BSON (and ``extjson``) are critical for correct -operation and data exchange, but historically had no common test corpus. -This pseudo-specification provides such tests. - -Goals ------ - -* Provide machine-readable test data files for BSON and ``extjson`` encoding - and decoding. - -* Cover all current and historical BSON types. - -* Define test data patterns for three cases: (a) conversion/roundtrip, (b) - decode errors, and (c) parse errors. - -Non-Goals ---------- - -* Replace or extend the offical BSON spec at http://bsonspec.org. - -* Provide a formal specification for ``extjson``. - -Specification -============= - -The specification for BSON lives at http://bsonspec.org. The ``extjson`` -format specification lives at -https://github.com/mongodb/specifications/blob/master/source/extended-json.rst. - -Test Plan -========= - -This test plan describes a general approach for BSON testing. Future BSON -specifications (such as for new types like Decimal128) may specialize or -alter the approach described below. - -Description of the BSON Corpus ------------------------------- - -This BSON test data corpus consists of a JSON file for each BSON type, plus -a ``top.json`` file for testing the overall, enclosing document and a -``multi-type.json`` file for testing a document with all BSON types. -There is also a ``multi-type-deprecated.json`` that includes deprecated keys. - -Top level keys -~~~~~~~~~~~~~~ - -* ``description``: human-readable description of what is in the file - -* ``bson_type``: hex string of the first byte of a BSON element (e.g. "0x01" - for type "double"); this will be the synthetic value "0x00" for "whole - document" tests like ``top.json``. - -* ``test_key``: (optional) name of a field in a single-BSON-type ``valid`` test - case that contains the data type being tested. - -* ``valid`` (optional): an array of validity test cases (see below). - -* ``decodeErrors`` (optional): an array of decode error cases (see below). - -* ``parseErrors`` (optional): an array of type-specific parse error case (see - below). - -* ``deprecated`` (optional): this field will be present (and true) if the - BSON type has been deprecated (i.e. Symbol, Undefined and DBPointer) - -Validity test case keys -~~~~~~~~~~~~~~~~~~~~~~~ - -Validity test cases include 'canonical' forms of BSON and Extended JSON that -are deemed equivalent and may provide additional cases or metadata for -additional assertions. For each case, keys include: - -* ``description``: human-readable test case label. - -* ``canonical_bson``: an (uppercase) big-endian hex representation of a BSON - byte string. Be sure to mangle the case as appropriate in any roundtrip - tests. - -* ``canonical_extjson``: a string containing a Canonical Extended JSON document. - Because this is itself embedded as a *string* inside a JSON document, - characters like quote and backslash are escaped. - -* ``relaxed_extjson``: (optional) a string containing a Relaxed Extended JSON - document. Because this is itself embedded as a *string* inside a JSON - document, characters like quote and backslash are escaped. - -* ``degenerate_bson``: (optional) an (uppercase) big-endian hex representation - of a BSON byte string that is technically parseable, but not in compliance - with the BSON spec. Be sure to mangle the case as appropriate in any - roundtrip tests. - -* ``degenerate_extjson``: (optional) a string containing an invalid form of - Canonical Extended JSON that is still parseable according to type-specific - rules. (For example, "1e100" instead of "1E+100".) - -* ``converted_bson``: (optional) an (uppercase) big-endian hex representation - of a BSON byte string. It may be present for deprecated types. It represents - a possible conversion of the deprecated type to a non-deprecated type, e.g. - symbol to string. - -* ``converted_extjson``: (optional) a string containing a Canonical Extended - JSON document. Because this is itself embedded as a *string* inside a JSON - document, characters like quote and backslash are escaped. It may be - present for deprecated types and is the Canonical Extended JSON - representation of ``converted_bson`. - -* ``lossy`` (optional) -- boolean; present (and true) iff ``canonical_bson`` - can't be represented exactly with extended JSON (e.g. NaN with a payload). - -Decode error case keys -~~~~~~~~~~~~~~~~~~~~~~ - -Decode error cases provide an invalid BSON document or field that -should result in an error. For each case, keys include: - -* ``description``: human-readable test case label. - -* ``bson``: an (uppercase) big-endian hex representation of an invalid - BSON string that should fail to decode correctly. - -Parse error case keys -~~~~~~~~~~~~~~~~~~~~~ - -Parse error cases are type-specific and represent some input that can not -be encoded to the ``bson_type`` under test. For each case, keys include: - -* ``description``: human-readable test case label. - -* ``string``: a text or numeric representation of an input that can't be - parsed to a valid value of the given type. - -Extended JSON encoding, escaping and ordering ---------------------------------------------- - -Because the ``canonical_extjson`` and other Extended JSON fields are embedded -in a JSON document, all their JSON metacharacters are escaped. Control -characters and non-ASCII codepoints are represented with ``\uXXXX``. Note that -this means that the corpus JSON will appear to have double-escaped characters -``\\uXXXX``. This is by design to ensure that the Extended JSON fields remain -printable ASCII without embedded null characters to ensure maximum portability -to different language JSON or extended JSON decoders. - -There are legal differences in JSON representation that may complicate -testing for particular codecs. The JSON in the corpus may not resemble -the JSON generated by a codec, even though they represent the same data. -Some known differences include: - -* JSON only requires certain characters to be escaped but allows any character - to be escaped. - -* The JSON format is *unordered* and whitespace (outside of strings) is not - significant. - -Implementations using these tests MUST normalize JSON comparisons however -necessary for effective comparison. - -Language-specific differences ------------------------------ - -Some programming languages may not be able to represent or transmit all -types accurately. In such cases, implementations SHOULD ignore (or modify) -any tests which are not supported on that platform. - -Testing validity ----------------- - -To test validity of a case in the ``valid`` array, we consider up to five -possible representations: - -* Canonical BSON (denoted herein as "cB") -- fully valid, spec-compliant BSON - -* Degenerate BSON (denoted herein as "dB") -- invalid but still parseable BSON - (bad array keys, regex options out of order) - -* Canonical Extended JSON (denoted herein as "cEJ") -- A string format based on - the JSON standard that emphasizes type preservation at the expense of - readability and interoperability. - -* Degenerate Extended JSON (denoted herin as "dEJ") -- An invalid form of - Canonical Extended JSON that is still parseable. (For example, "1e100" - instead of "1E+100".) - -* Relaxed Extended JSON (denoted herein as "rEJ") -- A string format based on - the JSON standard that emphasizes readability and interoperability at the - expense of type preservation. - -Not all input types will exist for a given test case. - -There are two forms of BSON/Extended JSON codecs: ones that have a language-native -"intermediate" representation and ones that do not. - -For a codec *without* an intermediate representation (i.e. one that translates -directly from BSON to JSON or back), the following assertions MUST hold -(function names are for clarity of illustration only): - -* for cB input: - - * bson_to_canonical_extended_json(cB) = cEJ - - * bson_to_relaxed_extended_json(cB) = rEJ (if rEJ exists) - -* for cEJ input: - - * json_to_bson(cEJ) = cB (unless lossy) - -* for dB input (if it exists): - - * bson_to_canonical_extended_json(dB) = cEJ - - * bson_to_relaxed_extended_json(dB) = rEJ (if rEJ exists) - -* for dEJ input (if it exists): - - * json_to_bson(dEJ) = cB (unless lossy) - -* for rEJ input (if it exists): - - * bson_to_relaxed_extended_json( json_to_bson(rEJ) ) = rEJ - -For a codec that has a language-native representation, we want to test both -conversion and round-tripping. For these codecs, the following assertions MUST -hold (function names are for clarity of illustration only): - -* for cB input: - - * native_to_bson( bson_to_native(cB) ) = cB - - * native_to_canonical_extended_json( bson_to_native(cB) ) = cEJ - - * native_to_relaxed_extended_json( bson_to_native(cB) ) = rEJ (if rEJ exists) - -* for cEJ input: - - * native_to_canonical_extended_json( json_to_native(cEJ) ) = cEJ - - * native_to_bson( json_to_native(cEJ) ) = cB (unless lossy) - -* for dB input (if it exists): - - * native_to_bson( bson_to_native(dB) ) = cB - -* for dEJ input (if it exists): - - * native_to_canonical_extended_json( json_to_native(dEJ) ) = cEJ - - * native_to_bson( json_to_native(dEJ) ) = cB (unless lossy) - -* for rEJ input (if it exists): - - * native_to_relaxed_extended_json( json_to_native(rEJ) ) = rEJ - -Implementations MAY test assertions in an implementation-specific -manner. - -Testing decode errors ---------------------- - -The ``decodeErrors`` cases represent BSON documents that are sufficiently -incorrect that they can't be parsed even with liberal interpretation of -the BSON schema (e.g. reading arrays with invalid keys is possible, even -though technically invalid, so they are *not* ``decodeErrors``). - -Drivers SHOULD test that each case results in a decoding error. -Implementations MAY test assertions in an implementation-specific -manner. - -Testing parsing errors ----------------------- - -The interpretation of ``parseErrors`` is type-specific. For example, -helpers for creating Decimal128 values may parse strings to convert them -to binary Decimal128 values. The ``parseErrors`` cases are strings that -will *not* convert correctly. - -The documentation for a type (if any) will specify how to use these -cases for testing. - -For type "0x00" (i.e. top-level documents), the ``parseErrors`` entries have a -``description`` field and an ``string`` field. Parsing the ``string`` field -as Extended JSON MUST result in an error. - -Drivers SHOULD test that each case results in a parse error. -Implementations MAY test assertions in an implementation-specific -manner. - -Deprecated types ----------------- - -The corpus files for deprecated types are provided for informational purposes. -Implementations MAY ignore or modify them to match legacy treatment of -deprecated types. The ``converted_bson`` and ``converted_extjson`` fields MAY -be used to test conversion to a standard type or MAY be ignored. - -Implementation Notes -==================== - -A tool for visualizing BSON ---------------------------- - -The test directory includes a Perl script ``bsonview``, which will -decompose and highlight elements of a BSON document. It may be used like -this:: - - echo "0900000010610005000000" | perl bsonview -x - -Notes for certain types ------------------------ - -Array -~~~~~ - -Arrays can have degenerate BSON if the array indexes are not set as -"0", "1", etc. - -Boolean -~~~~~~~ - -The only valid values are 0 and 1. Other non-zero numbers MUST be -interpreted as errors rather than "true" values. - -Binary -~~~~~~ - -The Base64 encoded text in the extended JSON representation MUST be padded. - -Code -~~~~ - -There are multiple ways to encode Unicode characters as a JSON document. -Individual implementers may need to normalize provided and generated -extended JSON before comparison. - -Decimal -~~~~~~~ - -NaN with payload can't be represented in extended JSON, so such conversions are -lossy. - -Double -~~~~~~ - -There is not yet a way to represent Inf, -Inf or NaN in extended JSON. Even if -a $numberDouble is added, it is unlikely to support special values with -payloads, so such doubles would be lossy when converted to extended JSON. - -String representation of doubles is fairly unportable so it's hard to provide -a single string that all platforms/languages will generate. Testers may -need to normalize/modify the test cases. - -String -~~~~~~ - -There are multiple ways to encode Unicode characters as a JSON document. -Individual implementers may need to normalize provided and generated -extended JSON before comparison. - -DBPointer -~~~~~~~~~ - -This type is deprecated. The provided converted form (``converted_bson``) -represents them as DBRef documents, but such conversion is outside the scope of -this spec. - -Symbol -~~~~~~ - -This type is deprecated. The provided converted form converts these to -strings, but such conversion is outside the scope of this spec. - -Undefined -~~~~~~~~~ - -This type is deprecated. The provided converted form converts these to Null, -but such conversion is outside the scope of this spec. - -Reference Implementation -======================== - -The Java, C# and Perl drivers. - -Design Rationale -================ - -Use of extjson --------------- - -Testing conversion requires an "input" and an "output". With a BSON string -as both input and output, we can only test that it roundtrips correctly -- -we can't test that the decoded value visible to the language is correct. - -For example, a pathological encoder/decoder could invert Boolean true and -false during decoding and encoding. The BSON would roundtrip but the -program would see the wrong values. - -Therefore, we need a separate, semantic description of the contents of a BSON -string in a machine readable format. Fortunately, we already have extjson as a -means of doing so. The extended JSON strings contained within the tests adhere -to the Extended JSON Specification. - -Repetition across cases ------------------------ - -Some validity cases may result in duplicate assertions across cases, -particularly if the ``degenerate_bson`` field is different in different cases, -but the ``canonical_bson`` field is the same. This is by design so that each -case stands alone and can be confirmed to be internally consistent via the -assertions. This makes for easier and safer test case development. - -Changes -======= - -Version 2.0 - May 26, 2017 - -* Revised to be consistent with Extended JSON spec 2.0: valid case fields - have changed, as have the test assertions. - -Version 1.3 - January 23, 2017 - -* Added ``multi-type.json`` to test encoding and decoding all BSON types within - the same document. - -* Amended all extended JSON strings to adhere to the Extended JSON - Specification. - -* Modified the "Use of extjson" section of this specification to note that - canonical extended JSON is now used. - -Version 1.2 - November 14, 2016 - -* Removed "invalid flags" BSON Regexp case. - -Version 1.1 – October 25, 2016 - -* Added a "non-alphabetized flags" case to the BSON Regexp corpus file; - decoders must be able to read non-alphabetized flags, but encoders must - emit alphabetized flags. - -* Added an "invalid flags" case to the BSON Regexp corpus file. +.. note:: + This specification has been converted to Markdown and renamed to + `bson-corpus.md `_. diff --git a/source/bson-corpus/tests/array.json b/source/bson-corpus/tests/array.json index 1c654cf36b..9ff953e5ae 100644 --- a/source/bson-corpus/tests/array.json +++ b/source/bson-corpus/tests/array.json @@ -14,16 +14,22 @@ "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" }, { - "description": "Single Element Array with index set incorrectly", + "description": "Single Element Array with index set incorrectly to empty string", "degenerate_bson": "130000000461000B00000010000A0000000000", "canonical_bson": "140000000461000C0000001030000A0000000000", "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" }, { - "description": "Single Element Array with index set incorrectly", + "description": "Single Element Array with index set incorrectly to ab", "degenerate_bson": "150000000461000D000000106162000A0000000000", "canonical_bson": "140000000461000C0000001030000A0000000000", "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" + }, + { + "description": "Multi Element Array with duplicate indexes", + "degenerate_bson": "1b000000046100130000001030000a000000103000140000000000", + "canonical_bson": "1b000000046100130000001030000a000000103100140000000000", + "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}, {\"$numberInt\": \"20\"}]}" } ], "decodeErrors": [ diff --git a/source/bson-corpus/tests/binary.json b/source/bson-corpus/tests/binary.json index 90a15c1a1c..0e0056f3a2 100644 --- a/source/bson-corpus/tests/binary.json +++ b/source/bson-corpus/tests/binary.json @@ -39,11 +39,27 @@ "canonical_bson": "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400", "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"04\"}}}" }, + { + "description": "subtype 0x04 UUID", + "canonical_bson": "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400", + "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"04\"}}}", + "degenerate_extjson": "{\"x\" : { \"$uuid\" : \"73ffd264-44b3-4c69-90e8-e7d1dfc035d4\"}}" + }, { "description": "subtype 0x05", "canonical_bson": "1D000000057800100000000573FFD26444B34C6990E8E7D1DFC035D400", "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"05\"}}}" }, + { + "description": "subtype 0x07", + "canonical_bson": "1D000000057800100000000773FFD26444B34C6990E8E7D1DFC035D400", + "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"07\"}}}" + }, + { + "description": "subtype 0x08", + "canonical_bson": "1D000000057800100000000873FFD26444B34C6990E8E7D1DFC035D400", + "canonical_extjson": "{\"x\" : { \"$binary\" : {\"base64\" : \"c//SZESzTGmQ6OfR38A11A==\", \"subType\" : \"08\"}}}" + }, { "description": "subtype 0x80", "canonical_bson": "0F0000000578000200000080FFFF00", @@ -58,6 +74,36 @@ "description": "$type query operator (conflicts with legacy $binary form with $type field)", "canonical_bson": "180000000378001000000010247479706500020000000000", "canonical_extjson": "{\"x\" : { \"$type\" : {\"$numberInt\": \"2\"}}}" + }, + { + "description": "subtype 0x09 Vector FLOAT32", + "canonical_bson": "170000000578000A0000000927000000FE420000E04000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"JwAAAP5CAADgQA==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector INT8", + "canonical_bson": "11000000057800040000000903007F0700", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"AwB/Bw==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector PACKED_BIT", + "canonical_bson": "11000000057800040000000910007F0700", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"EAB/Bw==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) FLOAT32", + "canonical_bson": "0F0000000578000200000009270000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"JwA=\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) INT8", + "canonical_bson": "0F0000000578000200000009030000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"AwA=\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) PACKED_BIT", + "canonical_bson": "0F0000000578000200000009100000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"EAA=\", \"subType\": \"09\"}}}" } ], "decodeErrors": [ @@ -81,5 +127,27 @@ "description": "subtype 0x02 length negative one", "bson": "130000000578000600000002FFFFFFFFFFFF00" } + ], + "parseErrors": [ + { + "description": "$uuid wrong type", + "string": "{\"x\" : { \"$uuid\" : { \"data\" : \"73ffd264-44b3-4c69-90e8-e7d1dfc035d4\"}}}" + }, + { + "description": "$uuid invalid value--too short", + "string": "{\"x\" : { \"$uuid\" : \"73ffd264-44b3-90e8-e7d1dfc035d4\"}}" + }, + { + "description": "$uuid invalid value--too long", + "string": "{\"x\" : { \"$uuid\" : \"73ffd264-44b3-4c69-90e8-e7d1dfc035d4-789e4\"}}" + }, + { + "description": "$uuid invalid value--misplaced hyphens", + "string": "{\"x\" : { \"$uuid\" : \"73ff-d26444b-34c6-990e8e-7d1dfc035d4\"}}" + }, + { + "description": "$uuid invalid value--too many hyphens", + "string": "{\"x\" : { \"$uuid\" : \"----d264-44b3-4--9-90e8-e7d1dfc0----\"}}" + } ] } diff --git a/source/bson-corpus/tests/code.json b/source/bson-corpus/tests/code.json index 6f37349ad0..b8482b2541 100644 --- a/source/bson-corpus/tests/code.json +++ b/source/bson-corpus/tests/code.json @@ -20,48 +20,48 @@ }, { "description": "two-byte UTF-8 (\u00e9)", - "canonical_bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", - "canonical_extjson": "{\"a\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}" + "canonical_bson": "190000000D61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", + "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}}" }, { "description": "three-byte UTF-8 (\u2606)", - "canonical_bson": "190000000261000D000000E29886E29886E29886E298860000", - "canonical_extjson": "{\"a\" : \"\\u2606\\u2606\\u2606\\u2606\"}" + "canonical_bson": "190000000D61000D000000E29886E29886E29886E298860000", + "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u2606\\u2606\\u2606\\u2606\"}}" }, { "description": "Embedded nulls", - "canonical_bson": "190000000261000D0000006162006261620062616261620000", - "canonical_extjson": "{\"a\" : \"ab\\u0000bab\\u0000babab\"}" + "canonical_bson": "190000000D61000D0000006162006261620062616261620000", + "canonical_extjson": "{\"a\" : {\"$code\" : \"ab\\u0000bab\\u0000babab\"}}" } ], "decodeErrors": [ { "description": "bad code string length: 0 (but no 0x00 either)", - "bson": "0C0000000261000000000000" + "bson": "0C0000000D61000000000000" }, { "description": "bad code string length: -1", - "bson": "0C000000026100FFFFFFFF00" + "bson": "0C0000000D6100FFFFFFFF00" }, { "description": "bad code string length: eats terminator", - "bson": "10000000026100050000006200620000" + "bson": "100000000D6100050000006200620000" }, { "description": "bad code string length: longer than rest of document", - "bson": "120000000200FFFFFF00666F6F6261720000" + "bson": "120000000D00FFFFFF00666F6F6261720000" }, { "description": "code string is not null-terminated", - "bson": "1000000002610004000000616263FF00" + "bson": "100000000D610004000000616263FF00" }, { "description": "empty code string, but extra null", - "bson": "0E00000002610001000000000000" + "bson": "0E0000000D610001000000000000" }, { "description": "invalid UTF-8", - "bson": "0E00000002610002000000E90000" + "bson": "0E0000000D610002000000E90000" } ] } diff --git a/source/bson-corpus/tests/datetime.json b/source/bson-corpus/tests/datetime.json index 60506ce174..1554341d29 100644 --- a/source/bson-corpus/tests/datetime.json +++ b/source/bson-corpus/tests/datetime.json @@ -24,7 +24,14 @@ { "description" : "Y10K", "canonical_bson" : "1000000009610000DC1FD277E6000000", + "relaxed_extjson" : "{\"a\":{\"$date\":{\"$numberLong\":\"253402300800000\"}}}", "canonical_extjson" : "{\"a\":{\"$date\":{\"$numberLong\":\"253402300800000\"}}}" + }, + { + "description": "leading zero ms", + "canonical_bson": "10000000096100D1D6D6CC3B01000000", + "relaxed_extjson": "{\"a\" : {\"$date\" : \"2012-12-24T12:15:30.001Z\"}}", + "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"1356351330001\"}}}" } ], "decodeErrors": [ diff --git a/source/bson-corpus/tests/dbref.json b/source/bson-corpus/tests/dbref.json index 1fe12c6f68..41c0b09d0e 100644 --- a/source/bson-corpus/tests/dbref.json +++ b/source/bson-corpus/tests/dbref.json @@ -1,5 +1,5 @@ { - "description": "DBRef", + "description": "Document type (DBRef sub-documents)", "bson_type": "0x03", "valid": [ { @@ -26,6 +26,26 @@ "description": "Document with key names similar to those of a DBRef", "canonical_bson": "3e0000000224726566000c0000006e6f742d612d646272656600072469640058921b3e6e32ab156a22b59e022462616e616e6100050000007065656c0000", "canonical_extjson": "{\"$ref\": \"not-a-dbref\", \"$id\": {\"$oid\": \"58921b3e6e32ab156a22b59e\"}, \"$banana\": \"peel\"}" + }, + { + "description": "DBRef with additional dollar-prefixed and dotted fields", + "canonical_bson": "48000000036462726566003c0000000224726566000b000000636f6c6c656374696f6e00072469640058921b3e6e32ab156a22b59e10612e62000100000010246300010000000000", + "canonical_extjson": "{\"dbref\": {\"$ref\": \"collection\", \"$id\": {\"$oid\": \"58921b3e6e32ab156a22b59e\"}, \"a.b\": {\"$numberInt\": \"1\"}, \"$c\": {\"$numberInt\": \"1\"}}}" + }, + { + "description": "Sub-document resembles DBRef but $id is missing", + "canonical_bson": "26000000036462726566001a0000000224726566000b000000636f6c6c656374696f6e000000", + "canonical_extjson": "{\"dbref\": {\"$ref\": \"collection\"}}" + }, + { + "description": "Sub-document resembles DBRef but $ref is not a string", + "canonical_bson": "2c000000036462726566002000000010247265660001000000072469640058921b3e6e32ab156a22b59e0000", + "canonical_extjson": "{\"dbref\": {\"$ref\": {\"$numberInt\": \"1\"}, \"$id\": {\"$oid\": \"58921b3e6e32ab156a22b59e\"}}}" + }, + { + "description": "Sub-document resembles DBRef but $db is not a string", + "canonical_bson": "4000000003646272656600340000000224726566000b000000636f6c6c656374696f6e00072469640058921b3e6e32ab156a22b59e1024646200010000000000", + "canonical_extjson": "{\"dbref\": {\"$ref\": \"collection\", \"$id\": {\"$oid\": \"58921b3e6e32ab156a22b59e\"}, \"$db\": {\"$numberInt\": \"1\"}}}" } ] } diff --git a/source/bson-corpus/tests/decimal128-1.json b/source/bson-corpus/tests/decimal128-1.json index 7eefec6bf7..8e7fbc93c6 100644 --- a/source/bson-corpus/tests/decimal128-1.json +++ b/source/bson-corpus/tests/decimal128-1.json @@ -312,6 +312,30 @@ "canonical_bson": "18000000136400000000000a5bc138938d44c64d31cc3700", "degenerate_extjson": "{\"d\" : {\"$numberDecimal\" : \"1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"}}", "canonical_extjson": "{\"d\" : {\"$numberDecimal\" : \"1.000000000000000000000000000000000E+999\"}}" + }, + { + "description": "Clamped zeros with a large positive exponent", + "canonical_bson": "180000001364000000000000000000000000000000FE5F00", + "degenerate_extjson": "{\"d\" : {\"$numberDecimal\" : \"0E+2147483647\"}}", + "canonical_extjson": "{\"d\" : {\"$numberDecimal\" : \"0E+6111\"}}" + }, + { + "description": "Clamped zeros with a large negative exponent", + "canonical_bson": "180000001364000000000000000000000000000000000000", + "degenerate_extjson": "{\"d\" : {\"$numberDecimal\" : \"0E-2147483647\"}}", + "canonical_extjson": "{\"d\" : {\"$numberDecimal\" : \"0E-6176\"}}" + }, + { + "description": "Clamped negative zeros with a large positive exponent", + "canonical_bson": "180000001364000000000000000000000000000000FEDF00", + "degenerate_extjson": "{\"d\" : {\"$numberDecimal\" : \"-0E+2147483647\"}}", + "canonical_extjson": "{\"d\" : {\"$numberDecimal\" : \"-0E+6111\"}}" + }, + { + "description": "Clamped negative zeros with a large negative exponent", + "canonical_bson": "180000001364000000000000000000000000000000008000", + "degenerate_extjson": "{\"d\" : {\"$numberDecimal\" : \"-0E-2147483647\"}}", + "canonical_extjson": "{\"d\" : {\"$numberDecimal\" : \"-0E-6176\"}}" } ] } diff --git a/source/bson-corpus/tests/document.json b/source/bson-corpus/tests/document.json index 3ec9187044..698e7ae90a 100644 --- a/source/bson-corpus/tests/document.json +++ b/source/bson-corpus/tests/document.json @@ -17,6 +17,26 @@ "description": "Single-character key subdoc", "canonical_bson": "160000000378000E0000000261000200000062000000", "canonical_extjson": "{\"x\" : {\"a\" : \"b\"}}" + }, + { + "description": "Dollar-prefixed key in sub-document", + "canonical_bson": "170000000378000F000000022461000200000062000000", + "canonical_extjson": "{\"x\" : {\"$a\" : \"b\"}}" + }, + { + "description": "Dollar as key in sub-document", + "canonical_bson": "160000000378000E0000000224000200000061000000", + "canonical_extjson": "{\"x\" : {\"$\" : \"a\"}}" + }, + { + "description": "Dotted key in sub-document", + "canonical_bson": "180000000378001000000002612E62000200000063000000", + "canonical_extjson": "{\"x\" : {\"a.b\" : \"c\"}}" + }, + { + "description": "Dot as key in sub-document", + "canonical_bson": "160000000378000E000000022E000200000061000000", + "canonical_extjson": "{\"x\" : {\".\" : \"a\"}}" } ], "decodeErrors": [ @@ -31,6 +51,10 @@ { "description": "Invalid subdocument: bad string length in field", "bson": "1C00000003666F6F001200000002626172000500000062617A000000" + }, + { + "description": "Null byte in sub-document key", + "bson": "150000000378000D00000010610000010000000000" } ] } diff --git a/source/bson-corpus/tests/double.json b/source/bson-corpus/tests/double.json index 00f75196fe..d5b8fb3d7e 100644 --- a/source/bson-corpus/tests/double.json +++ b/source/bson-corpus/tests/double.json @@ -28,16 +28,16 @@ "relaxed_extjson": "{\"d\" : -1.0001220703125}" }, { - "description": "1.23456789012345677E+18", - "canonical_bson": "1000000001640081E97DF41022B14300", - "canonical_extjson": "{\"d\" : {\"$numberDouble\": \"1.23456789012345677E+18\"}}", - "relaxed_extjson": "{\"d\" : 1.23456789012345677E+18}" + "description": "1.2345678921232E+18", + "canonical_bson": "100000000164002a1bf5f41022b14300", + "canonical_extjson": "{\"d\" : {\"$numberDouble\": \"1.2345678921232E+18\"}}", + "relaxed_extjson": "{\"d\" : 1.2345678921232E+18}" }, { - "description": "-1.23456789012345677E+18", - "canonical_bson": "1000000001640081E97DF41022B1C300", - "canonical_extjson": "{\"d\" : {\"$numberDouble\": \"-1.23456789012345677E+18\"}}", - "relaxed_extjson": "{\"d\" : -1.23456789012345677E+18}" + "description": "-1.2345678921232E+18", + "canonical_bson": "100000000164002a1bf5f41022b1c300", + "canonical_extjson": "{\"d\" : {\"$numberDouble\": \"-1.2345678921232E+18\"}}", + "relaxed_extjson": "{\"d\" : -1.2345678921232E+18}" }, { "description": "0.0", diff --git a/source/bson-corpus/tests/regex.json b/source/bson-corpus/tests/regex.json index c62b019cdf..223802169d 100644 --- a/source/bson-corpus/tests/regex.json +++ b/source/bson-corpus/tests/regex.json @@ -54,11 +54,11 @@ ], "decodeErrors": [ { - "description": "embedded null in pattern", + "description": "Null byte in pattern string", "bson": "0F0000000B610061006300696D0000" }, { - "description": "embedded null in flags", + "description": "Null byte in flags string", "bson": "100000000B61006162630069006D0000" } ] diff --git a/source/bson-corpus/tests/symbol.json b/source/bson-corpus/tests/symbol.json index 4e46cb9511..3dd3577ebd 100644 --- a/source/bson-corpus/tests/symbol.json +++ b/source/bson-corpus/tests/symbol.json @@ -50,31 +50,31 @@ "decodeErrors": [ { "description": "bad symbol length: 0 (but no 0x00 either)", - "bson": "0C0000000261000000000000" + "bson": "0C0000000E61000000000000" }, { "description": "bad symbol length: -1", - "bson": "0C000000026100FFFFFFFF00" + "bson": "0C0000000E6100FFFFFFFF00" }, { "description": "bad symbol length: eats terminator", - "bson": "10000000026100050000006200620000" + "bson": "100000000E6100050000006200620000" }, { "description": "bad symbol length: longer than rest of document", - "bson": "120000000200FFFFFF00666F6F6261720000" + "bson": "120000000E00FFFFFF00666F6F6261720000" }, { "description": "symbol is not null-terminated", - "bson": "1000000002610004000000616263FF00" + "bson": "100000000E610004000000616263FF00" }, { "description": "empty symbol, but extra null", - "bson": "0E00000002610001000000000000" + "bson": "0E0000000E610001000000000000" }, { "description": "invalid UTF-8", - "bson": "0E00000002610002000000E90000" + "bson": "0E0000000E610002000000E90000" } ] } diff --git a/source/bson-corpus/tests/timestamp.json b/source/bson-corpus/tests/timestamp.json index c76bc2998e..6f46564a32 100644 --- a/source/bson-corpus/tests/timestamp.json +++ b/source/bson-corpus/tests/timestamp.json @@ -18,6 +18,11 @@ "description": "Timestamp with high-order bit set on both seconds and increment", "canonical_bson": "10000000116100FFFFFFFFFFFFFFFF00", "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 4294967295, \"i\" : 4294967295} } }" + }, + { + "description": "Timestamp with high-order bit set on both seconds and increment (not UINT32_MAX)", + "canonical_bson": "1000000011610000286BEE00286BEE00", + "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 4000000000, \"i\" : 4000000000} } }" } ], "decodeErrors": [ diff --git a/source/bson-corpus/tests/top.json b/source/bson-corpus/tests/top.json index 68b51195ab..9c649b5e3f 100644 --- a/source/bson-corpus/tests/top.json +++ b/source/bson-corpus/tests/top.json @@ -3,9 +3,24 @@ "bson_type": "0x00", "valid": [ { - "description": "Document with keys that start with $", + "description": "Dollar-prefixed key in top-level document", "canonical_bson": "0F00000010246B6579002A00000000", "canonical_extjson": "{\"$key\": {\"$numberInt\": \"42\"}}" + }, + { + "description": "Dollar as key in top-level document", + "canonical_bson": "0E00000002240002000000610000", + "canonical_extjson": "{\"$\": \"a\"}" + }, + { + "description": "Dotted key in top-level document", + "canonical_bson": "1000000002612E620002000000630000", + "canonical_extjson": "{\"a.b\": \"c\"}" + }, + { + "description": "Dot as key in top-level document", + "canonical_bson": "0E000000022E0002000000610000", + "canonical_extjson": "{\".\": \"a\"}" } ], "decodeErrors": [ @@ -64,28 +79,32 @@ { "description": "Document truncated mid-key", "bson": "1200000002666F" + }, + { + "description": "Null byte in document key", + "bson": "0D000000107800000100000000" } ], "parseErrors": [ { "description" : "Bad $regularExpression (extra field)", - "string" : "{\"a\" : \"$regularExpression\": {\"pattern\": \"abc\", \"options\": \"\", \"unrelated\": true}}}" + "string" : "{\"a\" : {\"$regularExpression\": {\"pattern\": \"abc\", \"options\": \"\", \"unrelated\": true}}}" }, { "description" : "Bad $regularExpression (missing options field)", - "string" : "{\"a\" : \"$regularExpression\": {\"pattern\": \"abc\"}}}" + "string" : "{\"a\" : {\"$regularExpression\": {\"pattern\": \"abc\"}}}" }, { "description": "Bad $regularExpression (pattern is number, not string)", - "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"$options\" : \"\"}}}" + "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"options\" : \"\"}}}" }, { "description": "Bad $regularExpression (options are number, not string)", - "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"$options\" : 0}}}" + "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"options\" : 0}}}" }, { "description" : "Bad $regularExpression (missing pattern field)", - "string" : "{\"a\" : \"$regularExpression\": {\"options\":\"ix\"}}}" + "string" : "{\"a\" : {\"$regularExpression\": {\"options\":\"ix\"}}}" }, { "description": "Bad $oid (number, not string)", @@ -151,6 +170,10 @@ "description": "Bad $code (type is number, not string)", "string": "{\"a\" : {\"$code\" : 42}}" }, + { + "description": "Bad $code (type is number, not string) when $scope is also present", + "string": "{\"a\" : {\"$code\" : 42, \"$scope\" : {}}}" + }, { "description": "Bad $code (extra field)", "string": "{\"a\" : {\"$code\" : \"\", \"unrelated\": true}}" @@ -195,14 +218,6 @@ "description": "Bad $date (extra field)", "string": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"1356351330501\"}, \"unrelated\": true}}" }, - { - "description": "Bad DBRef (ref is number, not string)", - "string": "{\"x\" : {\"$ref\" : 42, \"$id\" : \"abc\"}}" - }, - { - "description": "Bad DBRef (db is number, not string)", - "string": "{\"x\" : {\"$ref\" : \"a\", \"$id\" : \"abc\", \"$db\" : 42}}" - }, { "description": "Bad $minKey (boolean, not integer)", "string": "{\"a\" : {\"$minKey\" : true}}" @@ -230,7 +245,22 @@ { "description": "Bad DBpointer (extra field)", "string": "{\"a\": {\"$dbPointer\": {\"a\": {\"$numberInt\": \"1\"}, \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}, \"c\": {\"$numberInt\": \"2\"}, \"$ref\": \"b\"}}}" + }, + { + "description" : "Null byte in document key", + "string" : "{\"a\\u0000\": 1 }" + }, + { + "description" : "Null byte in sub-document key", + "string" : "{\"a\" : {\"b\\u0000\": 1 }}" + }, + { + "description": "Null byte in $regularExpression pattern", + "string": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"b\\u0000\", \"options\" : \"i\"}}}" + }, + { + "description": "Null byte in $regularExpression options", + "string": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"b\", \"options\" : \"i\\u0000\"}}}" } - ] } diff --git a/source/bson-decimal128/decimal128.md b/source/bson-decimal128/decimal128.md new file mode 100644 index 0000000000..88bf630635 --- /dev/null +++ b/source/bson-decimal128/decimal128.md @@ -0,0 +1,354 @@ +# BSON Decimal128 + +- Status: Accepted +- Minimum Server Version: 3.4 + +______________________________________________________________________ + +## Abstract + +MongoDB 3.4 introduces a new BSON type representing high precision decimal (`"\x13"`), known as Decimal128. 3.4 +compatible drivers must support this type by creating a Value Object for it, possibly with accessor functions for +retrieving its value in data types supported by the respective languages. + +Round-tripping Decimal128 types between driver and server MUST not change its value or representation in any way. +Conversion to and from native language types is complicated and there are many pitfalls to represent Decimal128 +precisely in all languages + +While many languages offer a native decimal type, the precision of these types often does not exactly match that of the +MongoDB implementation. To ensure error-free conversion and consistency between official MongoDB drivers, this +specification does not allow automatically converting the `BSON Decimal128` type into a language-defined decimal type. + +Language drivers will wrap their native type in value objects by default and SHOULD offer accessor functions for +retrieving its value represented by language-defined types if appropriate. A driver that offers the ability to configure +mappings to/from BSON types to native types MAY allow the option to automatically convert the `BSON Decimal128` type to +a native type. It should however be made abundantly clear to the user that converting to native data types risks +incurring data loss. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Terminology + +**IEEE 754-2008 128-bit decimal floating point (Decimal128)** + +The Decimal128 specification supports 34 decimal digits of precision, a max value of approximately `10^6145`, and min +value of approximately `-10^6145`. This is the new `BSON Decimal128` type (`"\x13"`). + +**Clamping** + +Clamping happens when a value's exponent is too large for the destination format. This works by adding zeros to the +coefficient to reduce the exponent to the largest usable value. An overflow occurs if the number of digits required is +more than allowed in the destination format. + +**Binary Integer Decimal (BID)** + +MongoDB uses this binary encoding for the coefficient as specified in `IEEE 754-2008` section 3.5.2 using method 2 +"binary encoding" rather than method 1 "decimal encoding". The byte order is little-endian, like the rest of the BSON +types. + +**Value Object** + +An immutable container type representing a value (e.g. Decimal128). This Value Object MAY provide accessors that +retrieve the abstracted value as a different type (e.g. casting it). `double x = valueObject.getAsDouble();` + +## Specification + +### BSON Decimal128 implementation details + +The `BSON Decimal128` data type implements the +[Decimal Arithmetic Encodings](https://speleotrove.com/decimal/decbits.html) specification, with certain exceptions +around value integrity and the coefficient encoding. When a value cannot be represented exactly, the value will be +rejected. + +The coefficient MUST be stored as an unsigned binary integer (BID) rather than the densely-packed decimal (DPD) shown in +the specification. See either the `IEEE Std 754-2008` spec or the driver examples for further detail. + +The specification defines several statuses which are meant to signal exceptional +[circumstances](https://speleotrove.com/decimal/daexcep.html), such as when overflowing occurs, and how to handle them. + +`BSON Decimal128` Value Objects MUST implement these actions for these exceptions: + +- Overflow + + - When overflow occurs, the operation MUST emit an error and result in a failure + +- Underflow + + - When underflow occurs, the operation MUST emit an error and result in a failure + +- Clamping + + - Since clamping does not change the actual value, only the representation of it, clamping MUST occur without emitting + an error. + +- Rounding + + - When the coefficient requires more digits then Decimal128 provides, rounding MUST be done without emitting an error, + unless it would result in inexact rounding, in which case the operation MUST emit an error and result in a + failure. + +- Conversion Syntax + + - Invalid strings MUST emit an error and result in a failure. + +It should be noted that the given exponent is a preferred representation. If the value cannot be stored due to the value +of the exponent being too large or too small, but can be stored using an alternative representation by clamping and or +rounding, a `BSON Decimal128` compatible Value Object MUST do so, unless such operation results in an inexact rounding +or other underflow or overflow. + +### Reading from BSON + +A BSON type `"\x13"` MUST be represented by an immutable Value Object by default and MUST NOT be automatically converted +into language native numeric type by default. A driver that offers users a way to configure the exact type mapping to +and from BSON types MAY allow the `BSON Decimal128` type to be converted to the user configured type. + +A driver SHOULD provide accessors for this immutable Value Object, which can return a language-specific representation +of the Decimal128 value, after converting it into the respective type. For example, Java may choose to provide +`Decimal128.getBigDecimal()`. + +All drivers MUST provide an accessor for retrieving the value as a string. Drivers MAY provide other accessors, +retrieving the value as other types. + +### Serializing and writing BSON + +Drivers MUST provide a way of constructing the Value Object, as the driver representation of the `BSON Decimal128` is an +immutable Value Object by default. + +A driver MUST have a way to construct this Value Object from a string. For example, Java MUST provide a method similar +to `Decimal128.valueOf("2.000")`. + +A driver that has accessors for different types SHOULD provide a way to construct the Value Object from those types. + +### Reading from Extended JSON + +The Extended JSON representation of Decimal128 is a document with the key `$numberDecimal` and a value of the Decimal128 +as a string. Drivers that support Extended JSON formatting MUST support the `$numberDecimal` type specifier. + +When an Extended JSON `$numberDecimal` is parsed, its type should be the same as that of a deserialized +`BSON Decimal128`, as described in [Reading from BSON](#reading-from-bson). + +The Extended JSON `$numberDecimal` value follows the same stringification rules as defined in +[From String Representation](#from-string-representation). + +### Writing to Extended JSON + +The Extended JSON type identifier is `$numberDecimal`, while the value itself is a string. Drivers that support +converting values to Extended JSON MUST be able to convert its Decimal128 value object to Extended JSON. + +Converting a Decimal128 Value Object to Extended JSON MUST follow the conversion rules in +[To String Representation](#to-string-representation), and other stringification rules as when converting Decimal128 +Value Object to a String. + +### Operator overloading and math on Decimal128 Value Objects + +Drivers MUST NOT allow any mathematical operator overloading for the Decimal128 Value Objects. This includes adding two +Decimal128 Value Objects and assigning the result to a new object. + +If a user wants to perform mathematical operations on Decimal128 Value Objects, the user must explicitly retrieve the +native language value representations of the objects and perform the operations on those native representations. The +user will then create a new Decimal128 Value Object and optionally overwrite the original Decimal128 Value Object. + +### From String Representation + +For finite numbers, we will use the definition at . It has been modified +to account for a different NaN representation and whitespace rules and copied here: + +```text +Strings which are acceptable for conversion to the abstract representation of +numbers, or which might result from conversion from the abstract representation +to a string, are called numeric strings. + +A numeric string is a character string that describes either a finite +number or a special value. +* If it describes a finite number, it includes one or more decimal digits, + with an optional decimal point. The decimal point may be embedded in the + digits, or may be prefixed or suffixed to them. The group of digits (and + optional point) thus constructed may have an optional sign ('+' or '-') + which must come before any digits or decimal point. +* The string thus described may optionally be followed by an 'E' + (indicating an exponential part), an optional sign, and an integer + following the sign that represents a power of ten that is to be applied. + The 'E' may be in uppercase or lowercase. +* If it describes a special value, it is one of the case-independent names + 'Infinity', 'Inf', or 'NaN' (where the first two represent infinity and + the second represent NaN). The name may be preceded by an optional sign, + as for finite numbers. +* No blanks or other whitespace characters are permitted in a numeric string. + +Formally + + sign ::= '+' | '-' + digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | + '8' | '9' + indicator ::= 'e' | 'E' + digits ::= digit [digit]... + decimal-part ::= digits '.' [digits] | ['.'] digits + exponent-part ::= indicator [sign] digits + infinity ::= 'Infinity' | 'Inf' + nan ::= 'NaN' + numeric-value ::= decimal-part [exponent-part] | infinity + numeric-string ::= [sign] numeric-value | [sign] nan + +where the characters in the strings accepted for 'infinity' and 'nan' may be in +any case. If an implementation supports the concept of diagnostic information +on NaNs, the numeric strings for NaNs MAY include one or more digits, as shown +above.[3] These digits encode the diagnostic information in an +implementation-defined manner; however, conversions to and from string for +diagnostic NaNs should be reversible if possible. If an implementation does not +support diagnostic information on NaNs, these digits should be ignored where +necessary. A plain 'NaN' is usually the same as 'NaN0'. + +Drivers MAY choose to support signed NaN (sNaN), along with sNaN with +diagnostic information. + +Examples:: +Some numeric strings are: + "0" -- zero + "12" -- a whole number + "-76" -- a signed whole number + "12.70" -- some decimal places + "+0.003" -- a plus sign is allowed, too + "017." -- the same as 17 + ".5" -- the same as 0.5 + "4E+9" -- exponential notation + "0.73e-7" -- exponential notation, negative power + "Inf" -- the same as Infinity + "-infinity" -- the same as -Infinity + "NaN" -- not-a-Number + +Notes: +1. A single period alone or with a sign is not a valid numeric string. +2. A sign alone is not a valid numeric string. +3. Significant (after the decimal point) and insignificant leading zeros + are permitted. +``` + +### To String Representation + +For finite numbers, we will use the definition at . It has been copied +here: + +```text +The coefficient is first converted to a string in base ten using the characters +0 through 9 with no leading zeros (except if its value is zero, in which case a +single 0 character is used). + +Next, the adjusted exponent is calculated; this is the exponent, plus the +number of characters in the converted coefficient, less one. That is, +exponent+(clength-1), where clength is the length of the coefficient in decimal +digits. + +If the exponent is less than or equal to zero and the adjusted exponent is +greater than or equal to -6, the number will be converted to a character form +without using exponential notation. In this case, if the exponent is zero then +no decimal point is added. Otherwise (the exponent will be negative), a decimal +point will be inserted with the absolute value of the exponent specifying the +number of characters to the right of the decimal point. '0' characters are +added to the left of the converted coefficient as necessary. If no character +precedes the decimal point after this insertion then a conventional '0' +character is prefixed. + +Otherwise (that is, if the exponent is positive, or the adjusted exponent is +less than -6), the number will be converted to a character form using +exponential notation. In this case, if the converted coefficient has more than +one digit a decimal point is inserted after the first digit. An exponent in +character form is then suffixed to the converted coefficient (perhaps with +inserted decimal point); this comprises the letter 'E' followed immediately by +the adjusted exponent converted to a character form. The latter is in base ten, +using the characters 0 through 9 with no leading zeros, always prefixed by a +sign character ('-' if the calculated exponent is negative, '+' otherwise). +``` + +This corresponds to the following code snippet: + +```c +var adjusted_exponent = _exponent + (clength - 1); +if (_exponent > 0 || adjusted_exponent < -6) { + // exponential notation +} else { + // character form without using exponential notation +} +``` + +For special numbers such as infinity or the not a number (NaN) variants, the below table is used: + +| Value | String | +| --------------------- | --------- | +| Positive Infinite | Infinity | +| Negative Infinite | -Infinity | +| Positive NaN | NaN | +| Negative NaN | NaN | +| Signaled NaN | NaN | +| Negative Signaled NaN | NaN | +| NaN with a payload | NaN | + +Finally, there are certain other invalid representations that must be treated as zeros, as per `IEEE 754-2008`. The +tests will verify that each special value has been accounted for. + +The server log files as well as the Extended JSON Format for Decimal128 use this format. + +## Motivation for Change + +BSON already contains support for `double` (`"\x01"`), but this type is insufficient for certain values that require +strict precision and representation, such as money, where it is necessary to perform exact decimal rounding. + +The new BSON type is the 128-bit `IEEE 754-2008` decimal floating point number, which is specifically designed to cope +with these issues. + +## Design Rationale + +For simplicity and consistency between drivers, drivers must not automatically convert this type into a native type by +default. This also ensures original data preservation, which is crucial to Decimal128. It is however recommended that +drivers offer a way to convert the Value Object to a native type through accessors, and to create a new BSON type from +native types. This forces the user to explicitly do the conversion and thus understand the difference between the +MongoDB type and possible language precision and representation. Representations via conversions done outside MongoDB +are not guaranteed to be identical. + +## Backwards Compatibility + +There should be no backwards compatibility concerns. This specification merely deals with how to encode and decode +BSON/Extended JSON Decimal128. + +## Reference Implementations + +- [Libbson](https://github.com/mongodb/libbson/blob/master/src/bson/bson-decimal128.c) +- [Ruby](https://github.com/estolfo/bson-ruby/blob/RUBY-1098-decimal128/lib/bson/decimal128.rb) +- [.NET](https://github.com/craiggwilson/mongo-csharp-driver/tree/decimal) +- [PyMongo](https://github.com/mongodb/mongo-python-driver/blob/master/bson/decimal128.py) +- [Node](https://github.com/mongodb/js-bson/blob/0.5/lib/bson/decimal128.js) +- [Java](https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/BsonDecimal128.java) + +## Tests + +See the [BSON Corpus](../bson-corpus/bson-corpus.md) for tests. + +Most of the tests are converted from the +[General Decimal Arithmetic Testcases](https://speleotrove.com/decimal/dectest.html). + +## Q&A + +- Is it true Decimal128 doesn't normalize the value? + + - Yes. As a result of non-normalization rules of the Decimal128 data type, precision is represented exactly. For + example, '2.00' always remains stored as 200E-2 in Decimal128, and it differs from the representation of '2.0' + (20E-1). These two values compare equally, but represent different ideas. + +- How does Decimal128 "2.000" look in the shell? + + - NumberDecimal("2.000") + +- Should a driver avoid sending Decimal128 values to pre-3.4 servers? + + - No + +- Is there a wire version bump or something for Decimal128? + + - No + +## Changelog + +- 2024-02-08: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter. diff --git a/source/bson-decimal128/decimal128.rst b/source/bson-decimal128/decimal128.rst index c15e33d234..1ad1cd7a8b 100644 --- a/source/bson-decimal128/decimal128.rst +++ b/source/bson-decimal128/decimal128.rst @@ -1,447 +1,3 @@ -======================================== -BSON Decimal128 Type Handling in Drivers -======================================== - -:Spec Title: BSON Decimal128 Type Handling In Drivers -:Spec Version: 1.0 -:Author: Hannes Magnusson -:Advisory Group: Emily Stolfo and Craig Wilson -:Kernel Advisory: Geert Bosch -:Original Work: David Hatch and Raymond Jacobson -:Status: Approved -:Type: Standards -:Minimum Server Version: 3.4 -:Last Modified: 2016-06-06 - - -.. contents:: - --------- - - -Abstract -======== - -MongoDB 3.4 introduces a new BSON type representing high precision decimal -(``"\x13"``), known as Decimal128. 3.4 compatible drivers must support this -type by creating a Value Object for it, possibly with accessor functions for -retrieving its value in data types supported by the respective languages. - - -Round-tripping Decimal128 types between driver and server MUST not change its -value or representation in any way. Conversion to and from native language -types is complicated and there are many pitfalls to represent Decimal128 -precisely in all languages - - -While many languages offer a native decimal type, the precision of these types -often does not exactly match that of the MongoDB implementation. To ensure -error-free conversion and consistency between official MongoDB drivers, this -specification does not allow automatically converting the ``BSON Decimal128`` type -into a language-defined decimal type. - - -Language drivers will wrap their native type in value objects by default and -SHOULD offer accessor functions for retrieving its value represented by -language-defined types if appropriate. A driver that offers the ability to -configure mappings to/from BSON types to native types MAY allow the option to -automatically convert the ``BSON Decimal128`` type to a native type. It should -however be made abundantly clear to the user that converting to native data -types risks incurring data loss. - - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - - -Terminology -=========== - -IEEE 754-2008 128-bit decimal floating point (Decimal128): - The Decimal128 specification supports 34 decimal digits of precision, a max - value of approximately ``10^6145``, and min value of approximately - ``-10^6145``. This is the new ``BSON Decimal128`` type (``"\x13"``). - - -Clamping: - Clamping happens when a value’s exponent is too large for the destination - format. This works by adding zeros to the coefficient to reduce the exponent to - the largest usable value. An overflow occurs if the number of digits required - is more than allowed in the destination format. - - -Binary Integer Decimal (BID): - MongoDB uses this binary encoding for the coefficient as specified in ``IEEE - 754-2008``. The byte order is little-endian, like the rest of the BSON types. - - -Value Object: - An immutable container type representing a value (e.g. Decimal128). This Value - Object MAY provide accessors that retrieve the abstracted value as a different - type (e.g. casting it). ``double x = valueObject.getAsDouble();`` - - -Specification -============= - - --------------------------------------- -BSON Decimal128 implementation details --------------------------------------- - -The ``BSON Decimal128`` data type implements the `Decimal Arithmetic Encodings -`_ specification, with certain -exceptions around value integrity. When a value cannot be represented exactly, -the value will be rejected. - - -The specification defines several statuses which are meant to signal -exceptional `circumstances `_, -such as when overflowing occurs, and how to handle them. - - -``BSON Decimal128`` Value Objects MUST implement these actions for these exceptions: - -* Overflow - * When overflow occurs, the operation MUST emit an error and result in a failure -* Underflow - * When underflow occurs, the operation MUST emit an error and result in a failure -* Clamping - * Since clamping does not change the actual value, only the representation - of it, clamping MUST occur without emitting an error. -* Rounding - * When the coefficient requires more digits then Decimal128 provides, - rounding MUST be done without emitting an error, unless it would result in - inexact rounding, in which case the operation MUST emit an error and - result in a failure. -* Conversion Syntax - * Invalid strings MUST emit an error and result in a failure. - - -It should be noted that the given exponent is a preferred representation. If -the value cannot be stored due to the value of the exponent being too large or -too small, but can be stored using an alternative representation by clamping -and or rounding, a ``BSON Decimal128`` compatible Value Object MUST do so, unless -such operation results in an inexact rounding or other underflow or overflow. - - ------------------ -Reading from BSON ------------------ - -A BSON type ``"\x13"`` MUST be represented by an immutable Value Object by -default and MUST NOT be automatically converted into language native numeric -type by default. A driver that offers users a way to configure the exact type -mapping to and from BSON types MAY allow the ``BSON Decimal128`` type to be -converted to the user configured type. - - -A driver SHOULD provide accessors for this immutable Value Object, which can -return a language-specific representation of the Decimal128 value, after -converting it into the respective type. For example, Java may choose to provide -``Decimal128.getBigDecimal()``. - - -All drivers MUST provide an accessor for retrieving the value as a string. -Drivers MAY provide other accessors, retrieving the value as other types. - - ----------------------------- -Serializing and writing BSON ----------------------------- - -Drivers MUST provide a way of constructing the Value Object, as the driver -representation of the ``BSON Decimal128`` is an immutable Value Object by default. - - -A driver MUST have a way to construct this Value Object from a string. For -example, Java MUST provide a method similar to ``Decimal128.valueOf("2.000")``. - - -A driver that has accessors for different types SHOULD provide a way to -construct the Value Object from those types. - - --------------------------- -Reading from Extended JSON --------------------------- - -The Extended JSON representation of Decimal128 is a document with the key -``$numberDecimal`` and a value of the Decimal128 as a string. Drivers that support -Extended JSON formatting MUST support the ``$numberDecimal`` type specifier. - - -When an Extended JSON ``$numberDecimal`` is parsed, its type should be the same as -that of a deserialized ``BSON Decimal128``, as described in `Reading from BSON`_. - - -The Extended JSON ``$numberDecimal`` value follows the same stringification rules -as defined in `From String Representation`_. - - ------------------------- -Writing to Extended JSON ------------------------- - -The Extended JSON type identifier is ``$numberDecimal``, while the value itself is -a string. Drivers that support converting values to Extended JSON MUST be able -to convert its Decimal128 value object to Extended JSON. - - -Converting a Decimal128 Value Object to Extended JSON MUST follow the -conversion rules in `To String Representation`_, and other stringification rules -as when converting Decimal128 Value Object to a String. - - ---------------------------------------------------------- -Operator overloading and math on Decimal128 Value Objects ---------------------------------------------------------- - -Drivers MUST NOT allow any mathematical operator overloading for the Decimal128 -Value Objects. This includes adding two Decimal128 Value Objects and assigning -the result to a new object. - - -If a user wants to perform mathematical operations on Decimal128 Value Objects, -the user must explicitly retrieve the native language value representations of -the objects and perform the operations on those native representations. The -user will then create a new Decimal128 Value Object and optionally overwrite -the original Decimal128 Value Object. - - --------------------------- -From String Representation --------------------------- - -For finite numbers, we will use the definition at -http://speleotrove.com/decimal/daconvs.html. It has been modified to account -for a different NaN representation and whitespace rules and copied here:: - - - Strings which are acceptable for conversion to the abstract representation of - numbers, or which might result from conversion from the abstract representation - to a string, are called numeric strings. - - - A numeric string is a character string that describes either a finite - number or a special value. - * If it describes a finite number, it includes one or more decimal digits, - with an optional decimal point. The decimal point may be embedded in the - digits, or may be prefixed or suffixed to them. The group of digits (and - optional point) thus constructed may have an optional sign (‘+’ or ‘-’) - which must come before any digits or decimal point. - * The string thus described may optionally be followed by an ‘E’ - (indicating an exponential part), an optional sign, and an integer - following the sign that represents a power of ten that is to be applied. - The ‘E’ may be in uppercase or lowercase. - * If it describes a special value, it is one of the case-independent names - ‘Infinity’, ‘Inf’, or ‘NaN’ (where the first two represent infinity and - the second represent NaN). The name may be preceded by an optional sign, - as for finite numbers. - * No blanks or other whitespace characters are permitted in a numeric string. - - Formally - - sign ::= ’+’ | ’-’ - digit ::= ’0’ | ’1’ | ’2’ | ’3’ | ’4’ | ’5’ | ’6’ | ’7’ | - ’8’ | ’9’ - indicator ::= ’e’ | ’E’ - digits ::= digit [digit]... - decimal-part ::= digits ’.’ [digits] | [’.’] digits - exponent-part ::= indicator [sign] digits - infinity ::= ’Infinity’ | ’Inf’ - nan ::= ’NaN’ - numeric-value ::= decimal-part [exponent-part] | infinity - numeric-string ::= [sign] numeric-value | [sign] nan - - where the characters in the strings accepted for ‘infinity’ and ‘nan’ may be in - any case. If an implementation supports the concept of diagnostic information - on NaNs, the numeric strings for NaNs MAY include one or more digits, as shown - above.[3] These digits encode the diagnostic information in an - implementation-defined manner; however, conversions to and from string for - diagnostic NaNs should be reversible if possible. If an implementation does not - support diagnostic information on NaNs, these digits should be ignored where - necessary. A plain ‘NaN’ is usually the same as ‘NaN0’. - - - Drivers MAY choose to support signed NaN (sNaN), along with sNaN with - diagnostic information. - - - - Examples:: - Some numeric strings are: - "0" -- zero - "12" -- a whole number - "-76" -- a signed whole number - "12.70" -- some decimal places - "+0.003" -- a plus sign is allowed, too - "017." -- the same as 17 - ".5" -- the same as 0.5 - "4E+9" -- exponential notation - "0.73e-7" -- exponential notation, negative power - "Inf" -- the same as Infinity - "-infinity" -- the same as -Infinity - "NaN" -- not-a-Number - - Notes: - 1. A single period alone or with a sign is not a valid numeric string. - 2. A sign alone is not a valid numeric string. - 3. Significant (after the decimal point) and insignificant leading zeros - are permitted. - - ------------------------- -To String Representation ------------------------- - -For finite numbers, we will use the definition at -http://speleotrove.com/decimal/daconvs.html. It has been copied here:: - - - The coefficient is first converted to a string in base ten using the characters - 0 through 9 with no leading zeros (except if its value is zero, in which case a - single 0 character is used). - - - Next, the adjusted exponent is calculated; this is the exponent, plus the - number of characters in the converted coefficient, less one. That is, - exponent+(clength-1), where clength is the length of the coefficient in decimal - digits. - - - If the exponent is less than or equal to zero and the adjusted exponent is - greater than or equal to -6, the number will be converted to a character form - without using exponential notation. In this case, if the exponent is zero then - no decimal point is added. Otherwise (the exponent will be negative), a decimal - point will be inserted with the absolute value of the exponent specifying the - number of characters to the right of the decimal point. ‘0’ characters are - added to the left of the converted coefficient as necessary. If no character - precedes the decimal point after this insertion then a conventional ‘0’ - character is prefixed. - - - Otherwise (that is, if the exponent is positive, or the adjusted exponent is - less than -6), the number will be converted to a character form using - exponential notation. In this case, if the converted coefficient has more than - one digit a decimal point is inserted after the first digit. An exponent in - character form is then suffixed to the converted coefficient (perhaps with - inserted decimal point); this comprises the letter ‘E’ followed immediately by - the adjusted exponent converted to a character form. The latter is in base ten, - using the characters 0 through 9 with no leading zeros, always prefixed by a - sign character (‘-’ if the calculated exponent is negative, ‘+’ otherwise). - - -This corresponds to the following code snippet: - - - .. code:: c - - var adjusted_exponent = _exponent + (clength - 1); - if (_exponent > 0 || adjusted_exponent < -6) { - // exponential notation - } else { - // character form without using exponential notation - } - - -For special numbers such as infinity or the not a number (NaN) variants, the -below table is used: - - -============================== ============ - Value String -============================== ============ -Positive Infinite Infinity -Negative Infinite -Infinity -Positive NaN NaN -Negative NaN NaN -Signaled NaN NaN -Negative Signaled NaN NaN -NaN with a payload NaN -Signaled NaN with a payload NaN -============================== ============ - - - -Finally, there are certain other invalid representations that must be treated -as zeros, as per ``IEEE 754-2008``. The tests will verify that each special value -has been accounted for. - - -The server log files as well as the Extended JSON Format for Decimal128 use -this format. - - -Motivation for Change -===================== - -BSON already contains support for ``double`` (``"\x01"``), but this type is -insufficient for certain values that require strict precision and -representation, such as money, where it is necessary to perform exact decimal -rounding. - - -The new BSON type is the 128-bit ``IEEE 754-2008`` decimal floating point number, -which is specifically designed to cope with these issues. - - -Design Rationale -================ - -For simplicity and consistency between drivers, drivers must not automatically -convert this type into a native type by default. This also ensures original -data preservation, which is crucial to Decimal128. It is however recommended -that drivers offer a way to convert the Value Object to a native type through -accessors, and to create a new BSON type from native types. This forces the -user to explicitly do the conversion and thus understand the difference between -the MongoDB type and possible language precision and representation. -Representations via conversions done outside MongoDB are not guaranteed to be -identical. - - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. This specification merely -deals with how to encode and decode BSON/Extended JSON Decimal128. - - -Reference Implementations -========================= - -* `Libbson `_ -* `Ruby `_ -* `.NET `_ -* `PyMongo `_ -* `Node `_ -* `Java `_ - - -Tests -===== - -See the `BSON Corpus `_ for tests. - -Most of the tests are converted from the -[General Decimal Arithmetic Testcases](http://speleotrove.com/decimal/dectest.html>). - -Q&A -=== - -* Is it true Decimal128 doesn’t normalize the value? - * Yes. As a result of non-normalization rules of the Decimal128 data type, - precision is represented exactly. For example, ‘2.00’ always remains - stored as 200E-2 in Decimal128, and it differs from the representation of - ‘2.0’ (20E-1). These two values compare equally, but represent different - ideas. -* How does Decimal128 "2.000" look in the shell? - * NumberDecimal("2.000") -* Should a driver avoid sending Decimal128 values to pre-3.4 servers? - * No -* Is there a wire version bump or something for Decimal128? - * No +.. note:: + This specification has been converted to Markdown and renamed to + `decimal128.md `_. diff --git a/source/bson-objectid/objectid.md b/source/bson-objectid/objectid.md new file mode 100644 index 0000000000..5d39b1733f --- /dev/null +++ b/source/bson-objectid/objectid.md @@ -0,0 +1,136 @@ +# BSON ObjectID + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This specification documents the format and data contents of ObjectID BSON values that the drivers and the server +generate when no field values have been specified (e.g. creating an ObjectID BSON value when no `_id` field is present +in a document). It is primarily aimed to provide an alternative to the historical use of the MD5 hashing algorithm for +the machine information field of the ObjectID, which is problematic when providing a FIPS compliant implementation. It +also documents existing best practices for the timestamp and counter fields. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +The [ObjectID](https://www.mongodb.com/docs/manual/reference/method/ObjectId/) BSON type is a 12-byte value consisting +of three different portions (fields): + +- a 4-byte value representing the seconds since the Unix epoch in the highest order bytes, +- a 5-byte random number unique to a machine and process, +- a 3-byte counter, starting with a random value. + +```text +4 byte timestamp 5 byte process unique 3 byte counter +|<----------------->|<---------------------->|<------------>| +[----|----|----|----|----|----|----|----|----|----|----|----] +0 4 8 12 +``` + +### Timestamp Field + +This 4-byte big endian field represents the seconds since the Unix epoch (Jan 1st, 1970, midnight UTC). It is an ever +increasing value that will have a range until about Jan 7th, 2106. + +Drivers MUST create ObjectIDs with this value representing the number of seconds since the Unix epoch. + +Drivers MUST interpret this value as an **unsigned 32-bit integer** when conversions to language specific date/time +values are created, and when converting this to a timestamp. + +Drivers SHOULD have an accessor method on an ObjectID class for obtaining the timestamp value. + +### Random Value + +A 5-byte field consisting of a random value generated once per process. This random value is unique to the machine and +process. + +Drivers MUST NOT have an accessor method on an ObjectID class for obtaining this value. + +The random number does not have to be cryptographic. If possible, use a PRNG with OS supplied entropy that SHOULD NOT +block to wait for more entropy to become available. Otherwise, seed a deterministic PRNG to ensure uniqueness of process +and machine by combining time, process ID, and hostname. + +### Counter + +A 3-byte big endian counter. + +This counter MUST be initialised to a random value when the driver is first activated. After initialisation, the counter +MUST be increased by 1 for every ObjectID creation. + +When the counter overflows (i.e., hits 16777215+1), the counter MUST be reset to 0. + +Drivers MUST NOT have an accessor method on an ObjectID class for obtaining this value. + +The random number does not have to be cryptographic. If possible, use a PRNG with OS supplied entropy that SHOULD NOT +block to wait for more entropy to become available. Otherwise, seed a deterministic PRNG to ensure uniqueness of process +and machine by combining time, process ID, and hostname. + +## Test Plan + +Drivers MUST: + +- Ensure that the Timestamp field is represented as an unsigned 32-bit representing the number of seconds since the + Epoch for the Timestamp values: + - `0x00000000`: To match `"Jan 1st, 1970 00:00:00 UTC"` + - `0x7FFFFFFF`: To match `"Jan 19th, 2038 03:14:07 UTC"` + - `0x80000000`: To match `"Jan 19th, 2038 03:14:08 UTC"` + - `0xFFFFFFFF`: To match `"Feb 7th, 2106 06:28:15 UTC"` +- Ensure that the Counter field successfully overflows its sequence from `0xFFFFFF` to `0x000000`. +- Ensure that after a new process is created through a fork() or similar process creation operation, the "random number + unique to a machine and process" is no longer the same as the parent process that created the new process. + +## Motivation for Change + +Besides the specific exclusion of MD5 as an allowed hashing algorithm, the information in this specification is meant to +align the ObjectID generation algorithm of both drivers and the server. + +## Design Rationale + +**Timestamp:** The timestamp is a 32-bit **unsigned** integer, as it allows us to extend the furthest date that the +timestamp can represent from the year 2038 to 2106. There is no reason why MongoDB would generate a timestamp to mean a +date before 1970, as MongoDB did not exist back then. + +**Random Value:** Originally, this field consisted of the Machine ID and Process ID fields. There were numerous +divergences between drivers due to implementation choices, and the Machine ID field traditionally used the MD5 hashing +algorithm which can't be used on FIPS compliant machines. In order to allow for a similar behaviour among all drivers +**and** the MongoDB Server, these two fields have been collated together into a single 5-byte random value, unique to a +machine and process. + +**Counter:** The counter makes it possible to have multiple ObjectIDs per second, per server, and per process. As the +counter can overflow, there is a possibility of having duplicate ObjectIDs if you create more than 16 million ObjectIDs +per second in the same process on a single machine. + +**Endianness:** The *Timestamp* and *Counter* are big endian because we can then use `memcmp` to order ObjectIDs, and we +want to ensure an increasing order. + +## Backwards Compatibility + +This specification requires that the existing *Machine ID* and *Process ID* fields are merged into a single 5-byte +value. This will change the behaviour of ObjectID generation, as well as the behaviour of drivers that currently have +getters and setters for the original *Machine ID* and *Process ID* fields. + +## Reference Implementation + +Currently there is no full reference implementation yet. + +## Changelog + +- 2024-07-30: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2019-01-14: Clarify that the random numbers don't need to be cryptographically secure. Add a test to test that the + unique value is different in forked processes. + +- 2018-10-11: Clarify that the *Timestamp* and *Counter* fields are big endian, and add the reason why. + +- 2018-07-02: Replaced Machine ID and Process ID fields with a single 5-byte unique value + +- 2018-05-22: Initial Release diff --git a/source/causal-consistency/causal-consistency.md b/source/causal-consistency/causal-consistency.md new file mode 100644 index 0000000000..07f45b2715 --- /dev/null +++ b/source/causal-consistency/causal-consistency.md @@ -0,0 +1,434 @@ +# Causal Consistency Specification + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +Version 3.6 of the server introduces support for causal consistency. This spec builds upon the Sessions Specification to +define how an application requests causal consistency and how a driver interacts with the server to implement causal +consistency. + +## Definitions + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Terms + +**Causal consistency** + +A property that guarantees that an application can read its own writes and that a later read will never observe a +version of the data that is older than an earlier read. + +**ClientSession** + +The driver object representing a client session and the operations that can be performed on it. + +**Cluster time** + +The current cluster time. The server reports its view of the current cluster time in the `$clusterTime` field in +responses from the server and the driver participates in distributing the current cluster time to all nodes (called +"gossipping the cluster time") by sending the highest `$clusterTime` it has seen so far in messages it sends to mongos +servers. The current cluster time is a logical time, but is digitally signed to prevent malicious clients from +propagating invalid cluster times. Cluster time is only used in replica sets and sharded clusters. + +**Logical time** + +A time-like quantity that can be used to determine the order in which events occurred. Logical time is represented as a +BsonTimestamp. + +**MongoClient** + +The root object of a driver's API. MAY be named differently in some drivers. + +**MongoCollection** + +The driver object representing a collection and the operations that can be performed on it. MAY be named differently in +some drivers. + +**MongoDatabase** + +The driver object representing a database and the operations that can be performed on it. MAY be named differently in +some drivers. + +**Operation time** + +The logical time at which an operation occurred. The server reports the operation time in the response to all commands, +including error responses. The operation time by definition is always less than or equal to the cluster time. Operation +times are tracked on a per `ClientSession` basis, so the `operationTime` of each `ClientSession` corresponds to the time +of the last operation performed in that particular `ClientSession`. + +**ServerSession** + +The driver object representing a server session. + +**Session** + +A session is an abstract concept that represents a set of sequential operations executed by an application that are +related in some way. This specification defines how sessions are used to implement causal consistency. + +**Unacknowledged writes** + +Unacknowledged writes are write operations that are sent to the server without waiting for a reply acknowledging the +write. See the "Unacknowledged Writes" section below for information on how unacknowledged writes interact with causal +consistency. + +## Specification + +An application requests causal consistency by creating a `ClientSession` with options that specify that causal +consistency is desired. An application then passes the session as an argument to methods in the `MongoDatabase` and +`MongoCollection` classes. Any operations performed against that session will then be causally consistent. + +## Naming variations + +This specification defines names for new methods and types. To the extent possible you SHOULD use these names in your +driver. However, where your driver's and/or language's naming conventions differ you SHOULD continue to use them +instead. For example, you might use `StartSession` or `start_session` instead of `startSession`. + +## High level summary of the API changes for causal consistency + +Causal consistency is built on top of client sessions. + +Applications will start a new client session for causal consistency like this: + +```typescript +options = new SessionOptions(causalConsistency = true); +session = client.startSession(options); +``` + +All read and write operations performed using this session will now be causally consistent. + +If no value is provided for `causalConsistency` and snapshot reads are not requested a value of true is implied. See the +`causalConsistency` section. + +## MongoClient changes + +There are no API changes to `MongoClient` to support causal consistency. Applications indicate whether they want causal +consistency by setting the `causalConsistency` field in the options passed to the `startSession` method. + +## SessionOptions changes + +`SessionOptions` change summary + +```typescript +class SessionOptions { + Optional causalConsistency; + + // other options defined by other specs +} +``` + +In order to support causal consistency a new property named `causalConsistency` is added to `SessionOptions`. +Applications set `causalConsistency` when starting a client session to indicate whether they want causal consistency. +All read and write operations performed using that client session are then causally consistent. + +Each new member is documented below. + +### causalConsistency + +Applications set `causalConsistency` when starting a session to indicate whether they want causal consistency. + +Note that the `causalConsistency` property is optional. For explicit sessions, the default value of this property is +`not supplied`. If no value is supplied for `causalConsistency` the value will be inherited. Currently it is inherited +from the global default which is defined to be true. In the future it *might* be inherited from client settings. For +implicit sessions, the value of this property MUST be set to `false` in order to avoid potential conflicts with an +operation's read concern level. + +Causal consistency is provided at the session level by tracking the `clusterTime` and `operationTime` for each session. +In some cases an application may wish subsequent operations in one session to be causally consistent with operations +that were executed in a different session. In that case the application can call the `advanceClusterTime` and +`advanceOperationTime` methods in `ClientSession` to advance the `clusterTime` and `operationTime` of one session to the +`clusterTime` and `operationTime` from another session. + +## ClientSession changes + +`ClientSession` changes summary + +```typescript +interface ClientSession { + Optional operationTime; + + void advanceOperationTime(BsonTimestamp operationTime); + + // other members as defined in other specs +} +``` + +Each new member is documented below. + +### operationTime + +This property returns the operation time of the most recent operation performed using this session. If no operations +have been performed using this session the value will be null unless `advanceOperationTime` has been called. This value +will also be null when the cluster does not report operation times. + +### advanceOperationTime + +This method advances the `operationTime` for a session. If the new `operationTime` is greater than the session's current +`operationTime` then the session's `operationTime` MUST be advanced to the new `operationTime`. If the new +`operationTime` is less than or equal to the session's current `operationTime` then the session's `operationTime` MUST +NOT be changed. + +Drivers MUST NOT attempt to validate the supplied `operationTime`. While the server requires that `operationTime` be +less than or equal to `clusterTime` we don't want to check that when `advanceOperationTime` is called. This allows an +application to call `advanceClusterTime` and `advanceOperationTime` in any order, or perhaps to not call +`advanceClusterTime` at all and let the `clusterTime` that is sent to the server be implied by the `clusterTime` in +`MongoClient`. + +## MongoDatabase changes + +There are no additional API changes to `MongoDatabase` beyond those specified in the Sessions Specification. All +`MongoDatabase` methods that talk to the server have been overloaded to take a session parameter. If that session was +started with `causalConsistency = true` then all operations using that session will be causally consistent. + +## MongoCollection changes + +There are no additional API changes to `MongoCollection` beyond those specified in the Sessions Specification. All +`MongoCollection` methods that talk to the server have been overloaded to take a session parameter. If that session was +started with `causalConsistency = true` then all operations using that session will be causally consistent. + +## Server Commands + +There are no new server commands related to causal consistency. Instead, causal consistency is implemented by: + +1. Saving the `operationTime` returned by 3.6+ servers for all operations in a property of the `ClientSession` object. + The server reports the `operationTime` whether the operation succeeded or not and drivers MUST save the + `operationTime` in the `ClientSession` whether the operation succeeded or not. +2. Passing that `operationTime` in the `afterClusterTime` field of the `readConcern` field for subsequent causally + consistent read and write operations (for all commands that support a `readConcern`) +3. Gossiping clusterTime (described in the Driver Session Specification) + +## Server Command Responses + +To support causal consistency the server returns the `operationTime` in responses it sends to the driver (for both read +and write operations). + +```typescript +{ + ok : 1 or 0, + ... // the rest of the command reply + operationTime : + $clusterTime : // only in deployments that support cluster times +} +``` + +The `operationTime` MUST be stored in the `ClientSession` to later be passed as the `afterClusterTime` field of the +`readConcern` field in subsequent causally consistent read and write operations. The `operationTime` is returned whether +the command succeeded or not and MUST be stored in either case. + +Drivers MUST examine all responses from the server for the presence of an `operationTime` field and store the value in +the `ClientSession`. + +When connected to a standalone node command replies do not include an `operationTime` field. All operations against a +standalone node are causally consistent automatically because there is only one node. + +When connected to a deployment that supports cluster times the command response also includes a field called +`$clusterTime` that drivers MUST use to gossip the cluster time. See the Sessions Specification for details. + +## Causally consistent read and write commands + +For causal consistency the driver MUST send the `operationTime` saved in the `ClientSession` as the value of the +`afterClusterTime` field of the `readConcern` field for read and write commands: + +```typescript +{ + find : , // or other read or write command + ... // the rest of the command parameters + readConcern : + { + level : ..., // from the operation's read concern (only if specified) + afterClusterTime : + } +} +``` + +For the list of commands that support causally consistent reads and writes, see the +[Read Concern](../read-write-concern/read-write-concern.md#afterclustertime) spec. + +The driver MUST merge the `ReadConcern` specified for the operation with the `operationTime` from the `ClientSession` +(which goes in the `afterClusterTime` field) to generate the combined `readConcern` to send to the server. If the level +property of the read concern for the operation is null then the driver MUST NOT include a `level` field alongside the +`afterClusterTime` of the `readConcern` value sent to the server. Drivers MUST NOT attempt to verify whether the server +supports causally consistent reads or not for a given read concern level. The server will return an error if a given +level does not support causal consistency. + +The Read and Write Concern specification states that when a user has not specified a `ReadConcern` or has specified the +server's default `ReadConcern`, drivers MUST omit the `ReadConcern` parameter when sending the command. For causally +consistent reads and writes this requirement is modified to state that when the `ReadConcern` parameter would normally +be omitted drivers MUST send a `ReadConcern` after all because that is how the `afterClusterTime` value is sent to the +server. + +The Read and Write Concern Specification states that drivers MUST NOT add a `readConcern` field to commands that are run +using a generic `runCommand` method. The same is true for causal consistency, so commands that are run using +`runCommand` MUST NOT have an `afterClusterTime` field added to them. + +When executing a causally consistent operation, the `afterClusterTime` field MUST be sent when connected to a deployment +that supports cluster times, and MUST NOT be sent when connected to a deployment that does not support cluster times. + +## Unacknowledged writes + +The implementation of causal consistency relies on the `operationTime` returned by the server in the acknowledgement of +a write. Since unacknowledged writes don't receive a response from the server (or don't wait for a response) the +`ClientSession`'s `operationTime` is not updated after an unacknowledged write. That means that a causally consistent +read after an unacknowledged write cannot be causally consistent with the unacknowledged write. Rather than prohibiting +unacknowledged writes in a causally consistent session we have decided to accept this limitation. Drivers MUST document +that causally consistent operations are not causally consistent with unacknowledged writes. + +## Test Plan + +Below is a list of test cases to write. + +Note: some tests are only relevant to certain deployments. For the purpose of deciding which tests to run assume that +any deployment that is version 3.6 or higher and is either a replica set or a sharded cluster supports cluster times. + +1. When a `ClientSession` is first created the `operationTime` has no value. + - `session = client.startSession()` + - assert `session.operationTime` has no value +2. The first read in a causally consistent session must not send `afterClusterTime` to the server (because the + `operationTime` has not yet been determined) + - `session = client.startSession(causalConsistency = true)` + - `document = collection.anyReadOperation(session, ...)` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command does not have an `afterClusterTime` +3. The first read or write on a `ClientSession` should update the `operationTime` of the `ClientSession`, even if there + is an error. + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession() // with or without causal consistency` + - `collection.anyReadOrWriteOperation(session, ...) // test with errors also if possible` + - capture the response sent from the server (using APM or other mechanism) + - assert `session.operationTime` has the same value that is in the response from the server +4. A `findOne` followed by any other read operation (test them all) should include the `operationTime` returned by the + server for the first operation in the `afterClusterTime` parameter of the second operation + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession(causalConsistency = true)` + - `collection.findOne(session, {})` + - `operationTime = session.operationTime` + - `collection.anyReadOperation(session, ...)` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command has an `afterClusterTime` field with a value of `operationTime` +5. Any write operation (test them all) followed by a `findOne` operation should include the `operationTime` of the + first operation in the `afterClusterTime` parameter of the second operation, including the case where the first + operation returned an error. + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession(causalConsistency = true)` + - `collection.anyWriteOperation(session, ...) // test with errors also where possible` + - `operationTime = session.operationTime` + - `collection.findOne(session, {})` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command has an `afterClusterTime` field with a value of `operationTime` +6. A read operation in a `ClientSession` that is not causally consistent should not include the `afterClusterTime` + parameter in the command sent to the server. + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession(causalConsistency = false)` + - `collection.anyReadOperation(session, {})` + - `operationTime = session.operationTime` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command does not have an `afterClusterTime` field +7. A read operation in a causally consistent session against a deployment that does not support cluster times does not + include the `afterClusterTime` parameter in the command sent to the server. + - skip this test if connected to a deployment that does support cluster times + - `session = client.startSession(causalConsistency = true)` + - `collection.anyReadOperation(session, {})` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command does not have an `afterClusterTime` field +8. When using the default server `ReadConcern` the `readConcern` parameter in the command sent to the server should not + include a `level` field. + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession(causalConsistency = true)` + - configure `collection` to use default server `ReadConcern` + - `collection.findOne(session, {})` + - `operationTime = session.operationTime` + - `collection.anyReadOperation(session, ...)` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command does not have a `` `level `` field + - assert that the command has a `afterClusterTime` field with a value of `operationTime` +9. When using a custom `ReadConcern` the `readConcern` field in the command sent to the server should be a merger of + the `ReadConcern` value and the `afterClusterTime` field. + - skip this test if connected to a deployment that does not support cluster times + - `session = client.startSession(causalConsistency = true)` + - configure collection to use a custom ReadConcern + - `collection.findOne(session, {})` + - `operationTime = session.operationTime` + - `collection.anyReadOperation(session, ...)` + - capture the command sent to the server (using APM or other mechanism) + - assert that the command has a `level` field with a value matching the custom readConcern + - assert that the command has an `afterClusterTime` field with a value of `operationTime` +10. **Removed** +11. When connected to a deployment that does not support cluster times messages sent to the server should not include + `$clusterTime`. + - skip this test when connected to a deployment that does support cluster times + - `document = collection.findOne({})` + - capture the command sent to the server + - assert that the command does not include a `$clusterTime` field +12. When connected to a deployment that does support cluster times messages sent to the server should include + `$clusterTime`. + - skip this test when connected to a deployment that does not support cluster times + - `document = collection.findOne({})` + - capture the command sent to the server + - assert that the command includes a `$clusterTime` field + +## Motivation + +To support causal consistency. Only supported with server version 3.6 or newer. + +## Design Rationale + +The goal is to modify the driver API as little as possible so that existing programs that don't need causal consistency +don't have to be changed. This goal is met by defining a `SessionOptions` field that applications use to start a +`ClientSession` that can be used for causal consistency. Any operations performed with such a session are then causally +consistent. + +The `operationTime` is tracked on a per `ClientSession` basis. This allows each `ClientSession` to have an +`operationTime` that is sufficiently new to guarantee causal consistency for that session, but no newer. Using an +`operationTime` that is newer than necessary can cause reads to block longer than necessary when sent to a lagging +secondary. The goal is to block for just long enough to guarantee causal consistency and no longer. + +## Backwards Compatibility + +The API changes to support sessions extend the existing API but do not introduce any backward breaking changes. Existing +programs that don't use causal consistency continue to compile and run correctly. + +## Reference Implementation + +A reference implementation must be completed before any spec is given status "Final", but it need not be completed +before the spec is "Accepted". While there is merit to the approach of reaching consensus on the specification and +rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to +resolving many discussions of spec details. A final reference implementation must include test code and documentation. + +## Q&A + +## Changelog + +- 2026-05-04: Require `afterClusterTime` on all write commands in causally-consistent sessions, not only on read + commands. + +- 2024-02-08: Migrated from reStructuredText to Markdown. + +- 2022-11-11: Require `causalConsistency=false` for implicit sessions. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-28: Fix formatting for prose tests + +- 2022-01-22: Remove outdated prose test #10 + +- 2021-06-26: Default value for causalConsistency is influenced by snapshot reads + +- 2017-11-17: Added link to ReadConcern spec which lists commands that support readConcern + +- 2017-10-06: advanceOperationTime MUST NOT validate operationTime + +- 2017-10-05: How to handle default read concern + +- 2017-10-04: Added advanceOperationTime + +- 2017-09-28: Remove remaining references to collections being associated with sessions. Update spec to reflect that + replica sets use $clusterTime also now. + +- 2017-09-13: Renamed "causally consistent reads" to "causal consistency". If no value is supplied for + `causallyConsistent` assume true. diff --git a/source/causal-consistency/causal-consistency.rst b/source/causal-consistency/causal-consistency.rst index 1b16d69dc3..1853a1eafd 100644 --- a/source/causal-consistency/causal-consistency.rst +++ b/source/causal-consistency/causal-consistency.rst @@ -1,524 +1,4 @@ -================================ -Causal Consistency Specification -================================ -:Spec Title: Causal Consistency Specification (See the registry of specs) -:Spec Version: 1.0 -:Author: Robert Stam -:Spec Lead: A\. Jesse Jiryu Davis -:Advisory Group: Jeremy Mikola, Jeff Yemin, Misha Tyulene, A. Jesse Jiryu Davis -:Approver(s): A\. Jesse Jiryu Davis, Jeff Yemin, Bernie Hackett, David Golden, Matt Broadstone, Andy Schwerin, Kal Manassiev, Eliot -:Informed: drivers@, Bryan Reinero, Christopher Hendel -:Status: Accepted (Could be Draft, Accepted, Rejected, Final, or Replaced) -:Type: Standards -:Minimum Server Version: 3.6 (The minimum server version this spec applies to) -:Last Modified: 17-Nov-2017 - -.. contents:: - --------- - -Abstract -======== - -Version 3.6 of the server introduces support for causal consistency. -This spec builds upon the Sessions Specification to define how an application -requests causal consistency and how a driver interacts with the server -to implement causal consistency. - -Definitions -=========== - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Terms ------ - -Causal consistency - A property that guarantees that an application can read its own writes and that - a later read will never observe a version of the data that is older than an - earlier read. - -ClientSession - The driver object representing a client session and the operations that can be - performed on it. - -Cluster time - The current cluster time. The server reports its view of the current cluster - time in the ``$clusterTime`` field in responses from the server and the driver - participates in distributing the current cluster time to all nodes (called - "gossipping the cluster time") by sending the highest ``$clusterTime`` it has seen - so far in messages it sends to mongos servers. The current cluster time is a - logical time, but is digitally signed to prevent malicious clients from - propagating invalid cluster times. Cluster time is only used in replica sets - and sharded clusters. - -Logical time - A time-like quantity that can be used to determine the order in which events - occurred. Logical time is represented as a BsonTimestamp. - -MongoClient - The root object of a driver's API. MAY be named differently in some drivers. - -MongoCollection - The driver object representing a collection and the operations that can be - performed on it. MAY be named differently in some drivers. - -MongoDatabase - The driver object representing a database and the operations that can be - performed on it. MAY be named differently in some drivers. - -Operation time - The logical time at which an operation occurred. The server reports the - operation time in the response to all commands, including error responses. The - operation time by definition is always less than or equal to the cluster time. - Operation times are tracked on a per ``ClientSession`` basis, so the ``operationTime`` - of each ``ClientSession`` corresponds to the time of the last operation performed - in that particular ``ClientSession``. - -ServerSession - The driver object representing a server session. - -Session - A session is an abstract concept that represents a set of sequential - operations executed by an application that are related in some way. This - specification defines how sessions are used to implement causal - consistency. - -Unacknowledged writes - Unacknowledged writes are write operations that are sent to the server without - waiting for a reply acknowledging the write. See the "Unacknowledged Writes" - section below for information on how unacknowledged writes interact with - causal consistency. - -Specification -============= - -An application requests causal consistency by creating a ``ClientSession`` -with options that specify that causal consistency is desired. An -application then passes the session as an argument to methods in the -``MongoDatabase`` and ``MongoCollection`` classes. Any operations performed against -that session will then be causally consistent. - -Naming variations -================= - -This specification defines names for new methods and types. To the extent -possible you SHOULD use these names in your driver. However, where your -driver's and/or language's naming conventions differ you SHOULD continue to use -them instead. For example, you might use ``StartSession`` or ``start_session`` instead -of ``startSession``. - -High level summary of the API changes for causal consistency -============================================================ - -Causal consistency is built on top of client sessions. - -Applications will start a new client session for causal consistency like -this: - -.. code:: typescript - - options = new SessionOptions(causalConsistency = true); - session = client.startSession(options); - -All read operations performed using this session will now be causally -consistent. - -If no value is provided for ``causalConsistency`` a value of true is -implied. See the ``causalConsistency`` section. - -MongoClient changes -=================== - -There are no API changes to ``MongoClient`` to support causal consistency. -Applications indicate whether they want causal consistency by setting the -``causalConsistency`` field in the options passed to the ``startSession`` method. - -SessionOptions changes -====================== - -``SessionOptions`` change summary - -.. code:: typescript - - class SessionOptions { - Optional causalConsistency; - - // other options defined by other specs - } - -In order to support causal consistency a new property named -``causalConsistency`` is added to ``SessionOptions``. Applications set -``causalConsistency`` when starting a client session to indicate -whether they want causal consistency. All read operations performed -using that client session are then causally consistent. - -Each new member is documented below. - -causalConsistency ------------------ - -Applications set ``causalConsistency`` when starting a session to -indicate whether they want causal consistency. - -Note that the ``causalConsistency`` property is optional. The default value of -this property is ``not supplied``. If no value is supplied for -``causalConsistency`` the value will be inherited. Currently it is inherited -from the global default which is defined to be true. In the future it *might* -be inherited from client settings. - -Causal consistency is provided at the session level by tracking the ``clusterTime`` -and ``operationTime`` for each session. In some cases an application may wish -subsequent operations in one session to be causally consistent with operations -that were executed in a different session. In that case the application can call -the ``advanceClusterTime`` and ``advanceOperationTime`` methods in ``ClientSession`` to -advance the ``clusterTime`` and ``operationTime`` of one session to the ``clusterTime`` and -``operationTime`` from another session. - -ClientSession changes -===================== - -``ClientSession`` changes summary - -.. code:: typescript - - interface ClientSession { - Optional operationTime; - - void advanceOperationTime(BsonTimestamp operationTime); - - // other members as defined in other specs - } - -Each new member is documented below. - -operationTime -------------- - -This property returns the operation time of the most recent operation performed -using this session. If no operations have been performed using this session the value will be -null unless ``advanceOperationTime`` has been called. -This value will also be null when the cluster does not report -operation times. - -advanceOperationTime --------------------- - -This method advances the ``operationTime`` for a session. If the new -``operationTime`` is greater than the session's current ``operationTime`` then the -session's ``operationTime`` MUST be advanced to the new ``operationTime``. If the -new ``operationTime`` is less than or equal to the session's current -``operationTime`` then the session's ``operationTime`` MUST NOT be changed. - -Drivers MUST NOT attempt to validate the supplied ``operationTime``. While the -server requires that ``operationTime`` be less than or equal to ``clusterTime`` -we don't want to check that when ``advanceOperationTime`` is called. This -allows an application to call ``advanceClusterTime`` and -``advanceOperationTime`` in any order, or perhaps to not call -``advanceClusterTime`` at all and let the ``clusterTime`` that is sent to the -server be implied by the ``clusterTime`` in ``MongoClient``. - -MongoDatabase changes -===================== - -There are no additional API changes to ``MongoDatabase`` beyond those specified in -the Sessions Specification. All ``MongoDatabase`` methods that talk to the server -have been overloaded to take a session parameter. If that session was started -with ``causalConsistency = true`` then all operations using that session will -be causally consistent. - -MongoCollection changes -======================= - -There are no additional API changes to ``MongoCollection`` beyond those specified -in the Sessions Specification. All ``MongoCollection`` methods that talk to the -server have been overloaded to take a session parameter. If that session was -started with ``causalConsistency = true`` then all operations using that -session will be causally consistent. - -Server Commands -=============== - -There are no new server commands related to causal consistency. Instead, -causal consistency is implemented by: - -1. Saving the ``operationTime`` returned by 3.6+ servers for all operations in a - property of the ``ClientSession`` object. The server reports the ``operationTime`` - whether the operation succeeded or not and drivers MUST save the ``operationTime`` - in the ``ClientSession`` whether the operation succeeded or not. - -2. Passing that ``operationTime`` in the ``afterClusterTime`` field of the ``readConcern`` field - for subsequent causally consistent read operations (for all commands that - support a ``readConcern``) - -3. Gossiping clusterTime (described in the Driver Session Specification) - -Server Command Responses -======================== - -To support causal consistency the server returns the ``operationTime`` in -responses it sends to the driver (for both read and write operations). - -.. code:: typescript - - { - ok : 1 or 0, - ... // the rest of the command reply - operationTime : - $clusterTime : // only in deployments that support cluster times - } - -The ``operationTime`` MUST be stored in the ``ClientSession`` to later be passed as the -``afterClusterTime`` field of the ``readConcern`` field in subsequent read operations. The -``operationTime`` is returned whether the command succeeded or not and MUST be -stored in either case. - -Drivers MUST examine all responses from the server for the presence of an -``operationTime`` field and store the value in the ``ClientSession``. - -When connected to a standalone node command replies do not include an -``operationTime`` field. All operations against a standalone node are causally -consistent automatically because there is only one node. - -When connected to a deployment that supports cluster times the command response also includes a field -called ``$clusterTime`` that drivers MUST use to gossip the cluster time. See the -Sessions Specification for details. - -Causally consistent read commands -================================= - -For causal consistency the driver MUST send the ``operationTime`` saved in -the ``ClientSession`` as the value of the ``afterClusterTime`` field of the -``readConcern`` field: - -.. code:: typescript - - { - find : , // or other read command - ... // the rest of the command parameters - readConcern : - { - level : ..., // from the operation's read concern (only if specified) - afterClusterTime : - } - } - -For the lists of commands that support causally consistent reads, see `ReadConcern`_ spec. - -.. _ReadConcern: https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst#read-concern/ - -The driver MUST merge the ``ReadConcern`` specified for the operation with the -``operationTime`` from the ``ClientSession`` (which goes in the ``afterClusterTime`` field) -to generate the combined ``readConcern`` to send to the server. If the level -property of the read concern for the operation is null then the driver MUST NOT -include a ``level`` field alongside the ``afterClusterTime`` of the ``readConcern`` -value sent to the -server. Drivers MUST NOT attempt to verify whether the server supports causally -consistent reads or not for a given read concern level. The server will return -an error if a given level does not support causal consistency. - -The Read and Write Concern specification states that when a user has not specified a -``ReadConcern`` or has specified the server's default ``ReadConcern``, drivers MUST -omit the ``ReadConcern`` parameter when sending the command. For causally -consistent reads this requirement is modified to state that when the -``ReadConcern`` parameter would normally be omitted drivers MUST send a ``ReadConcern`` -after all because that is how the ``afterClusterTime`` value is sent to the server. - -The Read and Write Concern Specification states that drivers MUST NOT add a -``readConcern`` field to commands that are run using a generic ``runCommand`` method. -The same is true for causal consistency, so commands that are run using ``runCommand`` -MUST NOT have an ``afterClusterTime`` field added to them. - -When executing a causally consistent read, the ``afterClusterTime`` field MUST be -sent when connected to a deployment that supports cluster times, and MUST NOT be sent -when connected to a deployment that does not support cluster times. - -Unacknowledged writes -===================== - -The implementation of causal consistency relies on the ``operationTime`` -returned by the server in the acknowledgement of a write. Since unacknowledged -writes don't receive a response from the server (or don't wait for a response) -the ``ClientSession``'s ``operationTime`` is not updated after an unacknowledged write. -That means that a causally consistent read after an unacknowledged write cannot -be causally consistent with the unacknowledged write. Rather than prohibiting -unacknowledged writes in a causally consistent session we have decided to -accept this limitation. Drivers MUST document that causally consistent reads -are not causally consistent with unacknowledged writes. - -Test Plan -========= - -Below is a list of test cases to write. - -Note: some tests are only relevant to certain deployments. For the purpose of deciding -which tests to run assume that any deployment that is version 3.6 or higher and is either a -replica set or a sharded cluster supports cluster times. - -1. When a ``ClientSession`` is first created the ``operationTime`` has no value - * ``session = client.startSession()`` - * assert ``session.operationTime`` has no value - -2. The first read in a causally consistent session must not send - ``afterClusterTime`` to the server (because the ``operationTime`` has not yet - been determined) - * ``session = client.startSession(causalConsistency = true)`` - * ``document = collection.anyReadOperation(session, ...)`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command does not have an ``afterClusterTime`` - -3. The first read or write on a ``ClientSession`` should update the - ``operationTime`` of the ``ClientSession``, even if there is an error - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession() // with or without causal consistency`` - * ``collection.anyReadOrWriteOperation(session, ...) // test with errors also if possible`` - * capture the response sent from the server (using APM or other mechanism) - * assert ``session.operationTime`` has the same value that is in the response from the server - -4. A ``findOne`` followed by any other read operation (test them all) should - include the ``operationTime`` returned by the server for the first operation in - the ``afterClusterTime`` parameter of the second operation - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession(causalConsistency = true)`` - * ``collection.findOne(session, {})`` - * ``operationTime = session.operationTime`` - * ``collection.anyReadOperation(session, ...)`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command has an ``afterClusterTime`` field with a value of ``operationTime`` - -5. Any write operation (test them all) followed by a ``findOne`` operation should - include the ``operationTime`` of the first operation in the ``afterClusterTime`` - parameter of the second operation, including the case where the first operation - returned an error - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession(causalConsistency = true)`` - * ``collection.anyWriteOperation(session, ...) // test with errors also where possible`` - * ``operationTime = session.operationTime`` - * ``collection.findOne(session, {})`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command has an ``afterClusterTime`` field with a value of ``operationTime`` - -6. A read operation in a ``ClientSession`` that is not causally consistent - should not include the ``afterClusterTime`` parameter in the command sent to the - server - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession(causalConsistency = false)`` - * ``collection.anyReadOperation(session, {})`` - * ``operationTime = session.operationTime`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command does not have an ``afterClusterTime`` field - -7. A read operation in a causally consistent session against a deployment that does not support cluster times does - not include the ``afterClusterTime`` parameter in the command sent to the server - * skip this test if connected to a deployment that does support cluster times - * ``session = client.startSession(causalConsistency = true)`` - * ``collection.anyReadOperation(session, {})`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command does not have an ``afterClusterTime`` field - -8. When using the default server ``ReadConcern`` the ``readConcern`` parameter in the - command sent to the server should not include a ``level`` field - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession(causalConsistency = true)`` - * configure ``collection`` to use default server ``ReadConcern`` - * ``collection.findOne(session, {})`` - * ``operationTime = session.operationTime`` - * ``collection.anyReadOperation(session, ...)`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command does not have a ```level`` field - * assert that the command has a ``afterClusterTime`` field with a value of ``operationTime`` - -9. When using a custom ``ReadConcern`` the ``readConcern`` field in the command sent to - the server should be a merger of the ``ReadConcern`` value and the ``afterClusterTime`` - field - * skip this test if connected to a deployment that does not support cluster times - * ``session = client.startSession(causalConsistency = true)`` - * configure collection to use a custom ReadConcern - * ``collection.findOne(session, {})`` - * ``operationTime = session.operationTime`` - * ``collection.anyReadOperation(session, ...)`` - * capture the command sent to the server (using APM or other mechanism) - * assert that the command has a ``level`` field with a value matching the custom readConcern - * assert that the command has an ``afterClusterTime`` field with a value of ``operationTime`` - -10. When an unacknowledged write is executed in a causally consistent - ``ClientSession`` the ``operationTime`` property of the ``ClientSession`` is - not updated - * ``session = client.startSession(causalConsistency = true)`` - * configure the collection to use ``{ w : 0 }`` unacknowledged writes - * ``collection.anyWriteOperation(session, ...)`` - * assert ``session.operationTime`` does not have a value - -11. When connected to a deployment that does not support cluster times messages sent to - the server should not include ``$clusterTime`` - * skip this test when connected to a deployment that does support cluster times - * ``document = collection.findOne({})`` - * capture the command sent to the server - * assert that the command does not include a ``$clusterTime`` field - -12. When connected to a deployment that does support cluster times messages sent to the server should - include ``$clusterTime`` - * skip this test when connected to a deployment that does not support cluster times - * ``document = collection.findOne({})`` - * capture the command sent to the server - * assert that the command includes a ``$clusterTime`` field - -Motivation -========== - -To support causal consistency. Only supported with server version 3.6 or newer. - -Design Rationale -================ - -The goal is to modify the driver API as little as possible so that existing -programs that don't need causal consistency don't have to be changed. -This goal is met by defining a ``SessionOptions`` field that applications use to -start a ``ClientSession`` that can be used for causal consistency. Any -operations performed with such a session are then causally consistent. - -The ``operationTime`` is tracked on a per ``ClientSession`` basis. This allows each -``ClientSession`` to have an ``operationTime`` that is sufficiently new to guarantee -causal consistency for that session, but no newer. Using an ``operationTime`` that -is newer than necessary can cause reads to block longer than necessary when -sent to a lagging secondary. The goal is to block for just long enough to -guarantee causal consistency and no longer. - -Backwards Compatibility -======================= - -The API changes to support sessions extend the existing API but do not -introduce any backward breaking changes. Existing programs that don't use -causal consistency continue to compile and run correctly. - -Reference Implementation -======================== - -A reference implementation must be completed before any spec is given status -"Final", but it need not be completed before the spec is “Accepted”. While -there is merit to the approach of reaching consensus on the specification and -rationale before writing code, the principle of "rough consensus and running -code" is still useful when it comes to resolving many discussions of spec -details. A final reference implementation must include test code and -documentation. - -Q&A -=== - -Changelog -========= - -- 2017-09-13: Renamed "causally consistent reads" to "causal consistency" -- 2017-09-13: If no value is supplied for ``causallyConsistent`` assume true -- 2017-09-28: Remove remaining references to collections being associated with sessions -- 2017-09-28: Update spec to reflect that replica sets use $clusterTime also now -- 2017-10-04: Added advanceOperationTime -- 2017-10-05: How to handle default read concern -- 2017-10-06: advanceOperationTime MUST NOT validate operationTime -- 2017-11-17 : Added link to ReadConcern spec which lists commands that support readConcern +.. note:: + This specification has been converted to Markdown and renamed to + `causal-consistency.md `_. diff --git a/source/causal-consistency/tests/causal-consistency-clientBulkWrite.json b/source/causal-consistency/tests/causal-consistency-clientBulkWrite.json new file mode 100644 index 0000000000..c2a04422dd --- /dev/null +++ b/source/causal-consistency/tests/causal-consistency-clientBulkWrite.json @@ -0,0 +1,151 @@ +{ + "description": "causal consistency bulkWrite include afterClusterTime", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "causal-consistency-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "causalConsistency": true + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "causal-consistency-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "clientBulkWrite includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "causal-consistency-tests.test", + "document": { + "_id": 4 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "command": { + "bulkWrite": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/causal-consistency/tests/causal-consistency-clientBulkWrite.yml b/source/causal-consistency/tests/causal-consistency-clientBulkWrite.yml new file mode 100644 index 0000000000..d1902e188e --- /dev/null +++ b/source/causal-consistency/tests/causal-consistency-clientBulkWrite.yml @@ -0,0 +1,75 @@ +description: "causal consistency bulkWrite include afterClusterTime" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "8.0" + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName causal-consistency-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + sessionOptions: + causalConsistency: true + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +# In a causally consistent session, once an operationTime has been established by a prior +# operation, subsequent write commands MUST include readConcern.afterClusterTime so the +# server can apply the write causally after the previously-observed data. + +tests: + - description: "clientBulkWrite includes afterClusterTime in causally consistent session" + operations: + - name: find + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: [{ _id: 1, x: 11 }] + - name: clientBulkWrite + object: *client0 + arguments: + session: *session0 + models: + - insertOne: + namespace: causal-consistency-tests.test + document: { _id: 4 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: find + command: + find: *collectionName + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + - commandStartedEvent: + commandName: bulkWrite + command: + bulkWrite: 1 + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } diff --git a/source/causal-consistency/tests/causal-consistency-write-commands.json b/source/causal-consistency/tests/causal-consistency-write-commands.json new file mode 100644 index 0000000000..a8140c2b3f --- /dev/null +++ b/source/causal-consistency/tests/causal-consistency-write-commands.json @@ -0,0 +1,1396 @@ +{ + "description": "causal consistency write commands include afterClusterTime", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "causal-consistency-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": { + "causalConsistency": true + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "causal-consistency-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "insertOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "insertMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "updateOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 100 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "updateMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gt": 0 + } + }, + "update": { + "$set": { + "updated": true + } + } + }, + "expectResult": { + "matchedCount": 3, + "modifiedCount": 3, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "replaceOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "x": 100 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "deleteOne includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "deleteMany includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gt": 0 + } + } + }, + "expectResult": { + "deletedCount": 3 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 100 + } + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndDelete includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndReplace includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "x": 100 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "findAndModify": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "bulkWrite includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 4 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": 100 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "update": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "delete": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "create includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "causal-consistency-createCollection-test" + } + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "causal-consistency-createCollection-test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "drop", + "command": { + "drop": "causal-consistency-createCollection-test", + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "create", + "command": { + "create": "causal-consistency-createCollection-test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "createIndexes includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "session": "session0", + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes", + "command": { + "createIndexes": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "drop includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "drop", + "command": { + "drop": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "dropDatabase includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "dropDatabase", + "object": "client0", + "arguments": { + "session": "session0", + "database": "causal-consistency-dropDatabase-test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "dropDatabase", + "command": { + "dropDatabase": 1, + "$db": "causal-consistency-dropDatabase-test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "dropIndexes includes afterClusterTime in causally consistent session", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "dropIndexes", + "object": "collection0", + "arguments": { + "session": "session0" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "command": { + "dropIndexes": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + } + } + } + } + ] + } + ] + }, + { + "description": "first write command in a causally consistent session does not include afterClusterTime", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/causal-consistency/tests/causal-consistency-write-commands.yml b/source/causal-consistency/tests/causal-consistency-write-commands.yml new file mode 100644 index 0000000000..a33ca17436 --- /dev/null +++ b/source/causal-consistency/tests/causal-consistency-write-commands.yml @@ -0,0 +1,472 @@ +description: "causal consistency write commands include afterClusterTime" + +schemaVersion: "1.3" + +runOnRequirements: + - topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName causal-consistency-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + sessionOptions: + causalConsistency: true + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +# In a causally consistent session, once an operationTime has been established by a prior +# operation, subsequent write commands MUST include readConcern.afterClusterTime so the +# server can apply the write causally after the previously-observed data. + +tests: + - description: "insertOne includes afterClusterTime in causally consistent session" + operations: + - &find + name: find + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: [{ _id: 1, x: 11 }] + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + expectEvents: + - client: *client0 + events: + - &findEvent + commandStartedEvent: + commandName: find + command: + find: *collectionName + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "insertMany includes afterClusterTime in causally consistent session" + operations: + - *find + - name: insertMany + object: *collection0 + arguments: + session: *session0 + documents: + - { _id: 4 } + - { _id: 5 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "updateOne includes afterClusterTime in causally consistent session" + operations: + - *find + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $set: { x: 100 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "updateMany includes afterClusterTime in causally consistent session" + operations: + - *find + - name: updateMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gt: 0 } } + update: { $set: { updated: true } } + expectResult: + matchedCount: 3 + modifiedCount: 3 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "replaceOne includes afterClusterTime in causally consistent session" + operations: + - *find + - name: replaceOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { x: 100 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "deleteOne includes afterClusterTime in causally consistent session" + operations: + - *find + - name: deleteOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "deleteMany includes afterClusterTime in causally consistent session" + operations: + - *find + - name: deleteMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gt: 0 } } + expectResult: + deletedCount: 3 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "findOneAndUpdate includes afterClusterTime in causally consistent session" + operations: + - *find + - name: findOneAndUpdate + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $set: { x: 100 } } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "findOneAndDelete includes afterClusterTime in causally consistent session" + operations: + - *find + - name: findOneAndDelete + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "findOneAndReplace includes afterClusterTime in causally consistent session" + operations: + - *find + - name: findOneAndReplace + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { x: 100 } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: findAndModify + command: + findAndModify: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "bulkWrite includes afterClusterTime in causally consistent session" + operations: + - *find + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - insertOne: + document: { _id: 4 } + - updateOne: + filter: { _id: 2 } + update: { $set: { x: 100 } } + - deleteOne: + filter: { _id: 3 } + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + - commandStartedEvent: + commandName: update + command: + update: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + - commandStartedEvent: + commandName: delete + command: + delete: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "create includes afterClusterTime in causally consistent session" + operations: + - *find + # Drop the collection first to make sure there's no name conflict during + # createCollection. + - name: dropCollection + object: *database0 + arguments: + session: *session0 + collection: &newCollectionName causal-consistency-createCollection-test + - name: createCollection + object: *database0 + arguments: + session: *session0 + collection: *newCollectionName + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: drop + command: + drop: *newCollectionName + lsid: { $$sessionLsid: *session0 } + - commandStartedEvent: + commandName: create + command: + create: *newCollectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "createIndexes includes afterClusterTime in causally consistent session" + operations: + - *find + - name: createIndex + object: *collection0 + arguments: + session: *session0 + keys: { x: 1 } + name: x_1 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: createIndexes + command: + createIndexes: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "drop includes afterClusterTime in causally consistent session" + operations: + - *find + - name: dropCollection + object: *database0 + arguments: + session: *session0 + collection: *collectionName + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: drop + command: + drop: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "dropDatabase includes afterClusterTime in causally consistent session" + operations: + - *find + - name: dropDatabase + object: *client0 + arguments: + session: *session0 + database: causal-consistency-dropDatabase-test + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: dropDatabase + command: + dropDatabase: 1 + $db: causal-consistency-dropDatabase-test + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + - description: "dropIndexes includes afterClusterTime in causally consistent session" + operations: + - *find + - name: dropIndexes + object: *collection0 + arguments: + session: *session0 + expectEvents: + - client: *client0 + events: + - *findEvent + - commandStartedEvent: + commandName: dropIndexes + command: + dropIndexes: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + + # Covers the same condition as Causal Consistency prose test #2, but for a write operation. + - description: "first write command in a causally consistent session does not include afterClusterTime" + operations: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: insert + command: + insert: *collectionName + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } diff --git a/source/change-streams/change-streams.md b/source/change-streams/change-streams.md new file mode 100644 index 0000000000..39e20dddd0 --- /dev/null +++ b/source/change-streams/change-streams.md @@ -0,0 +1,1141 @@ +# Change Streams + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +As of version 3.6 of the MongoDB server a new `$changeStream` pipeline stage is supported in the aggregation framework. +Specifying this stage first in an aggregation pipeline allows users to request that notifications are sent for all +changes to a particular collection. This specification defines the means for creating change streams in drivers, as well +as behavior during failure scenarios. + +## Specification + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +#### Terms + +##### Resumable Error + +An error is considered resumable if it meets any of the criteria listed below. For any criteria with wire version +constraints, the driver MUST use the wire version of the connection used to do the initial `aggregate` or the `getMore` +that resulted in the error. Drivers MUST NOT check the wire version of the server after the command has been executed +when checking these constraints. + +- Any error encountered which is not a server error (e.g. a timeout error or network error) + +- A server error with code 43 (`CursorNotFound`) + +- For servers with wire version 9 or higher (server version 4.4 or higher), any server error with the + `ResumableChangeStreamError` error label. + +- For servers with wire version less than 9, a server error with one of the following codes: + + | Error Name | Error Code | + | ------------------------------- | ---------- | + | HostUnreachable | 6 | + | HostNotFound | 7 | + | NetworkTimeout | 89 | + | ShutdownInProgress | 91 | + | PrimarySteppedDown | 189 | + | ExceededTimeLimit | 262 | + | SocketException | 9001 | + | NotWritablePrimary | 10107 | + | InterruptedAtShutdown | 11600 | + | InterruptedDueToReplStateChange | 11602 | + | NotPrimaryNoSecondaryOk | 13435 | + | NotPrimaryOrSecondary | 13436 | + | StaleShardVersion | 63 | + | StaleEpoch | 150 | + | StaleConfig | 13388 | + | RetryChangeStream | 234 | + | FailedToSatisfyReadPreference | 133 | + +An error on an aggregate command is not a resumable error. Only errors on a getMore command may be considered resumable +errors. + +### Guidance + +For naming and deviation guidance, see the [CRUD specification](../crud/crud.md#naming). + +### Server Specification + +#### Response Format + +**NOTE:** The examples in this section are provided for illustrative purposes, and are subject to change without +warning. Drivers that provide a static type to represent ChangeStreamDocument MAY include additional fields in their +API. + +If an aggregate command with a `$changeStream` stage completes successfully, the response contains documents with the +following structure: + +```typescript +class ChangeStreamDocument { + /** + * The id functions as an opaque token for use when resuming an interrupted + * change stream. + */ + _id: Document; + + /** + * Describes the type of operation represented in this change notification. + * + * @note: Whether a change is reported as an event of the operation type + * `update` or `replace` is a server implementation detail. + * + * @note: The server will add new `operationType` values in the future and drivers + * MUST NOT err when they encounter a new `operationType`. Unknown `operationType` + * values may be represented by "unknown" or the literal string value. + */ + operationType: "insert" + | "update" + | "replace" + | "delete" + | "invalidate" + | "drop" + | "dropDatabase" + | "rename" + | "createIndexes" + | "dropIndexes" + | "modify" + | "create" + | "shardCollection" + | "refineCollectionShardKey" + | "reshardCollection"; + + /** + * Contains two fields: "db" and "coll" containing the database and + * collection name in which the change happened. + * + * @note: Drivers MUST NOT err when extra fields are encountered in the `ns` document + * as the server may add new fields in the future such as `viewOn`. + */ + ns: Document; + + /** + * Only present for ops of type 'create'. + * Only present when the `showExpandedEvents` change stream option is enabled. + * + * The type of the newly created object. + * + * @since 8.1.0 + */ + nsType: Optional<"collection" | "timeseries" | "view">; + + /** + * Only present for ops of type 'rename'. + * + * The namespace, in the same format as `ns`, that a collection has been renamed to. + */ + to: Optional; + + /** + * * Only present when the `showExpandedEvents` change stream option is enabled and for the following events: + * - 'rename' + * - 'create' + * - 'modify' + * - 'createIndexes' + * - 'dropIndexes' + * - 'shardCollection' + * - 'reshardCollection' + * - 'refineCollectionShardKey' + * + * A description of the operation. + * + * @since 6.0.0 + */ + operationDescription: Optional + + /** + * Only present for ops of type 'insert', 'update', 'replace', and + * 'delete'. + * + * For unsharded collections this contains a single field, _id, with the + * value of the _id of the document updated. For sharded collections, + * this will contain all the components of the shard key in order, + * followed by the _id if the _id isn't part of the shard key. + */ + documentKey: Optional; + + /** + * Only present for ops of type 'update'. + */ + updateDescription: Optional; + + /** + * Always present for operations of type 'insert' and 'replace'. Also + * present for operations of type 'update' if the user has specified + * 'updateLookup' for the 'fullDocument' option when creating the change + * stream. + * + * For operations of type 'insert' and 'replace', this key will contain the + * document being inserted or the new version of the document that is + * replacing the existing document, respectively. + * + * For operations of type 'update', this key will contain a copy of the full + * version of the document from some point after the update occurred. If the + * document was deleted since the updated happened, it will be null. + * + * Contains the point-in-time post-image of the modified document if the + * post-image is available and either 'required' or 'whenAvailable' was + * specified for the 'fullDocument' option when creating the change stream. + * A post-image is always available for 'insert' and 'replace' events. + */ + fullDocument: Document | null; + + /** + * Contains the pre-image of the modified or deleted document if the + * pre-image is available for the change event and either 'required' or + * 'whenAvailable' was specified for the 'fullDocumentBeforeChange' option + * when creating the change stream. If 'whenAvailable' was specified but the + * pre-image is unavailable, this will be explicitly set to null. + */ + fullDocumentBeforeChange: Document | null; + + /** + * The wall time from the mongod that the change event originated from. + * Populated for server versions 6.0 and above. + */ + wallTime: Optional; + + /** + * The `ui` field from the oplog entry corresponding to the change event. + * + * Only present when the `showExpandedEvents` change stream option is enabled and for the following events: + * - 'insert' + * - 'update' + * - 'delete' + * - 'createIndexes' + * - 'dropIndexes' + * - 'modify' + * - 'drop' + * - 'create' + * - 'shardCollection' + * - 'reshardCollection' + * - 'refineCollectionShardKey' + * + * This field is a value of binary subtype 4 (UUID). + * + * @since 6.0.0 + */ + collectionUUID: Optional; + + /** + * The cluster time at which the change occurred. + */ + clusterTime: Timestamp; + +} + +class UpdateDescription { + /** + * A document containing key:value pairs of names of the fields that were + * changed (excluding the fields reported via `truncatedArrays`), and the new value for those fields. + * + * Despite array fields reported via `truncatedArrays` being excluded from this field, + * changes to fields of the elements of the array values may be reported via this field. + * Example: + * original field: + * "arrayField": ["foo", {"a": "bar"}, 1, 2, 3] + * updated field: + * "arrayField": ["foo", {"a": "bar", "b": 3}] + * a potential corresponding UpdateDescription: + * { + * "updatedFields": { + * "arrayField.1.b": 3 + * }, + * "removedFields": [], + * "truncatedArrays": [ + * { + * "field": "arrayField", + * "newSize": 2 + * } + * ] + * } + * + * Modifications to array elements are expressed via the dot notation (https://www.mongodb.com/docs/manual/core/document/#document-dot-notation). + * Example: an `update` which sets the element with index 0 in the array field named arrayField to 7 is reported as + * "updatedFields": {"arrayField.0": 7} + */ + updatedFields: Document; + + /** + * An array of field names that were removed from the document. + */ + removedFields: Array; + + /** + * Truncations of arrays may be reported using one of the following methods: + * either via this field or via the 'updatedFields' field. In the latter case the entire array is considered to be replaced. + * + * The structure of documents in this field is + * { + * "field": , + * "newSize": + * } + * Example: an `update` which shrinks the array arrayField.0.nestedArrayField from size 8 to 5 may be reported as + * "truncatedArrays": [{"field": "arrayField.0.nestedArrayField", "newSize": 5}] + * + * @note The method used to report a truncation is a server implementation detail. + * @since 4.7.0 + */ + truncatedArrays: Array; + + /** + * A document containing a map that associates an update path to an array containing the path components used in the update document. This data + * can be used in combination with the other fields in an `UpdateDescription` to determine the actual path in the document that was updated. This is + * necessary in cases where a key contains dot-separated strings (i.e., `{ "a.b": "c" }`) or a document contains a numeric literal string key + * (i.e., `{ "a": { "0": "a" } }`. Note that in this scenario, the numeric key can't be the top level key, because `{ "0": "a" }` is not ambiguous - + * update paths would simply be `'0'` which is unambiguous because BSON documents cannot have arrays at the top level.). + * + * Each entry in the document maps an update path to an array which contains the actual path used when the document was updated. + * For example, given a document with the following shape `{ "a": { "0": 0 } }` and an update of `{ $inc: { "a.0": 1 } }`, `disambiguatedPaths` would + * look like the following: + * { + * "a.0": ["a", "0"] + * } + * + * In each array, all elements will be returned as strings with the exception of array indices, which will be returned as 32 bit integers. + * + * Only present when the `showExpandedEvents` change stream option is enabled. + * + * @since 6.1.0 + */ + disambiguatedPaths: Optional +} +``` + +The responses to a change stream aggregate or getMore have the following structures: + +```typescript +/** + * Response to a successful aggregate. + */ +{ + ok: 1, + cursor: { + ns: String, + id: Int64, + firstBatch: Array, + /** + * postBatchResumeToken is returned in MongoDB 4.0.7 and later. + */ + postBatchResumeToken: Document + }, + operationTime: Timestamp, + $clusterTime: Document, +} + +/** + * Response to a successful getMore. + */ +{ + ok: 1, + cursor: { + ns: String, + id: Int64, + nextBatch: Array + /** + * postBatchResumeToken is returned in MongoDB 4.0.7 and later. + */ + postBatchResumeToken: Document + }, + operationTime: Timestamp, + $clusterTime: Document, +} +``` + +### Driver API + +```typescript +interface ChangeStream extends Iterable { + /** + * The cached resume token + */ + private resumeToken: Document; + + /** + * The pipeline of stages to append to an initial ``$changeStream`` stage + */ + private pipeline: Array; + + /** + * The options provided to the initial ``$changeStream`` stage + */ + private options: ChangeStreamOptions; + + /** + * The read preference for the initial change stream aggregation, used + * for server selection during an automatic resume. + */ + private readPreference: ReadPreference; +} + +interface Collection { + /** + * @returns a change stream on a specific collection. + */ + watch(pipeline: Document[], options: Optional): ChangeStream; +} + +interface Database { + /** + * Allows a client to observe all changes in a database. + * Excludes system collections. + * @returns a change stream on all collections in a database + * @since 4.0 + * @see https://www.mongodb.com/docs/manual/reference/system-collections/ + */ + watch(pipeline: Document[], options: Optional): ChangeStream; +} + +interface MongoClient { + /** + * Allows a client to observe all changes in a cluster. + * Excludes system collections. + * Excludes the "config", "local", and "admin" databases. + * @since 4.0 + * @returns a change stream on all collections in all databases in a cluster + * @see https://www.mongodb.com/docs/manual/reference/system-collections/ + */ + watch(pipeline: Document[], options: Optional): ChangeStream; +} + +class ChangeStreamOptions { + /** + * Allowed values: 'default', 'updateLookup', 'whenAvailable', 'required'. + * + * The default is to not send a value, which is equivalent to 'default'. By + * default, the change notification for partial updates will include a delta + * describing the changes to the document. + * + * 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. + * + * When set to 'whenAvailable', configures the change stream to return the + * post-image of the modified document for replace and update change events + * if the post-image for this event is available. + * + * When set to 'required', the same behavior as 'whenAvailable' except that + * an error is raised if the post-image is not available. + * + * For forward compatibility, a driver MUST NOT raise an error when a user + * provides an unknown value. The driver relies on the server to validate + * this option. + * + * @note this is an option of the `$changeStream` pipeline stage. + */ + fullDocument: Optional; + + /** + * Allowed values: 'whenAvailable', 'required', 'off'. + * + * The default is to not send a value, which is equivalent to 'off'. + * + * When set to 'whenAvailable', configures the change stream to return the + * pre-image of the modified document for replace, update, and delete change + * events if it is available. + * + * When set to 'required', the same behavior as 'whenAvailable' except that + * an error is raised if the pre-image is not available. + * + * For forward compatibility, a driver MUST NOT raise an error when a user + * provides an unknown value. The driver relies on the server to validate + * this option. + * + * @note this is an option of the `$changeStream` pipeline stage. + */ + fullDocumentBeforeChange: Optional; + + /** + * Specifies the logical starting point for the new change stream. + * + * @note this is an option of the `$changeStream` pipeline stage. + */ + resumeAfter: Optional; + + /** + * The maximum amount of time for the server to wait on new documents to satisfy + * a change stream query. + * + * This is the same field described in FindOptions in the CRUD spec. + * + * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#read + * @note this option is an alias for `maxTimeMS`, used on `getMore` commands + * @note this option is not set on the `aggregate` command nor `$changeStream` pipeline stage + */ + maxAwaitTimeMS: Optional; + + /** + * The number of documents to return per batch. + * + * This option is sent only if the caller explicitly provides a value. The + * default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate + * @note this is an aggregation command option + */ + batchSize: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The + * default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate + * @note this is an aggregation command option + */ + collation: Optional; + + /** + * The change stream will only provide changes that occurred at or after the + * specified timestamp. Any command run against the server will return + * an operation time that can be used here. + * + * @since 4.0 + * @see https://www.mongodb.com/docs/manual/reference/method/db.runCommand/ + * @note this is an option of the `$changeStream` pipeline stage. + */ + startAtOperationTime: Optional; + + /** + * Similar to `resumeAfter`, this option takes a resume token and starts a + * new change stream returning the first notification after the token. + * This will allow users to watch collections that have been dropped and recreated + * or newly renamed collections without missing any notifications. + * + * The server will report an error if `startAfter` and `resumeAfter` are both specified. + * + * @since 4.1.1 + * @see https://www.mongodb.com/docs/manual/changeStreams/#change-stream-start-after + * @note this is an option of the `$changeStream` pipeline stage. + */ + startAfter: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 only support string as comment, + * and providing a non-string type will result in a server-side error. + * + * If a comment is provided, drivers MUST attach this comment to all + * subsequent getMore commands run on the same cursor for server + * versions 4.4 and above. For server versions below 4.4 drivers MUST NOT + * attach a comment to getMore commands. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate + * @note this is an aggregation command option + */ + comment: Optional + + /** + * Enables the server to send the 'expanded' list of change stream events. + * The list of additional events included with this flag set are + * - createIndexes + * - dropIndexes + * - modify + * - create + * - shardCollection + * - reshardCollection + * - refineCollectionShardKey + * + * This flag is available in server versions greater than 6.0.0. `reshardCollection` and + * `refineCollectionShardKey` events are not available until server version 6.1.0. + * + * @note this is an option of the change stream pipeline stage + */ + showExpandedEvents: Optional +} +``` + +**NOTE:** The set of `ChangeStreamOptions` may grow over time. + +#### Helper Method + +The driver API consists of a `ChangeStream` type, as well as three helper methods. All helpers MUST return a +`ChangeStream` instance. Implementers MUST document that helper methods are preferred to running a raw aggregation with +a `$changeStream` stage, for the purpose of supporting resumability. + +The helper methods must construct an aggregation command with a REQUIRED initial `$changeStream` stage. A driver MUST +NOT throw a custom exception if multiple `$changeStream` stages are present (e.g. if a user also passed `$changeStream` +in the pipeline supplied to the helper), as the server will return an error. + +The helper methods MUST determine a read concern for the operation in accordance with the +[Read and Write Concern specification](../read-write-concern/read-write-concern.md#via-code). The initial implementation +of change streams on the server requires a 'majority' read concern or no read concern. Drivers MUST document this +requirement. Drivers SHALL NOT throw an exception if any other read concern is specified, but instead should depend on +the server to return an error. + +The stage has the following shape: + +```typescript +{ $changeStream: ChangeStreamOptions } +``` + +The first parameter of the helpers specifies an array of aggregation pipeline stages which MUST be appended to the +initial stage. Drivers MUST support an empty pipeline. Languages which support default parameters MAY specify an empty +array as the default value for this parameter. Drivers SHOULD otherwise make specification of a pipeline as similar as +possible to the [aggregate](../crud/crud.md#read) CRUD method. + +Additionally, implementers MAY provide a form of these methods which require no parameters, assuming no options and no +additional stages beyond the initial `$changeStream` stage: + +```python +for change in db.collection.watch(): + print(change) +``` + +Presently change streams support only a subset of available aggregation stages: + +- `$match` +- `$project` +- `$addFields` +- `$replaceRoot` +- `$redact` + +A driver MUST NOT throw an exception if any unsupported stage is provided, but instead depend on the server to return an +error. + +A driver MUST NOT throw an exception if a user adds, removes, or modifies fields using `$project`. The server will +produce an error if `_id` is projected out, but a user should otherwise be able to modify the shape of the change stream +event as desired. This may require the result to be deserialized to a `BsonDocument` or custom-defined type rather than +a `ChangeStreamDocument`. It is the responsibility of the user to ensure that the deserialized type is compatible with +the specified `$project` stage. + +The aggregate helper methods MUST have no new logic related to the `$changeStream` stage. Drivers MUST be capable of +handling [TAILABLE_AWAIT](../crud/crud.md#read) cursors from the aggregate command in the same way they handle such +cursors from find. + +##### `Collection.watch` helper + +Returns a `ChangeStream` on a specific collection + +Command syntax: + +```typescript +{ + aggregate: 'collectionName' + pipeline: [{$changeStream: {...}}, ...], + ... +} +``` + +##### `Database.watch` helper + +- Since: 4.0 + +Returns a `ChangeStream` on all collections in a database. + +Command syntax: + +```typescript +{ + aggregate: 1 + pipeline: [{$changeStream: {...}}, ...], + ... +} +``` + +Drivers MUST use the `ns` returned in the `aggregate` command to set the `collection` option in subsequent `getMore` +commands. + +##### `MongoClient.watch` helper + +- Since: 4.0 + +Returns a `ChangeStream` on all collections in all databases in a cluster + +Command syntax: + +```typescript +{ + aggregate: 1 + pipeline: [{$changeStream: {allChangesForCluster: true, ...}}, ...], + ... +} +``` + +The helper MUST run the command against the `admin` database + +Drivers MUST use the `ns` returned in the `aggregate` command to set the `collection` option in subsequent `getMore` +commands. + +#### ChangeStream + +A `ChangeStream` is an abstraction of a [TAILABLE_AWAIT](../crud/crud.md#read) cursor, with support for resumability. +Implementers MAY choose to implement a `ChangeStream` as an extension of an existing tailable cursor implementation. If +the `ChangeStream` is implemented as a type which owns a tailable cursor, then the implementer MUST provide a manner of +closing the change stream, as well as satisfy the requirements of extending `Iterable`. If your language has +an idiomatic way of disposing of resources you MAY choose to implement that in addition to, or instead of, an explicit +close method. + +A change stream MUST track the last resume token, per +[Updating the Cached Resume Token](#updating-the-cached-resume-token). + +Drivers MUST raise an error on the first document received without a resume token (e.g. the user has removed `_id` with +a pipeline stage), and close the change stream. The error message SHOULD resemble "Cannot provide resume functionality +when the resume token is missing". + +A change stream MUST attempt to resume a single time if it encounters any resumable error per +[Resumable Error](#resumable-error). A change stream MUST NOT attempt to resume on any other type of error. + +In addition to tracking a resume token, change streams MUST also track the read preference specified when the change +stream was created. In the event of a resumable error, a change stream MUST perform server selection with the original +read preference using the +[rules for server selection](../server-selection/server-selection.md#rules-for-server-selection) before attempting to +resume. + +##### Single Server Topologies + +Presently, change streams cannot be initiated on single server topologies as they do not have an oplog. Drivers MUST NOT +throw an exception in this scenario, but instead rely on an error returned from the server. This allows for the server +to seamlessly introduce support for this in the future, without need to make changes in driver code. + +##### startAtOperationTime + +- Since: 4.0 + +`startAtOperationTime` specifies that a change stream will only return changes that occurred at or after the specified +`Timestamp`. + +The server expects `startAtOperationTime` as a BSON Timestamp. Drivers MUST allow users to specify a +`startAtOperationTime` option in the `watch` helpers. They MUST allow users to specify this value as a raw `Timestamp`. + +`startAtOperationTime`, `resumeAfter`, and `startAfter` are all mutually exclusive; if any two are set, the server will +return an error. Drivers MUST NOT throw a custom error, and MUST defer to the server error. + +The `ChangeStream` MUST save the `operationTime` from the initial `aggregate` response when the following criteria are +met: + +- None of `startAtOperationTime`, `resumeAfter`, `startAfter` were specified in the `ChangeStreamOptions`. +- The max wire version is >= `7`. +- The initial `aggregate` response had no results. +- The initial `aggregate` response did not include a `postBatchResumeToken`. + +##### resumeAfter + +`resumeAfter` is used to resume a `ChangeStream` that has been stopped to ensure that only changes starting with the log +entry immediately *after* the provided token will be returned. If the resume token specified does not exist, the server +will return an error. + +##### Resume Process + +Once a `ChangeStream` has encountered a resumable error, it MUST attempt to resume one time. The process for resuming +MUST follow these steps: + +- Perform server selection. +- Connect to selected server. +- If there is a cached `resumeToken`: + - If the `ChangeStream` was started with `startAfter` and has yet to return a result document: + - The driver MUST set `startAfter` to the cached `resumeToken`. + - The driver MUST NOT set `resumeAfter`. + - The driver MUST NOT set `startAtOperationTime`. If `startAtOperationTime` was in the original aggregation command, + the driver MUST remove it. + - Else: + - The driver MUST set `resumeAfter` to the cached `resumeToken`. + - The driver MUST NOT set `startAfter`. If `startAfter` was in the original aggregation command, the driver MUST + remove it. + - The driver MUST NOT set `startAtOperationTime`. If `startAtOperationTime` was in the original aggregation command, + the driver MUST remove it. +- Else if there is no cached `resumeToken` and the `ChangeStream` has a saved operation time (either from an originally + specified `startAtOperationTime` or saved from the original aggregation) and the max wire version is >= `7`: + - The driver MUST NOT set `resumeAfter`. + - The driver MUST NOT set `startAfter`. + - The driver MUST set `startAtOperationTime` to the value of the originally used `startAtOperationTime` or the one + saved from the original aggregation. +- Else: + - The driver MUST NOT set `resumeAfter`, `startAfter`, or `startAtOperationTime`. + - The driver MUST use the original aggregation command to resume. + +When `resumeAfter` is specified the `ChangeStream` will return notifications starting with the oplog entry immediately +*after* the provided token. + +If the server supports sessions, the resume attempt MUST use the same session as the previous attempt's command. + +A driver MUST only attempt to resume once from a resumable error. However, if the `aggregate` for that resume succeeds, +a driver MUST ensure that following resume attempts can succeed, even in the absence of any changes received by the +cursor between resume attempts. For example: + +1. `aggregate` (succeeds) +2. `getMore` (fails with resumable error) +3. `aggregate` (succeeds) +4. `getMore` (fails with resumable error) +5. `aggregate` (succeeds) +6. `getMore` (succeeds) +7. change stream document received + +A driver SHOULD attempt to kill the cursor on the server on which the cursor is opened during the resume process, and +MUST NOT attempt to kill the cursor on any other server. Any exceptions or errors that occur during the process of +killing the cursor should be suppressed, including both errors returned by the `killCursor` command and exceptions +thrown by opening, writing to, or reading from the socket. + +##### Exposing All Resume Tokens + +- Since: 4.0.7 + +Users can inspect the \_id on each `ChangeDocument` to use as a resume token. But since MongoDB 4.0.7, aggregate and +getMore responses also include a `postBatchResumeToken`. Drivers use one or the other when automatically resuming, as +described in [Resume Process](#resume-process). + +Drivers MUST expose a mechanism to retrieve the same resume token that would be used to automatically resume. It MUST be +possible to use this mechanism after iterating every document. It MUST be possible for users to use this mechanism +periodically even when no documents are getting returned (i.e. `getMore` has returned empty batches). Drivers have two +options to implement this. + +###### Option 1: ChangeStream::getResumeToken() + +```typescript +interface ChangeStream extends Iterable { + /** + * Returns the cached resume token that will be used to resume + * after the most recently returned change. + */ + public getResumeToken() Optional; +} +``` + +This MUST be implemented in synchronous drivers. This MAY be implemented in asynchronous drivers. + +###### Option 2: Event Emitted for Resume Token + +Allow users to set a callback to listen for new resume tokens. The exact interface is up to the driver, but it MUST meet +the following criteria: + +- The callback is set in the same manner as a callback used for receiving change documents. +- The callback accepts a resume token as an argument. +- The callback (or event) MAY include an optional ChangeDocument, which is unset when called with resume tokens sourced + from `postBatchResumeToken`. + +A possible interface for this callback MAY look like: + +```typescript +interface ChangeStream extends Iterable { + /** + * Returns a resume token that should be used to resume after the most + * recently returned change. + */ + public onResumeTokenChanged(ResumeTokenCallback:(Document resumeToken) => void); +} +``` + +This MUST NOT be implemented in synchronous drivers. This MAY be implemented in asynchronous drivers. + +###### Updating the Cached Resume Token + +The following rules describe how to update the cached `resumeToken`: + +- When the `ChangeStream` is started: + - If `startAfter` is set, cache it. + - Else if `resumeAfter` is set, cache it. + - Else, `resumeToken` remains unset. +- When `aggregate` or `getMore` returns: + - If an empty batch was returned and a `postBatchResumeToken` was included, cache it. +- When returning a document to the user: + - If it's the last document in the batch and a `postBatchResumeToken` is included, cache it. + - Else, cache the `_id` of the document. + +###### Not Blocking on Iteration + +Synchronous drivers MUST provide a way to iterate a change stream without blocking until a change document is returned. +This MUST give the user an opportunity to get the most up-to-date resume token, even when the change stream continues to +receive empty batches in getMore responses. This allows users to call `ChangeStream::getResumeToken()` after iterating +every document and periodically when no documents are getting returned. + +Although the implementation of tailable awaitData cursors is not specified, this MAY be implemented with a `tryNext` +method on the change stream cursor. + +All drivers MUST document how users can iterate a change stream and receive *all* resume token updates. +[Why do we allow access to the resume token to users](#why-do-we-allow-access-to-the-resume-token-to-users) shows an +example. The documentation MUST state that users intending to store the resume token should use this method to get the +most up to date resume token. + +##### Timeouts + +Drivers MUST apply timeouts to change stream establishment, iteration, and resume attempts per +[Client Side Operations Timeout: Change Streams](../client-side-operations-timeout/client-side-operations-timeout.md#change-streams). + +##### Notes and Restrictions + +**1. `fullDocument: updateLookup` can result in change documents larger than 16 MiB** + +There is a risk that if there is a large change to a large document, the full document and delta might result in a +document larger than the 16 MiB limitation on BSON documents. If that happens the cursor will be closed, and a server +error will be returned. + +**2. Users can remove the resume token with aggregation stages** + +It is possible for a user to specify the following stage: + +```javascript +{ $project: { _id: 0 } } +``` + +Similar removal of the resume token is possible with the `$redact` and `$replaceRoot` stages. While this is not +technically illegal, it makes it impossible for drivers to support resumability. Users may explicitly opt out of +resumability by issuing a raw aggregation with a `$changeStream` stage. + +## Rationale + +### Why Helper Methods? + +Change streams are a first class concept similar to CRUD or aggregation; the fact that they are initiated via an +aggregation pipeline stage is merely an implementation detail. By requiring drivers to support top-level helper methods +for this feature we not only signal this intent, but also solve a number of other potential problems: + +Disambiguation of the result type of this special-case aggregation pipeline (`ChangeStream`), and an ability to control +the behaviors of the resultant cursor + +More accurate support for the concept of a maximum time the user is willing to wait for subsequent queries to complete +on the resultant cursor (`maxAwaitTimeMs`) + +Finer control over the options pertaining specifically to this type of operation, without polluting the already +well-defined `AggregateOptions` + +Flexibility for future potentially breaking changes for this feature on the server + +### Why are ChangeStreams required to retry once on a resumable error? + +User experience is of the utmost importance. Errors not originating from the server are generally network errors, and +network errors can be transient. Attempting to resume an interrupted change stream after the initial error allows for a +seamless experience for the user, while subsequent network errors are likely to be an outage which can then be exposed +to the user with greater confidence. + +### Why do we allow access to the resume token to users + +Imagine a scenario in which a user wants to process each change to a collection **at least once**, but the application +crashes during processing. In order to overcome this failure, a user might use the following approach: + +```python +localChange = getChangeFromLocalStorage() +resumeToken = getResumeTokenFromLocalStorage() + +if localChange: + processChange(localChange) + +try: + change_stream = db.collection.watch([...], resumeAfter=resumeToken) + while True: + change = change_stream.try_next() + persistResumeTokenToLocalStorage(change_stream.get_resume_token()) + if change: + persistChangeToLocalStorage(change) + processChange(change) +except Exception: + log.error("...") +``` + +In this case the current change is always persisted locally, including the resume token, such that on restart the +application can still process the change while ensuring that the change stream continues from the right logical time in +the oplog. It is the application's responsibility to ensure that `processChange` is idempotent, this design merely makes +a reasonable effort to process each change **at least** once. + +### Why is there no example of the desired user experience? + +The specification used to include this overspecified example of the "desired user experience": + +```python +try: + for change in db.collection.watch(...): + print(change) +except Exception: + # We know for sure it's unrecoverable: + log.error("...") +``` + +It was decided to remove this example from the specification for the following reasons: + +- Tailable + awaitData cursors behave differently in existing supported drivers. +- There are considerations to be made for languages that do not permit interruptible I/O (such as Java), where a change + stream which blocks forever in a separate thread would necessitate killing the thread. +- There is something to be said for an API that allows cooperation by default. The model in which a call to next only + blocks until any response is returned (even an empty batch), allows for interruption and cooperation (e.g. + interaction with other event loops). + +### Why is an allow list of error codes preferable to a deny list? + +Change streams originally used a deny list of error codes to determine which errors were not resumable. However, this +allowed for the possibility of infinite resume loops if an error was not correctly deny listed. Due to the fact that all +errors aside from transient issues such as failovers are not resumable, the resume behavior was changed to use an allow +list. Part of this change was to introduce the `ResumableChangeStreamError` label so the server can add new error codes +to the allow list without requiring changes to drivers. + +### Why is `CursorNotFound` special-cased when determining resumability? + +With the exception of `CursorNotFound`, a server error on version 4.4 or higher is considered resumable if and only if +it contains the `ResumableChangeStreamError` label. However, this label is only added by the server if the cursor being +created or iterated is a change stream. `CursorNotFound` is returned when a `getMore` is done with a cursor ID that the +server isn't aware of and therefore can't determine if the cursor is a change stream. Marking all `CursorNotFound` +errors resumable in the server regardless of cursor type could be confusing as a user could see the +`ResumableChangeStreamError` label when iterating a non-change stream cursor. To workaround this, drivers always treat +this error as resumable despite it not having the proper error label. + +### Why do we need to send a default `startAtOperationTime` when resuming a `ChangeStream`? + +`startAtOperationTime` allows a user to create a resumable change stream even when a result (and corresponding +resumeToken) is not available until a later point in time. + +For example: + +- A client creates a `ChangeStream`, and calls `watch` +- The `ChangeStream` sends out the initial `aggregate` call, and receives a response with no initial values. Because + there are no initial values, there is no latest resumeToken. +- The client's network is partitioned from the server, causing the client's `getMore` to time out +- Changes occur on the server. +- The network is unpartitioned +- The client attempts to resume the `ChangeStream` + +In the above example, not sending `startAtOperationTime` will result in the change stream missing the changes that +occurred while the server and client are partitioned. By sending `startAtOperationTime`, the server will know to include +changes from that previous point in time. + +### Why do we need to expose the postBatchResumeToken? + +Resume tokens refer to an oplog entry. The resume token from the `_id` of a document corresponds the oplog entry of the +change. The `postBatchResumeToken` represents the oplog entry the change stream has scanned up to on the server (not +necessarily a matching change). This can be a much more recent oplog entry, and should be used to resume when possible. + +Attempting to resume with an old resume token may degrade server performance since the server needs to scan through more +oplog entries. Worse, if the resume token is older than the last oplog entry stored on the server, then resuming is +impossible. + +Imagine the change stream matches a very small percentage of events. On a `getMore` the server scans the oplog for the +duration of `maxAwaitTimeMS` but finds no matching entries and returns an empty response (still containing a +`postBatchResumeToken`). There may be a long sequence of empty responses. Then due to a network error, the change stream +tries resuming. If we tried resuming with the most recent `_id`, this throws out the oplog scanning the server had done +for the long sequence of getMores with empty responses. But resuming with the last `postBatchResumeToken` skips the +unnecessary scanning of unmatched oplog entries. + +## Test Plan + +See [tests/README.md](tests/README.md) + +## Backwards Compatibility + +There should be no backwards compatibility concerns. + +## Reference Implementations + +- NODE (NODE-1055) +- PYTHON (PYTHON-1338) +- RUBY (RUBY-1228) + +## Changelog + +- 2026-04-28: Remove test for nsType when creating timeseries + +- 2026-03-18: Revert expanded field visibility change. + +- 2025-09-08: Clarify resume behavior. + +- 2025-03-31: Update for expanded field visibility in server 8.2+ + +- 2025-02-24: Make `nsType` `Optional` to match other optional fields in the change stream spec. + +- 2025-01-29: Add `nsType` to `ChangeStreamDocument`. + +- 2024-02-09: Migrated from reStructuredText to Markdown. + +- 2023-08-11: Update server versions for `$changeStreamSplitLargeEvent` test. + +- 2023-05-22: Add spec test for `$changeStreamSplitLargeEvent`. + +- 2022-10-20: Reformat changelog. + +- 2022-10-05: Remove spec front matter. + +- 2022-08-22: Add `clusterTime` to `ChangeStreamDocument`. + +- 2022-08-17: Support `disambiguatedPaths` in `UpdateDescription`. + +- 2022-05-19: Support new change stream events with `showExpandedEvents`. + +- 2022-05-17: Add `wallTime` to `ChangeStreamDocument`. + +- 2022-04-13: Support returning point-in-time pre and post-images with `fullDocumentBeforeChange` and `fullDocument`. + +- 2022-03-25: Do not error when parsing change stream event documents. + +- 2022-02-28: Add `to` to `ChangeStreamDocument`. + +- 2022-02-10: Specify that `getMore` command must explicitly send inherited `comment`. + +- 2022-02-01: Add `comment` to `ChangeStreamOptions`. + +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout specification. + +- 2021-09-01: Clarify that server selection during resumption should respect normal server selection rules. + +- 2021-04-29: Add `load-balanced` to test topology requirements. + +- 2021-04-23: Update to use modern terminology. + +- 2021-02-08: Add the `UpdateDescription.truncatedArrays` field. + +- 2020-02-10: Change error handling approach to use an allow list. + +- 2019-07-15: Clarify resume process for change streams started with the `startAfter` option. + +- 2019-07-09: Change `fullDocument` to be an optional string. + +- 2019-07-02: Fix server version for `startAfter`. + +- 2019-07-01: Clarify that close may be implemented with more idiomatic patterns instead of a method. + +- 2019-06-20: Fix server version for addition of `postBatchResumeToken`. + +- 2019-04-12: Clarify caching process for resume token. + +- 2019-04-03: Update the lowest server version that supports `postBatchResumeToken`. + +- 2019-01-10: Clarify error handling for killing the cursor. + +- 2018-11-06: Add handling of `postBatchResumeToken`. + +- 2018-12-14: Add `startAfter` to change stream options. + +- 2018-09-09: Add `dropDatabase` to change stream `operationType`. + +- 2018-07-30: Remove redundant error message checks for resumable errors. + +- 2018-07-27: Add drop to change stream `operationType`. + +- 2018-06-14: Clarify how to calculate `startAtOperationTime`. + +- 2018-05-24: Change `startAtClusterTime` to `startAtOperationTime`. + +- 2018-04-18: Add helpers for Database and MongoClient, and add `startAtClusterTime` option. + +- 2018-04-17: Clarify that the initial aggregate should not be retried. + +- 2017-12-13: Default read concern is also accepted, not just "majority". + +- 2017-11-06: Defer to Read and Write concern spec for determining a read concern for the helper method. + +- 2017-09-26: Clarify that change stream options may be added later. + +- 2017-09-21: Clarify that we need to close the cursor on missing token. + +- 2017-09-06: Remove `desired user experience` example. + +- 2017-08-22: Clarify killing cursors during resume process. + +- 2017-08-16: Fix formatting of resume process. + +- 2017-08-16: Add clarification regarding Resumable errors. + +- 2017-08-07: Fix typo in command format. + +- 2017-08-03: Initial commit. diff --git a/source/change-streams/change-streams.rst b/source/change-streams/change-streams.rst index 259ae13164..92a1bd4163 100644 --- a/source/change-streams/change-streams.rst +++ b/source/change-streams/change-streams.rst @@ -1,606 +1,3 @@ -============== -Change Streams -============== - -:Title: Change Streams -:Author: Matt Broadstone -:Advisory Group: David Golden, A. Jesse Jiryu Davis -:Approvers: Jeff Yemin, A. Jesse Jiryu Davis, Bernie Hackett, David Golden, Eliot -:Status: Accepted -:Type: Standards -:Minimum Server Version: 3.6 -:Last Modified: July 30, 2018 -:Version: 1.5.1 - -.. contents:: - --------- - -Abstract -======== - -As of version 3.6 of the MongoDB server a new ``$changeStream`` pipeline stage is supported in the aggregation framework. Specifying this stage first in an aggregation pipeline allows users to request that notifications are sent for all changes to a particular collection. This specification defines the means for creating change streams in drivers, as well as behavior during failure scenarios. - -Specification -============= - ------------ -Definitions ------------ - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Terms ------ - -Resumable Error -^^^^^^^^^^^^^^^ - -An error is considered resumable if it meets any of the following criteria: - -- any error encountered which is not a server error (e.g. a timeout error or - network error) - -- *any* server error response from a getMore command excluding those - containing the following error codes - - .. list-table:: - :header-rows: 1 - - * - Error Name - - Error Code - * - Interrupted - - 11601 - * - CappedPositionLost - - 136 - * - CursorKilled - - 237 - -An error on an aggregate command is not a resumable error. Only errors on a -getMore command may be considered resumable errors. - -The criteria for resumable errors is similar to the discussion in the SDAM -spec's section on `Error Handling`_, but includes additional error codes. See -`What do the additional error codes mean?`_ for the reasoning behind these -additional errors. - -.. _Error Handling: ../server-discovery-and-monitoring/server-discovery-and-monitoring.rst#error-handling - --------- -Guidance --------- - -For naming and deviation guidance, see the `CRUD specification `_. - --------------------- -Server Specification --------------------- - -Response Format ---------------- - -If the aggregation with ``$changeStream`` specified completes successfully, the resultant stream will deliver documents in the following format: - -.. code:: typescript - - { - /** - * The id functions as an opaque token for use when resuming an interrupted - * change stream. - */ - _id: Document; - - /** - * Describes the type of operation represented in this change notification. - */ - operationType: "insert" | "update" | "replace" | "delete" | "invalidate" | "drop" | "dropDatabase"; - - /** - * Contains two fields: “db” and “coll” containing the database and - * collection name in which the change happened. - */ - ns: Document; - - /** - * Only present for ops of type ‘insert’, ‘update’, ‘replace’, and - * ‘delete’. - * - * For unsharded collections this contains a single field, _id, with the - * value of the _id of the document updated. For sharded collections, - * this will contain all the components of the shard key in order, - * followed by the _id if the _id isn’t part of the shard key. - */ - documentKey: Optional; - - /** - * Only present for ops of type ‘update’. - * - * Contains a description of updated and removed fields in this - * operation. - */ - updateDescription: Optional; - - /** - * Always present for operations of type ‘insert’ and ‘replace’. Also - * present for operations of type ‘update’ if the user has specified ‘updateLookup’ - * in the ‘fullDocument’ arguments to the ‘$changeStream’ stage. - * - * For operations of type ‘insert’ and ‘replace’, this key will contain the - * document being inserted, or the new version of the document that is replacing - * the existing document, respectively. - * - * For operations of type ‘update’, this key will contain a copy of the full - * version of the document from some point after the update occurred. If the - * document was deleted since the updated happened, it will be null. - */ - fullDocument: Document | null; - - } - - class UpdateDescription { - /** - * A document containing key:value pairs of names of the fields that were - * changed, and the new value for those fields. - */ - updatedFields: Document; - - /** - * An array of field names that were removed from the document. - */ - removedFields: Array; - } - -**NOTE:** The above format is provided for illustrative purposes, and is subject to change without warning. - ----------- -Driver API ----------- - -.. code:: typescript - - interface ChangeStream extends Iterable { - /** - * The resume token (_id) of the document the iterator last returned - */ - private resumeToken: Document; - - /** - * The pipeline of stages to append to an initial ``$changeStream`` stage - */ - private pipeline: Array; - - /** - * The options provided to the initial ``$changeStream`` stage - */ - private options: ChangeStreamOptions; - - /** - * The read preference for the initial change stream aggregation, used - * for server selection during an automatic resume. - */ - private readPreference: ReadPreference; - } - - interface Collection { - /** - * @returns a change stream on a specific collection. - */ - watch(pipeline: Document[], options: Optional): ChangeStream; - } - - interface Database { - /** - * Allows a client to observe all changes in a database. - * Excludes system collections. - * @returns a change stream on all collections in a database - * @since 4.0 - * @see https://docs.mongodb.com/manual/reference/system-collections/ - */ - watch(pipeline: Document[], options: Optional): ChangeStream; - } - - interface MongoClient { - /** - * Allows a client to observe all changes in a cluster. - * Excludes system collections. - * Excludes the "config", "local", and "admin" databases. - * @since 4.0 - * @returns a change stream on all collections in all databases in a cluster - * @see https://docs.mongodb.com/manual/reference/system-collections/ - */ - watch(pipeline: Document[], options: Optional): ChangeStream; - } - - class ChangeStreamOptions { - /** - * Allowed values: ‘default’, ‘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. For forward - * compatibility, a driver MUST NOT raise an error when a user provides an unknown - * value. The driver relies on the server to validate this option. - * - * @note this is an option of the `$changeStream` pipeline stage. - */ - fullDocument: string = ‘default’; - - /** - * Specifies the logical starting point for the new change stream. - * - * @note this is an option of the `$changeStream` pipeline stage. - */ - resumeAfter: Optional; - - /** - * The maximum amount of time for the server to wait on new documents to satisfy - * a change stream query. - * - * This is the same field described in FindOptions in the CRUD spec. - * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#read - * @note this option is an alias for `maxTimeMS`, used on `getMore` commands - * @note this is an aggregation command option - */ - maxAwaitTimeMS: Optional; - - /** - * The number of documents to return per batch. - * - * This option is sent only if the caller explicitly provides a value. The - * default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate - * @note this is an aggregation command option - */ - batchSize: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The - * default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate - * @note this is an aggregation command option - */ - collation: Optional; - - /** - * The change stream will only provide changes that occurred at or after the - * specified timestamp. Any command run against the server will return - * an operation time that can be used here. - * @since 4.0 - * @see https://docs.mongodb.com/manual/reference/method/db.runCommand/ - * @note this is an option of the `$changeStream` pipeline stage. - */ - startAtOperationTime: Optional; - } - -**NOTE:** The set of ``ChangeStreamOptions`` may grow over time. - -Helper Method -------------- - -The driver API consists of a ``ChangeStream`` type, as well as three helper methods. All helpers MUST return a ``ChangeStream`` instance. Implementers MUST document that helper methods are preferred to running a raw aggregation with a ``$changeStream`` stage, for the purpose of supporting resumability. - -The helper methods must construct an aggregation command with a REQUIRED initial ``$changeStream`` stage. A driver MUST NOT throw a custom exception if multiple ``$changeStream`` stages are present (e.g. if a user also passed ``$changeStream`` in the pipeline supplied to the helper), as the server will return an error. - -The helper methods MUST determine a read concern for the operation in accordance with the `Read and Write Concern specification `_. The initial implementation of change streams on the server requires a “majority” read concern or no read concern. Drivers MUST document this requirement. Drivers SHALL NOT throw an exception if any other read concern is specified, but instead should depend on the server to return an error. - -The stage has the following shape: - -.. code:: typescript - - { $changeStream: ChangeStreamOptions } - -The first parameter of the helpers specifies an array of aggregation pipeline stages which MUST be appended to the initial stage. Drivers MUST support an empty pipeline. Languages which support default parameters MAY specify an empty array as the default value for this parameter. Drivers SHOULD otherwise make specification of a pipeline as similar as possible to the `aggregate `_ CRUD method. - -Additionally, implementors MAY provide a form of these methods which require no parameters, assuming no options and no additional stages beyond the initial ``$changeStream`` stage: - -.. code:: python - - for change in db.collection.watch(): - print(change) - -Presently change streams support only a subset of available aggregation stages: - -- ``$match`` -- ``$project`` -- ``$addFields`` -- ``$replaceRoot`` -- ``$redact`` - -A driver MUST NOT throw an exception if any unsupported stage is provided, but instead depend on the server to return an error. - -The aggregate helper methods MUST have no new logic related to the ``$changeStream`` stage. Drivers MUST be capable of handling `TAILABLE_AWAIT `_ cursors from the aggregate command in the same way they handle such cursors from find. - -``Collection.watch`` helper -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Returns a ``ChangeStream`` on a specific collection - -Command syntax: - -.. code:: typescript - - { - aggregate: 'collectionName' - pipeline: [{$changeStream: {...}}, ...], - ... - } - -``Database.watch`` helper -^^^^^^^^^^^^^^^^^^^^^^^^^ - -:since: 4.0 - -Returns a ``ChangeStream`` on all collections in a database. - -Command syntax: - -.. code:: typescript - - { - aggregate: 1 - pipeline: [{$changeStream: {...}}, ...], - ... - } - -Drivers MUST use the ``ns`` returned in the ``aggregate`` command to set the ``collection`` option in subsequent ``getMore`` commands. - -``MongoClient.watch`` helper -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:since: 4.0 - -Returns a ``ChangeStream`` on all collections in all databases in a cluster - -Command syntax: - -.. code:: typescript - - { - aggregate: 1 - pipeline: [{$changeStream: {allChangesForCluster: true, ...}}, ...], - ... - } - -The helper MUST run the command against the `admin` database - -Drivers MUST use the ``ns`` returned in the ``aggregate`` command to set the ``collection`` option in subsequent ``getMore`` commands. - -ChangeStream ------------- - -A ``ChangeStream`` is an abstraction of a `TAILABLE_AWAIT `_ cursor, with support for resumability. Implementors MAY choose to implement a ``ChangeStream`` as an extension of an existing tailable cursor implementation. If the ``ChangeStream`` is implemented as a type which owns a tailable cursor, then the implementor MUST provide a method to close the change stream, as well as satisfy the requirements of extending ``Iterable``. - -A change stream MUST track the last resume token returned by the iterator to the user, caching it locally for use in future attempts to resume. A driver MUST raise an error on the first response received without a resume token (e.g. the user has removed it with a pipeline stage), and close the change stream. The error message SHOULD resemble “Cannot provide resume functionality when the resume token is missing”. - -A change stream MUST attempt to resume a single time if it encounters any resumable error. A change stream MUST NOT attempt to resume on any other type of error, with the exception of a “not master” server error. If a driver receives a “not master” error (for instance, because the primary it was connected to is stepping down), it will treat the error as a resumable error and attempt to resume. - -In addition to tracking the most recently delivered resume token, change streams MUST also track the read preference specified when the change stream was created. In the event of a resumable error, a change stream MUST perform server selection with the original read preference before attempting to resume. - -Single Server Topologies -^^^^^^^^^^^^^^^^^^^^^^^^ - -Presently, change streams cannot be initiated on single server topologies as they do not have an oplog. Drivers MUST NOT throw an exception in this scenario, but instead rely on an error returned from the server. This allows for the server to seamlessly introduce support for this in the future, without need to make changes in driver code. - -startAtOperationTime -^^^^^^^^^^^^^^^^^^^^ - -:since: 4.0 - -``startAtOperationTime`` specifies that a change stream will only return changes that occurred at or after the specified ``Timestamp``. - -The server expects ``startAtOperationTime`` as a BSON Timestamp. Drivers MUST allow users to specify a ``startAtOperationTime`` option in the ``watch`` helpers. They MUST allow users to specify this value as a raw ``Timestamp``. - -``startAtOperationTime`` and ``resumeAfter`` are mutually exclusive; if both ``startAtOperationTime`` and ``resumeAfter`` are set, the server will return an error. Drivers MUST NOT throw a custom error, and MUST defer to the server error. - -If neither ``startAtOperationTime`` nor ``resumeAfter`` are specified, and the max wire version is >= ``7``, and the initial ``aggregate`` command does not return a resumeToken (indicating no results), the ``ChangeStream`` MUST save the ``operationTime`` from the initial ``aggregate`` command when it returns. - -resumeAfter -^^^^^^^^^^^ - -``resumeAfter`` is used to resume a changeStream that has been stopped to ensure that only changes starting with the log entry immediately *after* the provided token will be returned. If the resume token specified does not exist, the server will return an error. - -Resume Process -^^^^^^^^^^^^^^ - -Once a ``ChangeStream`` has encountered a resumable error, it MUST attempt to resume one time. The process for resuming MUST follow these steps: - -- Perform server selection. -- Connect to selected server. -- If the ``ChangeStream`` has not received any changes, and ``resumeAfter`` is not specified, and the max wire version is >= ``7``: - - - The driver MUST execute the known aggregation command. - - The driver MUST specify the ``startAtOperationTime`` key set to the ``startAtOperationTime`` provided by the user or saved from the original aggregation. - - The driver MUST NOT set a ``resumeAfter`` key. - - In this case, the ``ChangeStream`` will return all changes that occurred after the specified ``startAtOperationTime``. -- Else: - - - The driver MUST execute the known aggregation command. - - The driver MUST specify a ``resumeAfter`` with the last known ``resumeToken``. - - The driver MUST NOT set a ``startAtOperationTime``. - - If a ``startAtOperationTime`` key was part of the original aggregation command, the driver MUST remove it. - - In this case, the ``ChangeStream`` will return notifications starting with the oplog entry immediately *after* the provided token. - -If the server supports sessions, the resume attempt MUST use the same session as the previous attempt's command. - -A driver SHOULD attempt to kill the cursor on the server on which the cursor is opened during the resume process, and MUST NOT attempt to kill the cursor on any other server. - - -Notes and Restrictions -^^^^^^^^^^^^^^^^^^^^^^ - -**1. `fullDocument: updateLookup` can result in change documents larger than 16MB** - -There is a risk that if there is a large change to a large document, the full document and delta might result in a document larger than the 16MB limitation on BSON documents. If that happens the cursor will be closed, and a server error will be returned. - -**2. Users can remove the resume token with aggregation stages** - -It is possible for a user to specify the following stage: - -.. code:: javascript - - { $project: { _id: 0 } } - -Similar removal of the resume token is possible with the ``$redact`` and ``$replaceRoot`` stages. While this is not technically illegal, it makes it impossible for drivers to support resumability. Users may explicitly opt out of resumability by issuing a raw aggregation with a ``$changeStream`` stage. - -Rationale -========= - -------------------- -Why Helper Methods? -------------------- - -Change streams are a first class concept similar to CRUD or aggregation; the fact that they are initiated via an aggregation pipeline stage is merely an implementation detail. By requiring drivers to support top-level helper methods for this feature we not only signal this intent, but also solve a number of other potential problems: - -Disambiguation of the result type of this special-case aggregation pipeline (``ChangeStream``), and an ability to control the behaviors of the resultant cursor - -More accurate support for the concept of a maximum time the user is willing to wait for subsequent queries to complete on the resultant cursor (``maxAwaitTimeMs``) - -Finer control over the options pertaining specifically to this type of operation, without polluting the already well-defined ``AggregateOptions`` - -Flexibility for future potentially breaking changes for this feature on the server - ------------------------------------------------------------------- -Why are ChangeStreams required to retry once on a resumable error? ------------------------------------------------------------------- - -User experience is of the utmost importance. Errors not originating from the server are generally network errors, and network errors can be transient. Attempting to resume an interrupted change stream after the initial error allows for a seamless experience for the user, while subsequent network errors are likely to be an outage which can then be exposed to the user with greater confidence. - ---------------------------------------------------- -Why do we allow access to the resume token to users ---------------------------------------------------- - -Imagine a scenario in which a user wants to process each change to a collection **at least once**, but the application crashes during processing. In order to overcome this failure, a user might use the following approach: - -.. code:: python - - resumeToken = None - localChange = getChangeFromLocalStorage() - if localChange: - processChange(localChange) - resumeToken = localChange['_id'] - - try: - for change in db.collection.watch([...], resumeAfter=resumeToken): - persistToLocalStorage(change) - processChange(change) - except Exception: - log.error("...") - -In this case the current change is always persisted locally, including the resume token, such that on restart the application can still process the change while ensuring that the change stream continues from the right logical time in the oplog. It is the application's responsibility to ensure that ``processChange`` is idempotent, this design merely makes a reasonable effort to process each change **at least** once. - -------------------------------------------------------- -Why is there no example of the desired user experience? -------------------------------------------------------- - -The specification used to include this overspecified example of the "desired user experience": - -.. code:: python - - try: - for change in db.collection.watch(...): - print(change) - except Exception: - # We know for sure it's unrecoverable: - log.error("...") - -It was decided to remove this example from the specification for the following reasons: - -- Tailable + awaitData cursors behave differently in existing supported drivers. -- There are considerations to be made for languages that do not permit interruptible I/O (such as Java), where a change stream which blocks forever in a separate thread would necessitate killing the thread. -- There is something to be said for an API that allows cooperation by default. The model in which a call to next only blocks until any response is returned (even an empty batch), allows for interruption and cooperation (e.g. interaction with other event loops). - ----------------------------------------- -What do the additional error codes mean? ----------------------------------------- - -The `CursorKilled` or `Interrupted` error implies implies some other actor killed the cursor. - -The `CappedPositionLost` error implies falling off of the back of the oplog, -so resuming is impossible. - -------------------------------------------------------------------------------------------- -Why do we need to send a default ``startAtOperationTime`` when resuming a ``ChangeStream``? -------------------------------------------------------------------------------------------- - -``startAtOperationTime`` allows a user to create a resumable change stream even when a result -(and corresponding resumeToken) is not available until a later point in time. - -For example: - -- A client creates a ``ChangeStream``, and calls ``watch`` -- The ``ChangeStream`` sends out the initial ``aggregate`` call, and receives a response -with no initial values. Because there are no initial values, there is no latest resumeToken. -- The client's network is partitioned from the server, causing the client's ``getMore`` to time out -- Changes occur on the server. -- The network is unpartitioned -- The client attempts to resume the ``ChangeStream`` - -In the above example, not sending ``startAtOperationTime`` will result in the change stream missing -the changes that occurred while the server and client are partitioned. By sending ``startAtOperationTime``, -the server will know to include changes from that previous point in time. - -Test Plan -========= - -See `tests/README.rst `_ - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. - - -Reference Implementations -========================= - - - NODE (NODE-1055) - - PYTHON (PYTHON-1338) - - RUBY (RUBY-1228) - -Changelog -========= -+------------+------------------------------------------------------------+ -| 2017-08-03 | Initial commit | -+------------+------------------------------------------------------------+ -| 2017-08-07 | Fixed typo in command format | -+------------+------------------------------------------------------------+ -| 2017-08-16 | Added clarification regarding Resumable errors | -+------------+------------------------------------------------------------+ -| 2017-08-16 | Fixed formatting of resume process | -+------------+------------------------------------------------------------+ -| 2017-08-22 | Clarified killing cursors during resume process | -+------------+------------------------------------------------------------+ -| 2017-09-06 | Remove `desired user experience` example | -+------------+------------------------------------------------------------+ -| 2017-09-21 | Clarified that we need to close the cursor on missing token| -+------------+------------------------------------------------------------+ -| 2017-09-26 | Clarified that change stream options may be added later | -+------------+------------------------------------------------------------+ -| 2017-11-06 | Defer to Read and Write concern spec for determining a read| -| | concern for the helper method. | -+------------+------------------------------------------------------------+ -| 2017-12-13 | Default read concern is also accepted, not just "majority".| -+------------+------------------------------------------------------------+ -| 2018-04-17 | Clarified that the initial aggregate should not be retried.| -+------------+------------------------------------------------------------+ -| 2018-04-18 | Added helpers for Database and MongoClient, | -| | and added ``startAtClusterTime`` option. | -+------------+------------------------------------------------------------+ -| 2018-05-24 | Changed ``startatClusterTime`` to ``startAtOperationTime`` | -+------------+------------------------------------------------------------+ -| 2018-06-14 | Clarified how to calculate ``startAtOperationTime`` | -+------------+------------------------------------------------------------+ -| 2018-07-27 | Added drop to change stream operationType | -+------------+------------------------------------------------------------+ -| 2018-07-30 | Remove redundant error message checks for resumable errors | -+------------+------------------------------------------------------------+ -| 2018-09-09 | Added dropDatabase to change stream operationType | -+------------+------------------------------------------------------------+ +.. note:: + This specification has been converted to Markdown and renamed to + `change-streams.md `_. diff --git a/source/change-streams/tests/README.md b/source/change-streams/tests/README.md new file mode 100644 index 0000000000..3804168576 --- /dev/null +++ b/source/change-streams/tests/README.md @@ -0,0 +1,252 @@ +# Change Streams + +______________________________________________________________________ + +## 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. + +### Subdirectories for Test Formats + +This document describes the legacy format for change streams tests. Tests in this legacy format are located under +`./legacy/`. + +New change streams tests should be written in the +[unified test format](../../unified-test-format/unified-test-format.md) and placed under `./unified/`. + +## 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 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`, `sharded`, and `load-balanced`. + - `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 value `42` acting as placeholders for "any value" + +Pseudocode implementation of `actual` MATCHES `expected`: + +```text +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. When executing tests against a sharded cluster, + `globalClient` must only connect to one mongos. This is because tests that set failpoints will only work + consistently if both the `configureFailPoint` and failing commands are sent to the same mongos. + +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/legacy-test-format.md#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. `hello` or legacy hello) +- Using `client`, create a changeStream `changeStream` against the specified `target`. Use `changeStreamPipeline` and + `changeStreamOptions` if they are non-empty. Capture any error. +- If there was no error, use `globalClient` and run every operation in `operations` in serial against the server until + all operations have been executed or an error is thrown. Capture any error. +- If there was no error and `result.error` is set, iterate `changeStream` once and capture any error. +- If there was no error and `result.success` is non-empty, iterate `changeStream` until it returns as many changes as + there are elements in the `result.success` array or an error is thrown. Capture any error. +- 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` + - If `actual[idx]` is a `killCursors` event, skip it and move to `actual[idx+1]`. + - Else assert that `actual[idx]` MATCHES `expected` + - Note: the change stream test command event expectations cover a prefix subset of all command events published by the + driver. The test runner MUST verify that, if there are N expectations, that the first N events published by the + driver match the expectations, and MUST NOT inspect any subsequent events published by the driver. +- Close the MongoClient `client` + +After running all tests + +- Close the MongoClient `globalClient` +- Drop database `database_name` +- Drop database `database2_name` + +### Iterating the Change Stream + +Although synchronous drivers must provide a +[non-blocking mode of iteration](../change-streams.md#not-blocking-on-iteration), asynchronous drivers may not have such +a mechanism. Those drivers with only a blocking mode of iteration should be careful not to iterate the change stream +unnecessarily, as doing so could cause the test runner to block indefinitely. For this reason, the test runner procedure +above advises drivers to take a conservative approach to iteration. + +If the test expects an error and one was not thrown by either creating the change stream or executing the test's +operations, iterating the change stream once allows for an error to be thrown by a `getMore` command. If the test does +not expect any error, the change stream should be iterated only until it returns as many result documents as are +expected by the test. + +### Testing on Sharded Clusters + +When writing data on sharded clusters, majority-committed data does not always show up in the response of the first +`getMore` command after the data is written. This is because in sharded clusters, no data from shard A may be returned +until all other shard reports an entry that sorts after the change in shard A. + +To account for this, drivers MUST NOT rely on change stream documents in certain batches. For example, if expecting two +documents in a change stream, these may not be part of the same `getMore` response, or even be produced in two +subsequent `getMore` responses. Drivers MUST allow for a `getMore` to produce empty batches when testing on a sharded +cluster. By default, this can take up to 10 seconds, but can be controlled by enabling the `writePeriodicNoops` server +parameter and configuring the `periodNoopIntervalSecs` parameter. Choosing lower values allows for running change stream +tests with smaller timeouts. + +## Prose Tests + +The following tests have not yet been automated, but MUST still be tested. All tests SHOULD be run on both replica sets +and sharded clusters unless otherwise specified: + +1. `ChangeStream` must continuously track the last seen `resumeToken` + +2. `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) + +3. After receiving a `resumeToken`, `ChangeStream` will automatically resume one time on a resumable error with the + initial pipeline and options, except for the addition/update of a `resumeToken`. + +4. `ChangeStream` will not attempt to resume on any error encountered while executing an `aggregate` command. Note that + retryable reads may retry `aggregate` commands. Drivers should be careful to distinguish retries from resume + attempts. Alternatively, drivers may specify `retryReads=false` or avoid using a + [retryable error](../../retryable-reads/retryable-reads.md#retryable-error) for this test. + +5. **Removed** + +6. `ChangeStream` will perform server selection before attempting to resume, using initial `readPreference` + +7. 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. + +8. The `killCursors` command sent during the "Resume Process" must not be allowed to throw an exception. + +9. `$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 change stream. + +10. **Removed** + +11. For a `ChangeStream` under these conditions: + + - The batch is empty or has been iterated to the last document. + + Expected result: + + - `getResumeToken` must return the `postBatchResumeToken` from the current command response. + +12. **Removed** + +13. 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. + +14. 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. + + Note that this test cannot be run against sharded topologies because in that case the initial `aggregate` command + only establishes cursors on the shards and always returns an empty `firstBatch`. + +15. **Removed** + +16. **Removed** + +17. `$changeStream` stage for `ChangeStream` started with `startAfter` against a server `>=4.1.1` that has not received + any results yet MUST include a `startAfter` option and MUST NOT include a `resumeAfter` option when resuming a + change stream. + +18. `$changeStream` stage for `ChangeStream` started with `startAfter` against a server `>=4.1.1` that has received at + least one result MUST include a `resumeAfter` option and MUST NOT include a `startAfter` option when resuming a + change stream. + +19. Validate that large `ChangeStream` events are split when using `$changeStreamSplitLargeEvent`: + + 1. Run only against servers `>=6.0.9 && <6.1` or `>=7.0`. + 2. Create a new collection `_[C]()` with `changeStreamPreAndPostImages` enabled. + 3. Insert into `_[C]()` a document at least 10mb in size, e.g. `{ "value": "q"*10*1024*1024 }` + 4. Create a change stream `_[S]()` by calling `watch` on `_[C]()` with pipeline + `[{ "$changeStreamSplitLargeEvent": {} }]` and `fullDocumentBeforeChange=required`. + 5. Call `updateOne` on `_[C]()` with an empty `query` and an update setting the field to a new large value, e.g. + `{ "$set": { "value": "z"*10*1024*1024 } }`. + 6. Collect two events from `_[S]()`. + 7. Assert that the events collected have `splitEvent` fields `{ "fragment": 1, "of": 2 }` and + `{ "fragment": 2, "of": 2 }`, in that order. diff --git a/source/change-streams/tests/README.rst b/source/change-streams/tests/README.rst deleted file mode 100644 index 98fdab0b72..0000000000 --- a/source/change-streams/tests/README.rst +++ /dev/null @@ -1,157 +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``: Reserved for later use - - ``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``: 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`` - -- 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 ``results.error`` - -- Else: - - - Assert that no error was expected for the test - - Assert that the changes received from ``changeStream`` MATCH the results in ``results.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 -#. ``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`` 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. diff --git a/source/change-streams/tests/change-streams-errors.json b/source/change-streams/tests/change-streams-errors.json deleted file mode 100644 index 053ac43e70..0000000000 --- a/source/change-streams/tests/change-streams-errors.json +++ /dev/null @@ -1,78 +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": [], - "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": { - "fullDocument": "default" - } - }, - { - "$unsupported": "foo" - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "error": { - "code": 40324 - } - } - } - ] -} diff --git a/source/change-streams/tests/change-streams-errors.yml b/source/change-streams/tests/change-streams-errors.yml deleted file mode 100644 index 1286e86588..0000000000 --- a/source/change-streams/tests/change-streams-errors.yml +++ /dev/null @@ -1,53 +0,0 @@ -collection_name: &collection_name "test" -database_name: &database_name "change-stream-tests" -collection2_name: &collection2_name "test2" -database2_name: &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: [] - 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: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - z: 3 - expectations: - - - command_started_event: - command: - aggregate: *collection_name - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - - - $unsupported: foo - command_name: aggregate - database_name: *database_name - result: - error: - code: 40324 \ No newline at end of file diff --git a/source/change-streams/tests/change-streams.json b/source/change-streams/tests/change-streams.json deleted file mode 100644 index 5c28b6276f..0000000000 --- a/source/change-streams/tests/change-streams.json +++ /dev/null @@ -1,445 +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": { - "fullDocument": "default" - } - } - ] - }, - "command_name": "aggregate", - "database_name": "change-stream-tests" - } - } - ], - "result": { - "success": [] - } - }, - { - "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": [], - "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": { - "fullDocument": "default" - } - } - ] - }, - "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": { - "fullDocument": "default" - } - }, - { - "$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": { - "fullDocument": "default" - } - } - ] - }, - "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": { - "fullDocument": "default", - "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" - } - } - } - ] - } - } - ] -} diff --git a/source/change-streams/tests/change-streams.yml b/source/change-streams/tests/change-streams.yml deleted file mode 100644 index 720a22f973..0000000000 --- a/source/change-streams/tests/change-streams.yml +++ /dev/null @@ -1,299 +0,0 @@ -collection_name: &collection_name "test" -database_name: &database_name "change-stream-tests" -collection2_name: &collection2_name "test2" -database2_name: &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: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - x: 1 - expectations: - - - command_started_event: - command: - aggregate: *collection_name - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - command_name: aggregate - database_name: *database_name - result: - success: [] - - - 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: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - x: 1 - expectations: [] - result: - success: - - - _id: "42" - documentKey: "42" - operationType: insert - ns: - db: *database_name - coll: *collection_name - 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: *database_name - collection: *collection2_name - name: insertOne - arguments: - document: - x: 1 - - - database: *database2_name - collection: *collection_name - name: insertOne - arguments: - document: - y: 2 - - - database: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - z: 3 - expectations: - - - command_started_event: - command: - aggregate: *collection_name - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - command_name: aggregate - database_name: *database_name - result: - success: - - - operationType: insert - ns: - db: *database_name - coll: *collection_name - 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: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - y: 2 - - - database: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - z: 3 - expectations: - - - command_started_event: - command: - aggregate: *collection_name - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - - - $match: - "fullDocument.z": - $numberInt: "3" - command_name: aggregate - database_name: *database_name - result: - success: - - - operationType: insert - ns: - db: *database_name - coll: *collection_name - 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: *database_name - collection: *collection2_name - name: insertOne - arguments: - document: - x: 1 - - - database: *database2_name - collection: *collection_name - name: insertOne - arguments: - document: - y: 2 - - - database: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - z: 3 - expectations: - - - command_started_event: - command: - aggregate: - $numberInt: "1" - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - command_name: aggregate - database_name: *database_name - result: - success: - - - operationType: insert - ns: - db: *database_name - coll: *collection2_name - fullDocument: - x: - $numberInt: "1" - - - operationType: insert - ns: - db: *database_name - coll: *collection_name - 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: *database_name - collection: *collection2_name - name: insertOne - arguments: - document: - x: 1 - - - database: *database2_name - collection: *collection_name - name: insertOne - arguments: - document: - y: 2 - - - database: *database_name - collection: *collection_name - name: insertOne - arguments: - document: - z: 3 - expectations: - - - command_started_event: - command: - aggregate: - $numberInt: "1" - cursor: {} - pipeline: - - - $changeStream: - fullDocument: default - allChangesForCluster: true - command_name: aggregate - database_name: admin - result: - success: - - - operationType: insert - ns: - db: *database_name - coll: *collection2_name - fullDocument: - x: - $numberInt: "1" - - - operationType: insert - ns: - db: *database2_name - coll: *collection_name - fullDocument: - y: - $numberInt: "2" - - - operationType: insert - ns: - db: *database_name - coll: *collection_name - fullDocument: - z: - $numberInt: "3" diff --git a/source/change-streams/tests/unified/change-streams-clusterTime.json b/source/change-streams/tests/unified/change-streams-clusterTime.json new file mode 100644 index 0000000000..2b09e548f1 --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-clusterTime.yml b/source/change-streams/tests/unified/change-streams-clusterTime.yml new file mode 100644 index 0000000000..b1d9f20e01 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-clusterTime.yml @@ -0,0 +1,41 @@ +description: "change-streams-clusterTime" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 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 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/source/change-streams/tests/unified/change-streams-disambiguatedPaths.json b/source/change-streams/tests/unified/change-streams-disambiguatedPaths.json new file mode 100644 index 0000000000..df7422295a --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-disambiguatedPaths.json @@ -0,0 +1,272 @@ +{ + "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", + "runOnRequirements": [ + { + "minServerVersion": "6.1.0", + "maxServerVersion": "8.1.99", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "8.2.1", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "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/source/change-streams/tests/unified/change-streams-disambiguatedPaths.yml b/source/change-streams/tests/unified/change-streams-disambiguatedPaths.yml new file mode 100644 index 0000000000..761193eda2 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-disambiguatedPaths.yml @@ -0,0 +1,112 @@ +description: "disambiguatedPaths" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 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" + # skip server version 8.2.0, which emits disambiguatedPaths unconditionally + runOnRequirements: + - minServerVersion: "6.1.0" + maxServerVersion: "8.1.99" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + - minServerVersion: "8.2.1" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, 'a': { '1': 1 } } + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 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 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 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/source/change-streams/tests/unified/change-streams-errors.json b/source/change-streams/tests/unified/change-streams-errors.json new file mode 100644 index 0000000000..65e99e541e --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-errors.yml b/source/change-streams/tests/unified/change-streams-errors.yml new file mode 100644 index 0000000000..85133dae0a --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-errors.yml @@ -0,0 +1,120 @@ +description: "change-streams-errors" + +schemaVersion: "1.7" + +runOnRequirements: + - serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ killCursors ] + useMultipleMongoses: false + - client: + id: &globalClient globalClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + - database: + id: &globalDatabase0 globalDatabase0 + client: *globalClient + databaseName: *database0 + - collection: + id: &globalCollection0 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 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 changeStream0 + - name: insertOne + object: *globalCollection0 + arguments: + document: { z: 3 } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectError: { errorCode: 216 } diff --git a/source/change-streams/tests/unified/change-streams-nsType.json b/source/change-streams/tests/unified/change-streams-nsType.json new file mode 100644 index 0000000000..877ca9c007 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-nsType.json @@ -0,0 +1,104 @@ +{ + "description": "change-streams-nsType", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "8.1.0", + "topologies": [ + "replicaset", + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + } + ], + "tests": [ + { + "description": "nsType is present when creating collections", + "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", + "nsType": "collection" + } + } + ] + }, + { + "description": "nsType is present when creating views", + "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", + "nsType": "view" + } + } + ] + } + ] +} diff --git a/source/change-streams/tests/unified/change-streams-nsType.yml b/source/change-streams/tests/unified/change-streams-nsType.yml new file mode 100644 index 0000000000..3471767c52 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-nsType.yml @@ -0,0 +1,60 @@ +description: "change-streams-nsType" +schemaVersion: "1.7" +runOnRequirements: + - minServerVersion: "8.1.0" + topologies: [ replicaset, sharded ] + serverless: forbid +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + +tests: + - description: "nsType is present when creating collections" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: &collection0 foo + - name: createChangeStream + object: *database0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 changeStream0 + - name: createCollection + object: *database0 + arguments: + collection: *collection0 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: create + nsType: collection + + - description: "nsType is present when creating views" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: &collection0 foo + - name: createChangeStream + object: *database0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 changeStream0 + - name: createCollection + object: *database0 + arguments: + collection: *collection0 + viewOn: testName + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: create + nsType: view \ No newline at end of file diff --git a/source/change-streams/tests/unified/change-streams-pre_and_post_images.json b/source/change-streams/tests/unified/change-streams-pre_and_post_images.json new file mode 100644 index 0000000000..e62fc03459 --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-pre_and_post_images.yml b/source/change-streams/tests/unified/change-streams-pre_and_post_images.yml new file mode 100644 index 0000000000..6bc58eaf2d --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-pre_and_post_images.yml @@ -0,0 +1,351 @@ +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 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ collMod, insert, update, getMore, killCursors ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name change-stream-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + +tests: + - description: "fullDocument:whenAvailable with changeStreamPreAndPostImages enabled" + operations: + - name: runCommand + object: *database0 + arguments: &enablePreAndPostImages + commandName: collMod + command: + collMod: *collection0Name + changeStreamPreAndPostImages: { enabled: true } + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocument: "whenAvailable" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocument: { _id: 1, x: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocument: "whenAvailable" } + + - description: "fullDocument:whenAvailable with changeStreamPreAndPostImages disabled" + operations: + - name: runCommand + object: *database0 + arguments: &disablePreAndPostImages + commandName: collMod + command: + collMod: *collection0Name + changeStreamPreAndPostImages: { enabled: false } + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocument: "whenAvailable" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocument: null + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocument: "whenAvailable" } + + - description: "fullDocument:required with changeStreamPreAndPostImages enabled" + operations: + - name: runCommand + object: *database0 + arguments: *enablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocument: "required" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocument: { _id: 1, x: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocument: "required" } + + - description: "fullDocument:required with changeStreamPreAndPostImages disabled" + operations: + - name: runCommand + object: *database0 + arguments: *disablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocument: "required" + saveResultAsEntity: &changeStream0 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: *collection0Name + pipeline: + - $changeStream: { fullDocument: "required" } + + - description: "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages enabled" + operations: + - name: runCommand + object: *database0 + arguments: *enablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "whenAvailable" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocumentBeforeChange: { _id: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "whenAvailable" } + + - description: "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages disabled" + operations: + - name: runCommand + object: *database0 + arguments: *disablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "whenAvailable" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocumentBeforeChange: null + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "whenAvailable" } + + - description: "fullDocumentBeforeChange:required with changeStreamPreAndPostImages enabled" + operations: + - name: runCommand + object: *database0 + arguments: *enablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "required" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocumentBeforeChange: { _id: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "required" } + + - description: "fullDocumentBeforeChange:required with changeStreamPreAndPostImages disabled" + operations: + - name: runCommand + object: *database0 + arguments: *disablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "required" + saveResultAsEntity: &changeStream0 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: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "required" } + + - description: "fullDocumentBeforeChange:off with changeStreamPreAndPostImages enabled" + operations: + - name: runCommand + object: *database0 + arguments: *enablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "off" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocumentBeforeChange: { $$exists: false } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "off" } + + - description: "fullDocumentBeforeChange:off with changeStreamPreAndPostImages disabled" + operations: + - name: runCommand + object: *database0 + arguments: *disablePreAndPostImages + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + fullDocumentBeforeChange: "off" + saveResultAsEntity: &changeStream0 changeStream0 + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 }} + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: "update" + ns: { db: *database0Name, coll: *collection0Name } + updateDescription: { $$type: "object" } + fullDocumentBeforeChange: { $$exists: false } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $changeStream: { fullDocumentBeforeChange: "off" } diff --git a/source/change-streams/tests/unified/change-streams-resume-allowlist.json b/source/change-streams/tests/unified/change-streams-resume-allowlist.json new file mode 100644 index 0000000000..1ec72b432b --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-resume-allowlist.yml b/source/change-streams/tests/unified/change-streams-resume-allowlist.yml new file mode 100644 index 0000000000..c5b7a874d3 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-resume-allowlist.yml @@ -0,0 +1,1169 @@ +# Tests for resume behavior on server versions that do not support the ResumableChangeStreamError label +description: "change-streams-resume-allowlist" + +schemaVersion: "1.7" + +runOnRequirements: + - minServerVersion: "3.6" + topologies: [ replicaset, sharded, load-balanced ] + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ killCursors ] + useMultipleMongoses: false + - client: + id: &globalClient globalClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + - database: + id: &globalDatabase0 globalDatabase0 + client: *globalClient + databaseName: *database0 + - collection: + id: &globalCollection0 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 + + # CursorNotFound is special-cased to be resumable regardless of server versions or error labels, so this test has + # no maxWireVersion. + - 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 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/source/change-streams/tests/unified/change-streams-resume-errorLabels.json b/source/change-streams/tests/unified/change-streams-resume-errorLabels.json new file mode 100644 index 0000000000..7fd70108f0 --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-resume-errorLabels.yml b/source/change-streams/tests/unified/change-streams-resume-errorLabels.yml new file mode 100644 index 0000000000..5cc6d423a4 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-resume-errorLabels.yml @@ -0,0 +1,1069 @@ +# Tests for resume behavior on server versions that support the ResumableChangeStreamError label +description: "change-streams-resume-errorlabels" + +schemaVersion: "1.7" + +runOnRequirements: + - minServerVersion: "4.3.1" + topologies: [ replicaset, sharded, load-balanced ] + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ killCursors ] + useMultipleMongoses: false + - client: + id: &globalClient globalClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + - database: + id: &globalDatabase0 globalDatabase0 + client: *globalClient + databaseName: *database0 + - collection: + id: &globalCollection0 globalCollection0 + database: *globalDatabase0 + collectionName: *collection0 + +tests: + - description: change stream resumes after HostUnreachable + operations: + - name: failPoint + object: testRunner + arguments: + client: *globalClient + failPoint: + configureFailPoint: failGetMoreAfterCursorCheckout # SERVER-46091 explains why a new failpoint was needed + mode: { times: 1 } + data: + errorCode: 6 + closeConnection: false + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 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 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 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 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 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 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 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 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 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 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 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 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: + # StaleShardVersion is obsolete as of 6.1 and is no longer marked as resumable. + - 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 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 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 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 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 + + # The next two tests ensure that the driver only uses the error label, not the allow list. + - 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 # Use an error code that does not have the allow list label by default + closeConnection: false + errorLabels: [ ResumableChangeStreamError ] + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 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 # failCommand will not add the allow list error label + mode: { times: 1 } + data: + failCommands: [ getMore ] + errorCode: 6 # Use an error code that is on the allow list + closeConnection: false + - name: createChangeStream + object: *collection0 + arguments: { pipeline: [] } + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *globalCollection0 + arguments: + document: { x: 1 } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectError: { errorCode: 6 } diff --git a/source/change-streams/tests/unified/change-streams-showExpandedEvents.json b/source/change-streams/tests/unified/change-streams-showExpandedEvents.json new file mode 100644 index 0000000000..b9594e0c1e --- /dev/null +++ b/source/change-streams/tests/unified/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/source/change-streams/tests/unified/change-streams-showExpandedEvents.yml b/source/change-streams/tests/unified/change-streams-showExpandedEvents.yml new file mode 100644 index 0000000000..e6289047bf --- /dev/null +++ b/source/change-streams/tests/unified/change-streams-showExpandedEvents.yml @@ -0,0 +1,307 @@ +description: "change-streams-showExpandedEvents" +schemaVersion: "1.7" +runOnRequirements: + - minServerVersion: "6.0.0" + topologies: [ replicaset, sharded ] + serverless: forbid +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ killCursors ] + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + - database: + id: &database1 database1 + client: *client0 + databaseName: *database1 + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection1 + - database: + id: &shardedDb shardedDb + client: *client0 + databaseName: *shardedDb + - database: + id: &adminDb adminDb + client: *client0 + databaseName: admin + - collection: + id: &shardedCollection 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 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 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: &existing-collection foo + - name: createCollection + object: *database0 + arguments: + collection: *existing-collection + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 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: *existing-collection + 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: *existing-collection + operationDescription: + dropTarget: + $$exists: true + to: + db: *database0 + coll: *existing-collection + + - description: "when showExpandedEvents is true, createIndex events are reported" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: + # On sharded clusters, the create command run when loading initial + # data sometimes is still reported in the change stream. To avoid + # this, we exclude the create command when creating the change + # stream, but specifically don't exclude other events to still catch + # driver errors. + - $match: + operationType: + $ne: create + showExpandedEvents: true + saveResultAsEntity: &changeStream0 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: &index1 x_1 + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 changeStream0 + - name: dropIndex + object: *collection0 + arguments: + name: *index1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: dropIndexes + + - description: "when showExpandedEvents is true, create events are reported" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: &collection1 foo + - name: createChangeStream + object: *database0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 changeStream0 + - name: createCollection + object: *database0 + arguments: + collection: *collection1 + - 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: &collection1 foo + - name: createChangeStream + object: *database0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 changeStream0 + - name: createCollection + object: *database0 + arguments: + collection: *collection1 + 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: &index2 x_2 + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + showExpandedEvents: true + saveResultAsEntity: &changeStream0 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: + # Note: minServerVersion is specified in top-level 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 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/source/change-streams/tests/unified/change-streams.json b/source/change-streams/tests/unified/change-streams.json new file mode 100644 index 0000000000..a155d85b6e --- /dev/null +++ b/source/change-streams/tests/unified/change-streams.json @@ -0,0 +1,1805 @@ +{ + "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 + } + ], + "disambiguatedPaths": { + "$$unsetOrMatches": { + "$$exists": true + } + } + } + } + } + ] + }, + { + "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 + } + }, + "disambiguatedPaths": { + "$$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/source/change-streams/tests/unified/change-streams.yml b/source/change-streams/tests/unified/change-streams.yml new file mode 100644 index 0000000000..6e9f8d6a75 --- /dev/null +++ b/source/change-streams/tests/unified/change-streams.yml @@ -0,0 +1,929 @@ +description: "change-streams" + +schemaVersion: "1.7" + +runOnRequirements: + - minServerVersion: "3.6" + # TODO(DRIVERS-2323): Run all possible tests against sharded clusters once we know the + # cause of unexpected command monitoring events. + topologies: [ replicaset ] + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + ignoreCommandMonitoringEvents: [ killCursors ] + useMultipleMongoses: false + - client: + id: &globalClient globalClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + - database: + id: &database1 database1 + client: *client0 + databaseName: *database1 + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection1 + - database: + id: &globalDatabase0 globalDatabase0 + client: *globalClient + databaseName: *database0 + - collection: + id: &globalCollection0 globalCollection0 + database: *globalDatabase0 + collectionName: *collection0 + - database: + id: &globalDatabase1 globalDatabase1 + client: *globalClient + databaseName: *database1 + - collection: + id: &globalCollection1 globalCollection1 + database: *globalDatabase1 + collectionName: *collection1 + # Some tests run operations against db1.coll0 or db0.coll1 + - collection: + id: &globalDb1Collection0 globalDb1Collection0 + database: *globalDatabase1 + collectionName: *collection0 + - collection: + id: &globalDb0Collection1 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 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" + }, + # It is up to the MongoDB server to decide how to report a change. + # This expectation is based on the current MongoDB server behavior. + # Alternatively, we could have used a set of possible expectations of which only one + # must be satisfied, but the unified test format does not support this. + "updateDescription": { + "updatedFields": {}, + "removedFields": [], + "truncatedArrays": [ + { + "field": "array", + "newSize": 2 + } + ], + disambiguatedPaths: { $$unsetOrMatches: { $$exists: true } } + } + } + + - description: "Test with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: &comment0 { name: "test1" } + saveResultAsEntity: &changeStream0 changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *comment0 + + - description: "Test with document comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: &comment0 { name: "test1" } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *comment0 + + - description: "Test with string comment" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: "comment" + saveResultAsEntity: &changeStream0 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: &commentDoc + key: "value" + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *collection0 + arguments: + document: &new_document + _id: 1 + a: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *commentDoc + - commandStartedEvent: + command: + insert: *collection0 + documents: + - *new_document + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0 + comment: *commentDoc + 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 changeStream0 + - name: insertOne + object: *collection0 + arguments: + document: &new_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: + - *new_document + - 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 changeStream0 + - name: dropCollection + object: *database0 + arguments: + collection: &collection1 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: + # using $project to simulate future changes to ChangeStreamDocument structure + pipeline: [ { $project: { operationType: "addedInFutureMongoDBVersion", ns: 1 } } ] + saveResultAsEntity: &changeStream0 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: + # using $project to simulate future changes to ChangeStreamDocument structure + pipeline: [ { $project: { operationType: 1, ns: 1, newField: "newFieldValue" } } ] + saveResultAsEntity: &changeStream0 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: + # using $project to simulate future changes to ChangeStreamDocument structure + pipeline: [ { $project: { operationType: "insert", "ns.viewOn": "db.coll" } } ] + saveResultAsEntity: &changeStream0 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: + # using $project to simulate future changes to ChangeStreamDocument structure + pipeline: [ { $project: { operationType: "insert", ns: { db: "$ns.db", coll: "$ns.coll", viewOn: "db.coll" } } } ] + saveResultAsEntity: &changeStream0 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" + # Server returns an error if _id is modified on versions 4.2 and higher + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [ { $project: { _id: 0 } } ] + saveResultAsEntity: &changeStream0 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 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 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 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 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 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 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 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 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 } } + disambiguatedPaths: { $$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 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 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 + + # Test that resume logic works correctly even after consecutive retryable failures of a getMore command, + # with no intervening events. This is ensured by setting the batch size of the change stream to 1, + - 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 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 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/source/client-backpressure/client-backpressure.md b/source/client-backpressure/client-backpressure.md new file mode 100644 index 0000000000..6aea3d2aaa --- /dev/null +++ b/source/client-backpressure/client-backpressure.md @@ -0,0 +1,442 @@ +# Client Backpressure + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This specification adds the ability for drivers to automatically retry requests that fail due to server overload errors +while applying backpressure to avoid further overloading the server. + +The retry behaviors defined in this specification are separate from and complementary to the retry behaviors defined in +the [Retryable Reads](../retryable-reads/retryable-reads.md) and +[Retryable Writes](../retryable-writes/retryable-writes.md) specifications. This specification expands retry support to +all commands when specific server overload conditions are encountered, regardless of whether the command would normally +be retryable under those specifications. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### Ingress Connection Rate Limiter + +A token bucket based system introduced in MongoDB 8.2 to admit, reject or queue connection requests. It aims to prevent +connection spikes from overloading the system. + +#### Ingress Request Rate Limiter + +A token bucket based system introduced in MongoDB 8.2 to admit a command or reject it with an overload error at the +front door of a mongod/s. It aims to prevent command spikes from overloading the system. + +The ingress request rate limiter only applies to commands sent on authenticated connections. + +#### MongoTune + +Mongotune is a policy engine outside the server (mongod or mongos) which monitors a set of metrics (MongoDB or system +host) to dynamically configure MongoDB settings. MongoTune is deployed to Atlas clusters and will dynamically configure +the connection and request rate limiters to prevent and mitigate overloading the system. + +#### RetryableError label + +This error label indicates that a command is safely retryable regardless of the command type (read or write), its +metadata, or any of its arguments. + +#### SystemOverloadedError label + +An error is considered to be an overload error if it contains the `SystemOverloadedError` label. This error label +indicates that the server is overloaded. If this error label is present, drivers will backoff before attempting a retry. + +#### Retryable Overload Error + +An error which indicates that it is an overload error (contains the `SystemOverloadedError` label) and contains the +`RetryableError` label. + +For example, when a request exceeds the ingress request rate limit, the following error may be returned: + +```js +{ + 'ok': 0.0, + 'errmsg': "Rate limiter 'ingressRequestRateLimiter' rate exceeded", + 'code': 462, + 'codeName': 'IngressRequestRateLimitExceeded', + 'errorLabels': ['SystemOverloadedError', 'RetryableError'], +} +``` + +Note that an error is not guaranteed to contain both the `SystemOverloadedError` and the `RetryableError` labels just +because it contains one of them. + +#### Goodput + +The throughput of positive, useful output. In the context of drivers, this refers to the number of non-error results +that the driver processes per unit of time. + +See [goodput](https://en.wikipedia.org/wiki/Goodput). + +### Requirements for Client Backpressure + +#### Driver mechanisms subject to the retry policy + +Commands sent by the driver to the server are subject to the retry policy defined in this specification unless the +command is included in the exceptions below. + +Driver commands not subject to the overload retry policy: + +- [Monitoring commands](../server-discovery-and-monitoring/server-monitoring.md#monitoring) and + [round-trip time pingers](../server-discovery-and-monitoring/server-monitoring.md#measuring-rtt) (see + [Why not apply the overload retry policy to monitoring and RTT connections?](./client-backpressure.md#why-not-apply-the-overload-retry-policy-to-monitoring-and-rtt-connections)). +- Commands executed during + [connection establishment](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#establishing-a-connection-internal-implementation) + and [reauthentication](../auth/auth.md) (see + [Why not apply the overload policy to authentication commands or reauthentication commands?](./client-backpressure.md#why-not-apply-the-overload-policy-to-authentication-commands-or-reauthentication-commands)). + +Note: Drivers communicate with [mongocryptd](../client-side-encryption/client-side-encryption.md#mongocryptd) using the +driver's `runCommand()` API. Consequently, drivers will implicitly apply the retry policy to communication with +mongocryptd, although in practice the retry policy would never be used because mongocryptd connections are not +authenticated. + +#### Overload retry policy + +This specification expands the driver's retry ability to all commands if the error indicates that it is a retryable +overload error, including those not eligible for retry under the +[read](../retryable-reads/retryable-reads.md)/[write](../retryable-writes/retryable-writes.md) retry policies such as +updateMany, create collection, getMore, and generic runCommand. The new command execution method obeys the following +rules: + +1. `attempt` is the execution attempt number (starting with 0). Note that `attempt` includes retries for errors that are + not overload errors (this might include attempts under other retry policies, see + [Interactions with Other Retry Policies](./client-backpressure.md#interaction-with-other-retry-policies)). +2. A retry attempt will only be permitted if: + 1. The error is a retryable overload error. + 2. We have not reached `MAX_RETRIES`. + - The default value of `MAX_RETRIES` is 2. Drivers MUST expose `maxAdaptiveRetries` as a configurable option for + this maximum. In the future, this default value or the default behavior of the driver may change without being + considered a breaking change. + - This intentionally changes the behavior of CSOT which otherwise would retry an unlimited number of times within + the timeout to avoid retry storms. + 3. (CSOT-only): There is still time for a retry attempt according to the + [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) + specification. + 4. The command is a write and [retryWrites](../retryable-writes/retryable-writes.md#retrywrites) is enabled or the + command is a read and [retryReads](../retryable-reads/retryable-reads.md#retryreads) is enabled. + - To retry `runCommand`, both [retryWrites](../retryable-writes/retryable-writes.md#retrywrites) and + [retryReads](../retryable-reads/retryable-reads.md#retryreads) MUST be enabled. See + [Why must both `retryWrites` and `retryReads` be enabled to retry runCommand?](client-backpressure.md#why-must-both-retrywrites-and-retryreads-be-enabled-to-retry-runcommand) +3. If the request is eligible for retry (as outlined in step 2 above), the client MUST apply exponential backoff + according to the following formula: `backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2^(attempt - 1))` + - `jitter` is a random jitter value between 0 and 1. + - `BASE_BACKOFF` is constant 100ms. + - `MAX_BACKOFF` is 10000ms. + - This results in delays of 100ms and 200ms before accounting for jitter. +4. If the request is eligible for retry (as outlined in step 2 above) and `enableOverloadRetargeting` is enabled, the + client MUST add the previously used server's address to the list of deprioritized server addresses for + [server selection](../server-selection/server-selection.md). Drivers MUST expose `enableOverloadRetargeting` as a + configurable boolean option that defaults to `false`. +5. If the request is eligible for retry (as outlined in step 2 above) and is a retryable write: + 1. If the command is a part of a transaction, the instructions for command modification on retry for commands in + transactions MUST be followed, as outlined in the + [transactions](../transactions/transactions.md#interaction-with-retryable-writes) specification. + 2. If the command is a not a part of a transaction, the instructions for command modification on retry for retryable + writes MUST be followed, as outlined in the [retryable writes](../retryable-writes/retryable-writes.md) + specification. +6. If the request is not eligible for any retries, then the client MUST propagate errors following the behaviors + described in the [retryable reads](../retryable-reads/retryable-reads.md), + [retryable writes](../retryable-writes/retryable-writes.md) and the [transactions](../transactions/transactions.md) + specifications. + - For the purposes of error propagation, `runCommand` is considered a write. + +#### Interaction with Other Retry Policies + +The retry policy in this specification is separate from the other retry policies defined in the +[retryable reads](../retryable-reads/retryable-reads.md) and [retryable writes](../retryable-writes/retryable-writes.md) +specifications. Drivers MUST ensure: + +- When a failed attempt is retried, backoff MUST be applied if and only if the error is an overload error. +- If an overload error is encountered: + - Regardless of whether CSOT is enabled or not, the maximum number of retries for any retry policy becomes + `MAX_RETRIES`. This limiting applies to all retry attempts, including retries for errors that are not overload + errors. + - If CSOT is enabled and a command has been retried at least `MAX_RETRIES` times, it MUST NOT be retried further. + +#### Pseudocode + +The following pseudocode demonstrates the unified retry policy, combining the overload retry policy defined in this +specification with the retry policies from [Retryable Reads](../retryable-reads/retryable-reads.md) and +[Retryable Writes](../retryable-writes/retryable-writes.md). For brevity, some interactions with other specs are not +included, such as error handling with `NoWritesPerformed` labels. + +```python +# Note: the values below have been scaled down by a factor of 1000 because +# Python's sleep API takes a duration in seconds, not milliseconds. +BASE_BACKOFF = 0.1 # 100ms +MAX_BACKOFF = 10 # 10000ms + +MAX_RETRIES = 2 + +def execute_command_retryable(command, ...): + deprioritized_servers = [] + attempt = 0 + allowed_retries = if is_csot then math.inf else 1 + + while True: + try: + server = select_server(deprioritized_servers) + connection = server.getConnection() + res = execute_command(connection, command) + return res + except PyMongoError as exc: + is_retryable = (is_retryable_write(command, exc) + or is_retryable_read(command, exc) + or (exc.contains_error_label("RetryableError") and exc.contains_error_label("SystemOverloadedError"))) + is_overload = exc.contains_error_label("SystemOverloadedError") + + # Raise if the error is non-retryable. + if not is_retryable: + raise + + attempt += 1 + if is_overload: + allowed_retries = MAX_RETRIES + + if attempt > allowed_retries: + raise + + # enableOverloadRetargeting is true + if overload_retargeting: + deprioritized_servers.append(server.address) + + if is_overload: + jitter = random.random() # Random float between [0.0, 1.0). + backoff = jitter * min(MAX_BACKOFF, BASE_BACKOFF * 2 ** (attempt - 1)) + + # If the delay exceeds the deadline, bail early. + if _csot.get_timeout(): + if time.monotonic() + backoff > _csot.get_deadline(): + raise + + time.sleep(backoff) +``` + +#### Handshake changes + +Drivers conforming to this spec MUST add `"backpressure": True` to the +[connection handshake](../mongodb-handshake/handshake.rst). This flag allows the server to identify clients which do and +do not support backpressure. Currently, this flag is unused but in the future the server may offer different rate +limiting behavior for clients that do not support backpressure. + +#### Implementation notes + +On some platforms sleep() can have a very low precision, meaning an attempt to sleep for 50ms may actually sleep for a +much larger time frame. Drivers are not required to work around this limitation. + +### Logging Retry Attempts + +[As with retryable writes](../retryable-writes/retryable-writes.md#logging-retry-attempts), drivers MAY choose to log +retry attempts for load shed commands. This specification does not define a format for such log messages. + +### Command Monitoring + +[As with retryable writes](../retryable-writes/retryable-writes.md#command-monitoring), in accordance with the +[Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) specification, +drivers MUST guarantee that each `CommandStartedEvent` has either a correlating `CommandSucceededEvent` or +`CommandFailedEvent` and that every "command started" log message has either a correlating "command succeeded" log +message or "command failed" log message. If an attempt of a retryable command encounters a retryable error, drivers MUST +fire a `CommandFailedEvent` and emit a "command failed" log message for the retryable error and fire a separate +`CommandStartedEvent` and emit a separate "command started" log message when executing the subsequent retry attempt. +Note that for retries, `CommandStartedEvent`s and "command started" log message may have different `connectionId`s, +since a server is reselected for a retry attempt. + +### Documentation + +1. Drivers MUST document that all commands support retries on server overload. +2. Driver release notes MUST make it clear to users that they may need to adjust custom retry logic to prevent an + application from inadvertently retrying for too long (see [Backwards Compatibility](#backwards-compatibility) for + details). + +### Backwards Compatibility + +The server's rate limiting can introduce higher error rates than previously would have been exposed to users under +periods of extreme server overload. The increased error rate is a tradeoff: given the choice between an overloaded +server (potential crash), or at minimum dramatically slower query execution time and a stable but lowered throughput +with higher error rate as the server load sheds, we have chosen the latter. + +The changes in this specification help smooth out the impact of the server's rate limiting on users by reducing the +number of errors users see during spikes or burst workloads and help prevent retry storms by spacing out retries. +However, older drivers do not have this benefit. Drivers MUST document that: + +- Users SHOULD upgrade to driver versions that officially support backpressure to avoid any impacts of server changes. +- Users who do not upgrade might need to update application error handling to handle higher rates of overload errors. + +## Test Plan + +See the [README](./tests/README.md) for tests. + +## Motivation for Change + +New load shedding mechanisms are being introduced to the server that improve its ability to remain available under +extreme load, however clients do not know how to handle the errors returned when one of its requests has been rejected. +As a result, such overload errors would currently either be propagated back to applications, increasing +externally-visible command failure rates, or be retried immediately, increasing the load on already overburdened +servers. To minimize these effects, this specification enables clients to retry requests that have been load shed in a +way that does not overburden already overloaded servers. This retry policy allows for more aggressive and effective load +shedding policies to be deployed in the future. This will also help unify the currently-divergent retry policy between +drivers and the server (mongos). + +## Reference Implementation + +The Node and Python drivers will provide the reference implementations. See +[NODE-7142](https://jira.mongodb.org/browse/NODE-7142) and [PYTHON-5528](https://jira.mongodb.org/browse/PYTHON-5528). + +## Future work + +1. [DRIVERS-3333](https://jira.mongodb.org/browse/DRIVERS-3333) Add a backoff state into the connection pool. +2. [DRIVERS-3241](https://jira.mongodb.org/browse/DRIVERS-3241) Add diagnostic metadata to retried commands. +3. [DRIVERS-3352](https://jira.mongodb.org/browse/DRIVERS-3352) Add support for RetryableError labels to retryable reads + and writes. + +## Q&A + +### Why are drivers not required to work around timing limitations in their language's sleep() APIs? + +The client backpressure retry loop is primarily concerned with spreading out retries to avoid retry storms. The exact +sleep duration is not critical to the intended behavior, so long as we sleep at least as long as we say we will. + +### Why override existing maximum number of retry attempt defaults for retryable reads and writes if an overload error is received at any point during an operation's retry loop? + +Load-shedded errors indicate that the request was rejected by the server to minimize load, not that the command failed +for logical reasons. So, when determining the number of retries an operation should attempt: + +- Any load-shedded errors should be retried to give them a real attempt at success +- If the command ultimately would have failed if it had not been load shed by the server, returning an actionable error + message is preferable to a generic overload error. + +The maximum retry attempt logic in this specification balances retry policies described in the +[retryable reads](../retryable-reads/retryable-reads.md) and [retryable writes](../retryable-writes/retryable-writes.md) +specifications with load-shedding behavior: + +- Relying on either 1 or infinite retries (depending on whether CSOT enabled or not) preserves retry behaviors defined + in the [retryable reads](../retryable-reads/retryable-reads.md), + [retryable writes](../retryable-writes/retryable-writes.md) and + [CSOT](../client-side-operations-timeout/client-side-operations-timeout.md) specifications when no overload errors are + encountered. +- Adjusting the maximum number of retry attempts to MAX_RETRIES if an overload error is returned from the server gives + requests more opportunities to succeed and helps reduce application errors. +- An alternative approach would be to retry once if we don't receive an overload error, in which case we'd retry 5 + times. The approach chosen allows for additional retries in scenarios where a non-overload error fails on a retry + with an overload error. + +### Why not apply the overload retry policy to monitoring and RTT connections? + +The ingress request rate limiter only applies to authenticated connections. Neither the +[monitoring connection](../server-discovery-and-monitoring/server-monitoring.md#monitoring) nor the +[RTT pinger](../server-discovery-and-monitoring/server-monitoring.md#measuring-rtt) use authentication, and consequently +will not encounter ingress operation rate limiter errors. + +It is conceivable that a driver attempting to establish a monitoring connection or RTT connection could encounter the +ingress connection rate limiter. However, in these scenarios, the driver already behaves in an appropriate manner. + +If an error is encountered, both the RTT connections and monitoring connections already retry. + +- The RTT pinger retries indefinitely until the monitor is reset. +- Monitoring failures will mark the server unknown, which will reset the monitor, triggering another monitoring request. + +Under most circumstances, both monitoring and RTT connections wait at least `minHeartbeatFrequencyMS` between `hello` +commands, ensuring delays between retries. The notable exception is monitoring connections retrying network errors +without waiting for `minHeartbeatFrequencyMS`, which is acceptable since re-establishing monitoring is the driver's top +priority when a monitoring connection disconnects. + +### Why not apply the overload policy to authentication commands or reauthentication commands? + +The ingress request rate limiter only applies to authenticated connections. The server does not consider a connection to +be authenticated until after the authentication workflow has completed and during reauthentication a connection is not +considered authenticated by the server. So, authentication and reauthentication commands will not hit the ingress +operation rate limiter. + +### Why must both `retryWrites` and `retryReads` be enabled to retry runCommand? + +[`runCommand`](../run-command/run-command.md) is not retryable under the +[retryable reads](../retryable-reads/retryable-reads.md) and [retryable writes](../retryable-writes/retryable-writes.md) +specifications and consequently it was not historically classified as a read or write command. + +The most flexible approach would be to inspect the user's command and determine if it is a read or a write. However, +this is problematic for two reasons: + +- The runCommand specification specifically forbids drivers from inspecting the user's command. +- `runCommand` is commonly used to execute commands of which the driver has no knowledge and therefore cannot determine + whether it is a read or write. + +Another option is to always consider `runCommand` retryable under the overload retry policy, regardless of the setting +of [`retryReads`](../retryable-reads/retryable-reads.md#retryreads) and +[`retryWrites`](../retryable-writes/retryable-writes.md#retrywrites). However, this behavior goes against a user's +expectations: if a user disables both options, they would expect no commands to be retried. + +Retrying `runCommand` only when both `retryReads` and `retryWrites` are enabled is a safe default that does not have the +pitfalls of either approach outlined by above: + +- This approach does not require drivers to inspect a user's command document. +- This approach will not retry commands if a user has disabled both `retryReads` and `retryWrites`. + +Additionally, both `retryReads` and `retryWrites` are enabled by default, so for most users `runCommand` will be +retried. This approach also prevents accidentally retrying a read command when only `retryWrites` is enabled, or +retrying a write command when only `retryReads` is enabled. + +### Why make `maxAdaptiveRetries` configurable? + +Modelling and the underpinning theory for backpressure shows that the n-retries approach (retry up to N times on +overload errors without a token bucket) can introduce retry storms as overload increases. However, the specifics of the +workload and cluster serving that workload significantly impacts the threshold at which retry volume becomes an +additional burden rather than a throughput improvement. Some applications and clusters may be very tolerant of many +additional retries, while others may want to break out of the loop much earlier. + +The selection of 2 as a default attempts to broadly pick a sensible default for most users that will on average be a +benefit rather than a negative during overload. However, savvy users, the users expected to be most affected by overload +and have the most insight into the specifics of their workload and cluster, will likely find that tweaking this value on +a per-workload basis produces better results. Additionally, there are situations where disabling overload retries +entirely is optimal, such as non-critical workloads against a cluster shared with critical workloads. Without a knob, +those situations will cause users to either have a strictly worse experience with a new driver, or force them to +downgrade to an older driver to avoid the issue. These are two strong motivations to add a knob for +`maxAdaptiveRetries`. + +### Why make `enableOverloadRetargeting` configurable? + +The current contract we've made with users utilizing `primaryPreferred` is that reads will only go to a secondary if the +primary is unavailable. The documentation does not explicitly define unavailable, but in practice that means the primary +is unselectable. Overload retargeting makes the primary unselectable for a retry operation if it returned an overload +error on a previous attempt. This materially changes how often secondary reads occur. Since secondary reads can result +in stale data, enabling overload retargeting increases the chance that users of `primaryPreferred` will get stale data +when they did not previously. This is a potentially significant change in expected behavior. Therefore, overload +retargeting is disabled by default with a knob to enable it. + +Overload retargeting significantly increases availability during overload, but it does increase the risk of getting +stale data when used with `primaryPreferred`. Users of `primaryPreferred` may widely end up preferring that behavior. If +that is the case, overload retargeting may be enabled by default in the future. + +`secondaryPreferred` does not have this same staleness issue, but it still materially changes what the preference means +from "almost always secondary" to "sometimes primary". + +Note that for sharded clusters, drivers always attempt to retarget across `mongos` instances on all retryable errors, +including overload errors, regardless of how `enableOverloadRetargeting` is set. `mongos` has a separate flag to +retarget overload errors within shards that is independent of the driver's configuration. + +Alternative design considered: Overload retargeting could have been implemented as a read preference option rather than +a client-level option. This would allow more granular control: enabling retargeting for  specific operations, databases, +or collections. However, a read preference option would require server changes to recognize the new field, and would add +another dimension to read preference selection that users need to reason about. A client-level binary setting is simpler +to understand and configure. + +## Changelog + +- 2026-04-14: Clarify correct retry behavior when a mix of overload and non-overload errors are encountered. + +- 2026-03-30: Introduce phase 1 support without token buckets. + +- 2026-02-20: Disable token buckets by default. + +- 2026-01-09: Initial version. diff --git a/source/client-backpressure/tests/README.md b/source/client-backpressure/tests/README.md new file mode 100644 index 0000000000..17becefd0a --- /dev/null +++ b/source/client-backpressure/tests/README.md @@ -0,0 +1,121 @@ +# Client Backpressure Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +retryable reads. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +### Prose Tests + +#### Test 1: Operation Retry Uses Exponential Backoff + +Drivers should test that retries do not occur immediately when a SystemOverloadedError is encountered. This test MUST be +executed against a MongoDB 4.4+ server that has enabled the `configureFailPoint` command with the `errorLabels` option. + +1. Let `client` be a `MongoClient` +2. Let `collection` be a collection +3. Now, run transactions without backoff: + 1. Configure the random number generator used for jitter to always return `0` -- this effectively disables backoff. + + 2. Configure the following failPoint: + + ```javascript + { + configureFailPoint: 'failCommand', + mode: 'alwaysOn', + data: { + failCommands: ['insert'], + errorCode: 2, + errorLabels: ['SystemOverloadedError', 'RetryableError'] + } + } + ``` + + 3. Insert the document `{ a: 1 }`. Expect that the command errors. Measure the duration of the command execution. + + ```javascript + const start = performance.now(); + expect( + await coll.insertOne({ a: 1 }).catch(e => e) + ).to.be.an.instanceof(MongoServerError); + const end = performance.now(); + ``` + + 4. Configure the random number generator used for jitter to always return a number as close as possible to `1`. + + 5. Execute step 3 again. + + 6. Compare the time between the two runs. + + ```python + assertTrue(absolute_value(with_backoff_time - (no_backoff_time + 0.3 seconds)) < 0.3 seconds) + ``` + + The sum of 2 backoffs is 0.3 seconds. There is a 0.3-second window to account for potential variance between the + two runs. + +#### Test 2: REMOVED + +#### Test 3: Overload Errors are Retried a Maximum of MAX_RETRIES times + +Drivers should test that overload errors are retried a maximum of MAX_RETRIES times. This test MUST be executed against +a MongoDB 4.4+ server that has enabled the `configureFailPoint` command with the `errorLabels` option. + +1. Let `client` be a `MongoClient` with command event monitoring enabled. + +2. Let `coll` be a collection. + +3. Configure the following failpoint: + + ```javascript + { + configureFailPoint: 'failCommand', + mode: 'alwaysOn', + data: { + failCommands: ['find'], + errorCode: 462, // IngressRequestRateLimitExceeded + errorLabels: ['SystemOverloadedError', 'RetryableError'] + } + } + ``` + +4. Perform a find operation with `coll` that fails. + +5. Assert that the raised error contains both the `RetryableError` and `SystemOverloadedError` error labels. + +6. Assert that the total number of started commands is MAX_RETRIES + 1 (3). + +#### Test 4: Overload Errors are Retried a Maximum of maxAdaptiveRetries times when configured + +Drivers should test that overload errors are retried a maximum of `maxAdaptiveRetries` times, when configured. This test +MUST be executed against a MongoDB 4.4+ server that has enabled the `configureFailPoint` command with the `errorLabels` +option. + +1. Let `client` be a `MongoClient` with `maxAdaptiveRetries=1` and command event monitoring enabled. + +2. Let `coll` be a collection. + +3. Configure the following failpoint: + + ```javascript + { + configureFailPoint: 'failCommand', + mode: 'alwaysOn', + data: { + failCommands: ['find'], + errorCode: 462, // IngressRequestRateLimitExceeded + errorLabels: ['SystemOverloadedError', 'RetryableError'] + } + } + ``` + +4. Perform a find operation with `coll` that fails. + +5. Assert that the raised error contains both the `RetryableError` and `SystemOverloadedError` error labels. + +6. Assert that the total number of started commands is `maxAdaptiveRetries` + 1 (2). diff --git a/source/client-backpressure/tests/backpressure-connection-checkin.json b/source/client-backpressure/tests/backpressure-connection-checkin.json new file mode 100644 index 0000000000..794951ad5f --- /dev/null +++ b/source/client-backpressure/tests/backpressure-connection-checkin.json @@ -0,0 +1,111 @@ +{ + "description": "tests that connections are returned to the pool on retry attempts for overload errors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + { + "client": { + "id": "fail_point_client", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "backpressure-connection-checkin" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "tests": [ + { + "description": "overload error retry attempts return connections to the pool", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/client-backpressure/tests/backpressure-connection-checkin.yml b/source/client-backpressure/tests/backpressure-connection-checkin.yml new file mode 100644 index 0000000000..ce8c1acb36 --- /dev/null +++ b/source/client-backpressure/tests/backpressure-connection-checkin.yml @@ -0,0 +1,60 @@ +description: tests that connections are returned to the pool on retry attempts for overload errors +schemaVersion: "1.3" +runOnRequirements: + - minServerVersion: "4.4" + topologies: + - replicaset + - sharded + - load-balanced +createEntities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - connectionCheckedOutEvent + - connectionCheckedInEvent + - client: + id: fail_point_client + useMultipleMongoses: false + - database: + id: database + client: client + databaseName: backpressure-connection-checkin + - collection: + id: collection + database: database + collectionName: coll +tests: + - description: overload error retry attempts return connections to the pool + operations: + - name: failPoint + object: testRunner + arguments: + client: fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - find + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 2 + - name: find + object: collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + expectEvents: + - client: client + eventType: cmap + events: + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} diff --git a/source/client-backpressure/tests/backpressure-retry-loop.json b/source/client-backpressure/tests/backpressure-retry-loop.json new file mode 100644 index 0000000000..a0b4877fac --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-loop.json @@ -0,0 +1,4553 @@ +{ + "description": "tests that operations respect overload backoff retry loop", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "client": { + "id": "internal_client", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "internal_db", + "client": "internal_client", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "retryable-writes-tests", + "database": "internal_db", + "collectionName": "coll" + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "client": { + "id": "client_retryReads_false", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "uriOptions": { + "retryReads": false + } + } + }, + { + "database": { + "id": "database_retryReads_false", + "client": "client_retryReads_false", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection_retryReads_false", + "database": "database_retryReads_false", + "collectionName": "coll" + } + }, + { + "client": { + "id": "client_retryWrites_false", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ], + "uriOptions": { + "retryWrites": false + } + } + }, + { + "database": { + "id": "database_retryWrites_false", + "client": "client_retryWrites_false", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection_retryWrites_false", + "database": "database_retryWrites_false", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "_yamlAnchors": { + "bulWriteInsertNamespace": "retryable-writes-tests.coll" + }, + "tests": [ + { + "description": "client.listDatabases retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabases (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabases", + "object": "client_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client_retryReads_false", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client_retryReads_false", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "client.clientBulkWrite retries using operation loop", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "client.clientBulkWrite (write) does not retry if retryWrites=false", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client_retryWrites_false", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "database.aggregate read retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.aggregate (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "database_retryReads_false", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.listCollections retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollections (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollections", + "object": "database_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.runCommand retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandSucceededEvent": { + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "database.runCommand (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "runCommand", + "object": "database_retryReads_false", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "database.runCommand (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "runCommand", + "object": "database_retryWrites_false", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database_retryReads_false", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate read retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection_retryReads_false", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandSucceededEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection_retryReads_false", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.distinct retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandSucceededEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.distinct (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "distinct", + "object": "collection_retryReads_false", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.find retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.find (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "object": "collection_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOne", + "object": "collection_retryReads_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection_retryReads_false", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection_retryReads_false", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream (read) does not retry if retryReads=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection_retryReads_false", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryReads_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.insertOne retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertOne (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertOne", + "object": "collection_retryWrites_false", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertMany", + "object": "collection_retryWrites_false", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteMany retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandSucceededEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteMany (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateOne", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateMany retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateMany (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateMany", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandSucceededEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection_retryWrites_false", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection_retryWrites_false", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.createIndex retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 11 + }, + "name": "x_11" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.createIndex (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createIndex", + "object": "collection_retryWrites_false", + "arguments": { + "keys": { + "x": 11 + }, + "name": "x_11" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndex retries using operation loop", + "operations": [ + { + "name": "createIndex", + "object": "retryable-writes-tests", + "arguments": { + "keys": { + "x": 11 + }, + "name": "x_11" + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_11" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndex (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "createIndex", + "object": "retryable-writes-tests", + "arguments": { + "keys": { + "x": 11 + }, + "name": "x_11" + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndex", + "object": "collection_retryWrites_false", + "arguments": { + "name": "x_11" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndexes retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndexes (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection_retryWrites_false", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate write retries using operation loop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$out": "output" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate (write) does not retry if retryWrites=false", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "internal_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection_retryWrites_false", + "arguments": { + "pipeline": [ + { + "$out": "output" + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client_retryWrites_false", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-backpressure/tests/backpressure-retry-loop.yml b/source/client-backpressure/tests/backpressure-retry-loop.yml new file mode 100644 index 0000000000..902726f626 --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-loop.yml @@ -0,0 +1,2282 @@ +# Tests in this file are generated from backpressure-retry-loop.yml.template. + +description: tests that operations respect overload backoff retry loop + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.4' # failCommand + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + + - client: + id: &internal_client internal_client + useMultipleMongoses: false + + - database: + id: &internal_db internal_db + client: *internal_client + databaseName: &database_name retryable-writes-tests + + - collection: + id: &internal_collection retryable-writes-tests + database: *internal_db + collectionName: &collection_name coll + + - database: + id: &database database + client: *client + databaseName: *database_name + + - collection: + id: &collection collection + database: *database + collectionName: *collection_name + + - client: + id: &client_retryReads_false client_retryReads_false + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + uriOptions: + retryReads: false + + - database: + id: &database_retryReads_false database_retryReads_false + client: *client_retryReads_false + databaseName: *database_name + + - collection: + id: &collection_retryReads_false collection_retryReads_false + database: *database_retryReads_false + collectionName: *collection_name + + - client: + id: &client_retryWrites_false client_retryWrites_false + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + uriOptions: + retryWrites: false + + - database: + id: &database_retryWrites_false database_retryWrites_false + client: *client_retryWrites_false + databaseName: *database_name + + - collection: + id: &collection_retryWrites_false collection_retryWrites_false + database: *database_retryWrites_false + collectionName: *collection_name + +initialData: +- collectionName: *collection_name + databaseName: *database_name + documents: [] + +_yamlAnchors: + bulWriteInsertNamespace: &client_bulk_write_ns retryable-writes-tests.coll + +tests: + - description: 'client.listDatabases retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabases + object: *client + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + - description: 'client.listDatabases (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabases + object: *client_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + + + - description: 'client.listDatabaseNames retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabaseNames + object: *client + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + - description: 'client.listDatabaseNames (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabaseNames + object: *client_retryReads_false + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + + + - description: 'client.createChangeStream retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'client.createChangeStream (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *client_retryReads_false + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'client.clientBulkWrite retries using operation loop' + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [bulkWrite] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: *client_bulk_write_ns + document: { _id: 8, x: 88 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + - commandStartedEvent: + commandName: bulkWrite + - commandSucceededEvent: + commandName: bulkWrite + - description: 'client.clientBulkWrite (write) does not retry if retryWrites=false' + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [bulkWrite] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: clientBulkWrite + object: *client_retryWrites_false + arguments: + models: + - insertOne: + namespace: *client_bulk_write_ns + document: { _id: 8, x: 88 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + + + - description: 'database.aggregate read retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'database.aggregate (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *database_retryReads_false + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'database.listCollections retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollections + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + - description: 'database.listCollections (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollections + object: *database_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + + + - description: 'database.listCollectionNames retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + - description: 'database.listCollectionNames (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollectionNames + object: *database_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + + + - description: 'database.runCommand retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ping] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: ping + - commandSucceededEvent: + commandName: ping + - description: 'database.runCommand (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ping] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: runCommand + object: *database_retryReads_false + arguments: + command: { ping: 1 } + commandName: ping + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + - description: 'database.runCommand (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ping] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: runCommand + object: *database_retryWrites_false + arguments: + command: { ping: 1 } + commandName: ping + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + + + - description: 'database.createChangeStream retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'database.createChangeStream (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *database_retryReads_false + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'collection.aggregate read retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'collection.aggregate (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection_retryReads_false + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'collection.countDocuments retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'collection.countDocuments (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: countDocuments + object: *collection_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'collection.estimatedDocumentCount retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [count] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: estimatedDocumentCount + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + - commandStartedEvent: + commandName: count + - commandSucceededEvent: + commandName: count + - description: 'collection.estimatedDocumentCount (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [count] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: estimatedDocumentCount + object: *collection_retryReads_false + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + + + - description: 'collection.distinct retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [distinct] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + - commandStartedEvent: + commandName: distinct + - commandSucceededEvent: + commandName: distinct + - description: 'collection.distinct (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [distinct] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: distinct + object: *collection_retryReads_false + arguments: + fieldName: x + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + + + - description: 'collection.find retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: find + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - description: 'collection.find (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: find + object: *collection_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + + + - description: 'collection.findOne retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - description: 'collection.findOne (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOne + object: *collection_retryReads_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + + + - description: 'collection.listIndexes retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexes + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + - description: 'collection.listIndexes (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexes + object: *collection_retryReads_false + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + + + - description: 'collection.listIndexNames retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexNames + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + - description: 'collection.listIndexNames (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexNames + object: *collection_retryReads_false + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + + + - description: 'collection.createChangeStream retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'collection.createChangeStream (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *collection_retryReads_false + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + + - description: 'collection.insertOne retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertOne + object: *collection + arguments: + document: { _id: 2, x: 22 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - description: 'collection.insertOne (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertOne + object: *collection_retryWrites_false + arguments: + document: { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + + - description: 'collection.insertMany retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - description: 'collection.insertMany (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertMany + object: *collection_retryWrites_false + arguments: + documents: + - { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + + - description: 'collection.deleteOne retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete + - description: 'collection.deleteOne (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteOne + object: *collection_retryWrites_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + + + - description: 'collection.deleteMany retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteMany + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandSucceededEvent: + commandName: delete + - description: 'collection.deleteMany (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteMany + object: *collection_retryWrites_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + + + - description: 'collection.replaceOne retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + - description: 'collection.replaceOne (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: replaceOne + object: *collection_retryWrites_false + arguments: + filter: {} + replacement: { x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + + - description: 'collection.updateOne retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + - description: 'collection.updateOne (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateOne + object: *collection_retryWrites_false + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + + - description: 'collection.updateMany retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + - description: 'collection.updateMany (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateMany + object: *collection_retryWrites_false + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + + - description: 'collection.findOneAndDelete retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + - description: 'collection.findOneAndDelete (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndDelete + object: *collection_retryWrites_false + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + + - description: 'collection.findOneAndReplace retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + - description: 'collection.findOneAndReplace (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndReplace + object: *collection_retryWrites_false + arguments: + filter: {} + replacement: { x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + + - description: 'collection.findOneAndUpdate retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandSucceededEvent: + commandName: findAndModify + - description: 'collection.findOneAndUpdate (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndUpdate + object: *collection_retryWrites_false + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + + - description: 'collection.bulkWrite retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - description: 'collection.bulkWrite (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: bulkWrite + object: *collection_retryWrites_false + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + + - description: 'collection.createIndex retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [createIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createIndex + object: *collection + arguments: + keys: { x: 11 } + name: "x_11" + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + - commandStartedEvent: + commandName: createIndexes + - commandSucceededEvent: + commandName: createIndexes + - description: 'collection.createIndex (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [createIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createIndex + object: *collection_retryWrites_false + arguments: + keys: { x: 11 } + name: "x_11" + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + + + - description: 'collection.dropIndex retries using operation loop' + operations: + - name: createIndex + object: *internal_collection + arguments: + keys: { x: 11 } + name: "x_11" + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndex + object: *collection + arguments: + name: "x_11" + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandSucceededEvent: + commandName: dropIndexes + - description: 'collection.dropIndex (write) does not retry if retryWrites=false' + operations: + - name: createIndex + object: *internal_collection + arguments: + keys: { x: 11 } + name: "x_11" + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndex + object: *collection_retryWrites_false + arguments: + name: "x_11" + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + + + - description: 'collection.dropIndexes retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndexes + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandSucceededEvent: + commandName: dropIndexes + - description: 'collection.dropIndexes (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndexes + object: *collection_retryWrites_false + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + + + - description: 'collection.aggregate write retries using operation loop' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection + arguments: + pipeline: [{$out: "output"}] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - description: 'collection.aggregate (write) does not retry if retryWrites=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection_retryWrites_false + arguments: + pipeline: [{$out: "output"}] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + diff --git a/source/client-backpressure/tests/backpressure-retry-loop.yml.template b/source/client-backpressure/tests/backpressure-retry-loop.yml.template new file mode 100644 index 0000000000..1825b2b20d --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-loop.yml.template @@ -0,0 +1,216 @@ +# Tests in this file are generated from backpressure-retry-loop.yml.template. + +description: tests that operations respect overload backoff retry loop + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.4' # failCommand + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + + - client: + id: &internal_client internal_client + useMultipleMongoses: false + + - database: + id: &internal_db internal_db + client: *internal_client + databaseName: &database_name retryable-writes-tests + + - collection: + id: &internal_collection retryable-writes-tests + database: *internal_db + collectionName: &collection_name coll + + - database: + id: &database database + client: *client + databaseName: *database_name + + - collection: + id: &collection collection + database: *database + collectionName: *collection_name + + - client: + id: &client_retryReads_false client_retryReads_false + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + uriOptions: + retryReads: false + + - database: + id: &database_retryReads_false database_retryReads_false + client: *client_retryReads_false + databaseName: *database_name + + - collection: + id: &collection_retryReads_false collection_retryReads_false + database: *database_retryReads_false + collectionName: *collection_name + + - client: + id: &client_retryWrites_false client_retryWrites_false + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + uriOptions: + retryWrites: false + + - database: + id: &database_retryWrites_false database_retryWrites_false + client: *client_retryWrites_false + databaseName: *database_name + + - collection: + id: &collection_retryWrites_false collection_retryWrites_false + database: *database_retryWrites_false + collectionName: *collection_name + +initialData: +- collectionName: *collection_name + databaseName: *database_name + documents: [] + +_yamlAnchors: + bulWriteInsertNamespace: &client_bulk_write_ns retryable-writes-tests.coll + +tests: {% for operation in operations %} + - description: '{{operation.object}}.{{operation.operation_name}}{{ " " + operation.operation_type + "" if operation.operation_name == "aggregate" else "" }} retries using operation loop' {%- if ((operation.operation_name == 'clientBulkWrite')) %} + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + {%- endif %} + operations: {%- if operation.operation_name == "dropIndex" %} + - name: createIndex + object: *internal_collection + arguments: + keys: { x: 11 } + name: "x_11" + {%- endif %} + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [{{operation.command_name}}] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} + + {%- if operation.operation_type == 'read' or operation.operation_name == "runCommand" %} + - description: '{{operation.object}}.{{operation.operation_name}} (read) does not retry if retryReads=false' + operations: + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [{{operation.command_name}}] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: {{operation.operation_name}} + object: *{{operation.object}}_retryReads_false + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryReads_false + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + {%- endif %} + + {%- if operation.operation_type == 'write' or operation.operation_name == "runCommand" %} + - description: '{{operation.object}}.{{operation.operation_name}} (write) does not retry if retryWrites=false' {%- if ((operation.operation_name == 'clientBulkWrite')) %} + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + {%- endif %} + operations: {%- if operation.operation_name == "dropIndex" %} + - name: createIndex + object: *internal_collection + arguments: + keys: { x: 11 } + name: "x_11" + {%- endif %} + - name: failPoint + object: testRunner + arguments: + client: *internal_client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [{{operation.command_name}}] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: {{operation.operation_name}} + object: *{{operation.object}}_retryWrites_false + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client_retryWrites_false + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + {%- endif %} + +{% endfor -%} diff --git a/source/client-backpressure/tests/backpressure-retry-max-attempts.json b/source/client-backpressure/tests/backpressure-retry-max-attempts.json new file mode 100644 index 0000000000..de52572765 --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-max-attempts.json @@ -0,0 +1,2569 @@ +{ + "description": "tests that operations retry at most maxAttempts=2 times", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "client": { + "id": "fail_point_client", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "_yamlAnchors": { + "bulkWriteInsertNamespace": "retryable-writes-tests.coll" + }, + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "client.listDatabases retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listDatabases" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandFailedEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "client.clientBulkWrite retries at most maxAttempts=2 times", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite" + } + }, + { + "commandFailedEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "database.aggregate read retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.listCollections retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listCollections" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandFailedEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.runCommand retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "ping" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate read retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "count" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandFailedEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.distinct retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "distinct" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandFailedEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.find retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "listIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.insertOne retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.insertMany retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.deleteOne retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.deleteMany retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + }, + { + "commandStartedEvent": { + "commandName": "delete" + } + }, + { + "commandFailedEvent": { + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "collection.replaceOne retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateOne retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.updateMany retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndDelete retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndReplace retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.findOneAndUpdate retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify" + } + }, + { + "commandFailedEvent": { + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "collection.bulkWrite retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "collection.createIndex retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "createIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 11 + }, + "name": "x_11" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "createIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndex retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_11" + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.dropIndexes retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "dropIndexes" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes" + } + }, + { + "commandFailedEvent": { + "commandName": "dropIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate write retries at most maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "fail_point_client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "aggregate" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$out": "output" + } + ] + }, + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandFailedEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-backpressure/tests/backpressure-retry-max-attempts.yml b/source/client-backpressure/tests/backpressure-retry-max-attempts.yml new file mode 100644 index 0000000000..eec89671fa --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-max-attempts.yml @@ -0,0 +1,1368 @@ +# Tests in this file are generated from backpressure-retry-max-attempts.yml.template. + +description: tests that operations retry at most maxAttempts=2 times + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.4' # failCommand + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + + - client: + id: &fail_point_client fail_point_client + useMultipleMongoses: false + + - database: + id: &database database + client: *client + databaseName: &database_name retryable-writes-tests + + - collection: + id: &collection collection + database: *database + collectionName: &collection_name coll + +_yamlAnchors: + bulkWriteInsertNamespace: &client_bulk_write_ns retryable-writes-tests.coll + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - description: 'client.listDatabases retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabases + object: *client + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + + - description: 'client.listDatabaseNames retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listDatabases] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listDatabaseNames + object: *client + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + - commandStartedEvent: + commandName: listDatabases + - commandFailedEvent: + commandName: listDatabases + + - description: 'client.createChangeStream retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *client + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'client.clientBulkWrite retries at most maxAttempts=2 times' + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [bulkWrite] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: *client_bulk_write_ns + document: { _id: 8, x: 88 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + - commandStartedEvent: + commandName: bulkWrite + - commandFailedEvent: + commandName: bulkWrite + + - description: 'database.aggregate read retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'database.listCollections retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollections + object: *database + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + + - description: 'database.listCollectionNames retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listCollections] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listCollectionNames + object: *database + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + - commandStartedEvent: + commandName: listCollections + - commandFailedEvent: + commandName: listCollections + + - description: 'database.runCommand retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [ping] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: ping + - commandFailedEvent: + commandName: ping + + - description: 'database.createChangeStream retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *database + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'collection.aggregate read retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'collection.countDocuments retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: countDocuments + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'collection.estimatedDocumentCount retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [count] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: estimatedDocumentCount + object: *collection + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + - commandStartedEvent: + commandName: count + - commandFailedEvent: + commandName: count + + - description: 'collection.distinct retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [distinct] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + - commandStartedEvent: + commandName: distinct + - commandFailedEvent: + commandName: distinct + + - description: 'collection.find retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: find + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + + - description: 'collection.findOne retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [find] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOne + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + + - description: 'collection.listIndexes retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexes + object: *collection + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + + - description: 'collection.listIndexNames retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [listIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: listIndexNames + object: *collection + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + - commandStartedEvent: + commandName: listIndexes + - commandFailedEvent: + commandName: listIndexes + + - description: 'collection.createChangeStream retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + + - description: 'collection.insertOne retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertOne + object: *collection + arguments: + document: { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + - description: 'collection.insertMany retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: insertMany + object: *collection + arguments: + documents: + - { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + - description: 'collection.deleteOne retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteOne + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + + - description: 'collection.deleteMany retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [delete] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: deleteMany + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + - commandStartedEvent: + commandName: delete + - commandFailedEvent: + commandName: delete + + - description: 'collection.replaceOne retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + - description: 'collection.updateOne retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + - description: 'collection.updateMany retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [update] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + + - description: 'collection.findOneAndDelete retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + - description: 'collection.findOneAndReplace retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + - description: 'collection.findOneAndUpdate retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [findAndModify] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 22 } } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + - commandStartedEvent: + commandName: findAndModify + - commandFailedEvent: + commandName: findAndModify + + - description: 'collection.bulkWrite retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [insert] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 2, x: 22 } + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + + - description: 'collection.createIndex retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [createIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: createIndex + object: *collection + arguments: + keys: { x: 11 } + name: "x_11" + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + - commandStartedEvent: + commandName: createIndexes + - commandFailedEvent: + commandName: createIndexes + + - description: 'collection.dropIndex retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndex + object: *collection + arguments: + name: "x_11" + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + + - description: 'collection.dropIndexes retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [dropIndexes] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: dropIndexes + object: *collection + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + - commandStartedEvent: + commandName: dropIndexes + - commandFailedEvent: + commandName: dropIndexes + + - description: 'collection.aggregate write retries at most maxAttempts=2 times' + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [aggregate] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: aggregate + object: *collection + arguments: + pipeline: [{$out: "output"}] + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate + - commandStartedEvent: + commandName: aggregate + - commandFailedEvent: + commandName: aggregate diff --git a/source/client-backpressure/tests/backpressure-retry-max-attempts.yml.template b/source/client-backpressure/tests/backpressure-retry-max-attempts.yml.template new file mode 100644 index 0000000000..ffbd08de9c --- /dev/null +++ b/source/client-backpressure/tests/backpressure-retry-max-attempts.yml.template @@ -0,0 +1,90 @@ +# Tests in this file are generated from backpressure-retry-max-attempts.yml.template. + +description: tests that operations retry at most maxAttempts=2 times + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.4' # failCommand + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent, commandFailedEvent] + ignoreCommandMonitoringEvents: [killCursors] + + - client: + id: &fail_point_client fail_point_client + useMultipleMongoses: false + + - database: + id: &database database + client: *client + databaseName: &database_name retryable-writes-tests + + - collection: + id: &collection collection + database: *database + collectionName: &collection_name coll + +_yamlAnchors: + bulkWriteInsertNamespace: &client_bulk_write_ns retryable-writes-tests.coll + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: {% for operation in operations %} + - description: '{{operation.object}}.{{operation.operation_name}}{{ " " + operation.operation_type + "" if operation.operation_name == "aggregate" else "" }} retries at most maxAttempts=2 times' + {%- if ((operation.operation_name == 'clientBulkWrite')) %} + runOnRequirements: + - minServerVersion: '8.0' # client bulk write added to server in 8.0 + {%- endif %} + operations: + - name: failPoint + object: testRunner + arguments: + client: *fail_point_client + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [{{operation.command_name}}] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: {{operation.operation_name}} + object: *{{operation.object}} {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + # we expect 3 pairs of command started and succeeded events: + # 1 initial attempt and 2 retries. + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandFailedEvent: + commandName: {{operation.command_name}} +{% endfor -%} diff --git a/source/client-backpressure/tests/getMore-retried.json b/source/client-backpressure/tests/getMore-retried.json new file mode 100644 index 0000000000..d7607d694b --- /dev/null +++ b/source/client-backpressure/tests/getMore-retried.json @@ -0,0 +1,253 @@ +{ + "description": "getMore-retried-backpressure", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent", + "commandSucceededEvent" + ] + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "a": 1 + }, + { + "a": 2 + }, + { + "a": 3 + } + ] + } + ], + "tests": [ + { + "description": "getMores are retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "object": "coll", + "arguments": { + "batchSize": 2, + "filter": {}, + "sort": { + "a": 1 + } + }, + "expectResult": [ + { + "a": 1 + }, + { + "a": 2 + }, + { + "a": 3 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandSucceededEvent": { + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "getMores are retried maxAttempts=2 times", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "getMore" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 2 + } + } + } + }, + { + "name": "find", + "arguments": { + "batchSize": 2, + "filter": {} + }, + "object": "coll", + "expectError": { + "isError": true, + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-backpressure/tests/getMore-retried.yml b/source/client-backpressure/tests/getMore-retried.yml new file mode 100644 index 0000000000..cd8198383a --- /dev/null +++ b/source/client-backpressure/tests/getMore-retried.yml @@ -0,0 +1,131 @@ +description: getMore-retried-backpressure +schemaVersion: "1.3" +runOnRequirements: + - minServerVersion: '4.4' # failCommand +createEntities: + - client: + id: &client client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandFailedEvent + - commandSucceededEvent + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - database: + id: db + client: *client + databaseName: &dbName default + - collection: + id: &collection coll + database: db + collectionName: &collectionName default +initialData: + - databaseName: *dbName + collectionName: *collectionName + documents: + - { a: 1 } + - { a: 2 } + - { a: 3 } + +tests: + - description: "getMores are retried" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [getMore] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: find + object: *collection + arguments: + # batch size of 2 with 3 docs in the collection ensures exactly one find + one getMore exhaust the cursor + batchSize: 2 + filter: {} + # ensure stable ordering of result documents + sort: { a: 1 } + expectResult: + - { a: 1 } + - { a: 2 } + - { a: 3 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + # first attempt + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + # second attempt + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + # success + - commandStartedEvent: + commandName: getMore + - commandSucceededEvent: + commandName: getMore + + - description: "getMores are retried maxAttempts=2 times" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: [getMore] + errorLabels: [RetryableError, SystemOverloadedError] + errorCode: 2 + + - name: find + arguments: + batchSize: 2 + filter: {} + object: *collection + expectError: + isError: true + isClientError: false + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + # first attempt + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + # second attempt + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + # third attempt + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + - commandStartedEvent: + commandName: killCursors + - commandSucceededEvent: + commandName: killCursors diff --git a/source/client-side-encryption/client-side-encryption.md b/source/client-side-encryption/client-side-encryption.md new file mode 100644 index 0000000000..ef40981fc2 --- /dev/null +++ b/source/client-side-encryption/client-side-encryption.md @@ -0,0 +1,2650 @@ +# Client Side Encryption + +- Status: Accepted +- Minimum Server Version: 4.2 (CSFLE), 7.0 (QE) +- Version: 1.14.0 + +______________________________________________________________________ + +## Abstract + +MongoDB 4.2 introduced support for client side encryption, guaranteeing that sensitive data can only be encrypted and +decrypted with access to both MongoDB and a separate key management provider (supporting AWS, Azure, GCP, a local +provider, and KMIP). Once enabled, data can be seamlessly encrypted and decrypted with minimal application code changes. +7.0 introduced the next generation of client side encryption based on a Structured Encryption framework which allows +expressive encrypted search operations. This spec covers both capabilities - 1st generation, "Client Side Field Level +Encryption" and generation 2, "Queryable Encryption" - as the associated core cryptographic library and supporting +drivers share a common codebase. + +### Naming + +The public name of this feature is +[In-Use Encryption](https://www.mongodb.com/docs/manual/core/security-in-use-encryption/) and consists of: + +- Client-Side Field Level Encryption (CSFLE). +- Queryable Encryption (QE). + +Internally, In-Use Encryption is sometimes called Field Level Encryption (FLE). Internally, CSFLE is sometimes called +Client Side Encryption (like this specification). Internally, CSFLE and QE are sometimes called FLE1 and FLE2, +respectively. + +### Server support history + +MongoDB 4.2 added support for CSFLE. This includes `encrypt` in JSON Schema +([SERVER-38900](https://jira.mongodb.org/browse/SERVER-38900)) and [mongocryptd](#mongocryptd) +([SPM-1258](https://jira.mongodb.org/browse/SPM-1258)). + +MongoDB 5.3 added the [crypt_shared](#crypt_shared) library ([SPM-2403](https://jira.mongodb.org/browse/SPM-2403)). + +MongoDB 6.0 added unstable support for QE (QEv1) ([SPM-2463](https://jira.mongodb.org/browse/SPM-2463)). This includes +`queryType=equality`. + +MongoDB 6.2 added unstable support for QE range queries ([SPM-2719](https://jira.mongodb.org/browse/SPM-2719)). This +includes `queryType=rangePreview`. + +MongoDB 7.0 dropped QEv1 and added stable support of QE (QEv2) ([SPM-2972](https://jira.mongodb.org/browse/SPM-2972)). +QEv1 and QEv2 are incompatible. + +MongoDB 8.0 dropped `queryType=rangePreview` and added `queryType=range` +([SPM-3583](https://jira.mongodb.org/browse/SPM-3583)). + +MongoDB 8.2 added unstable support for QE string queries ([SPM-2880](https://jira.mongodb.org/browse/SPM-2880)) + +MongoDB 9.0 dropped `queryType=prefixPreview|suffixPreview` and added `queryType=prefix|suffix` +([SPM-4539](https://jira.mongodb.org/browse/SPM-4539)) + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Terms + +**encrypted MongoClient** + +A MongoClient with client side encryption enabled. + +**data key** + +A key used to encrypt and decrypt BSON values. Data keys are encrypted with a key management service (e.g. AWS KMS) and +stored within a document in the MongoDB key vault collection (see +[Key vault collection schema for data keys](#key-vault-collection-schema-for-data-keys) for a description of the data +key document). Therefore, a client needs access to both MongoDB and the external KMS service to utilize a data key. + +**MongoDB key vault collection** + +A MongoDB collection designated to contain data keys. This can either be co-located with the data-bearing cluster, or in +a separate external MongoDB cluster. + +**Key Management Service (KMS)** + +An external service providing fixed-size encryption/decryption. Only data keys are encrypted and decrypted with KMS. + +**KMS providers** + +A map of KMS providers to credentials. Configured client-side. Example: + +```javascript +kms_providers = { + "aws": { + "accessKeyId": AWS_KEYID, + "secretAccessKey": AWS_SECRET, + }, + "local": { + "key": LOCAL_KEK + }, +} +``` + +**KMS provider** + +A configured KMS. Identified by a key in the KMS providers map. The key has the form `` or +`:`. Examples: `aws` or `aws:myname`. In [libmongocrypt](#libmongocrypt), the key +is referred to as the KMS ID. + +**KMS provider type** + +The type of backing KMS. Identified by the string: `aws`, `azure`, `gcp`, `kmip`, or `local`. + +**KMS provider name** + +An optional name to identify a KMS provider. Enables configuring multiple KMS providers with the same KMS provider type +(e.g. `aws:name1` and `aws:name2` can refer to different AWS accounts). + +**Master Key** + +The underlying key the KMS service uses to encrypt and decrypt. See +[AWS KMS Concepts](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#master_keys) for an AWS-specific +example (other KMS providers work similarly). + +The master key is also sometimes referred to as a Customer Master Key (CMK). + +**schema** + +A MongoDB JSON Schema (either supplied by the server or client-side) which may include metadata about encrypted fields. +This is a JSON Schema based on draft 4 of the JSON Schema specification, +[as documented in the MongoDB manual.](https://www.mongodb.com/docs/manual/reference/operator/query/jsonSchema/). + +**[libmongocrypt](#libmongocrypt)** + +A library, written in C, that coordinates communication, does encryption/decryption, caches key and schemas. +[Located here](https://github.com/mongodb/libmongocrypt). + +**[mongocryptd](#mongocryptd)** + +A local process the driver communicates with to determine how to encrypt values in a command. + +**[crypt_shared](#crypt_shared)** + +This term, spelled in all-lowercase with an underscore, refers to the client-side field-level-encryption dynamic library +provided as part of a MongoDB Enterprise distribution. It replaces [mongocryptd](#mongocryptd) as the method of +[marking-up a database command for encryption](../bson-binary-encrypted/binary-encrypted.md#intent-to-encrypt). + +See also: + +- [Introduction on crypt_shared](#crypt_shared) +- [Enabling crypt_shared](#enabling-crypt_shared) + +**ciphertext** + +One of the data formats of [BSON binary encrypted](../bson-binary-encrypted/binary-encrypted.md), representing an +encoded BSON document containing encrypted ciphertext and metadata. + +**Client-Side Field Level Encryption (CSFLE)** + +CSFLE is the first version of In-Use Encryption. CSFLE is almost entirely client-side with the exception of server-side +JSON schema. + +**Queryable Encryption (QE)** + +Queryable Encryption the second version of In-Use Encryption. Data is encrypted client-side. Queryable Encryption +supports indexed encrypted fields, which are further processed server-side. + +**In-Use Encryption** + +Is an umbrella term describing the both CSFLE and Queryable Encryption. + + **encryptedFields** + +A BSON document describing the Queryable Encryption encrypted fields. This is analogous to the JSON Schema in FLE. The +following is an example encryptedFields in extended canonical JSON: + +```javascript +{ + "escCollection": "enxcol_.CollectionName.esc", + "ecocCollection": "enxcol_.CollectionName.ecoc", + "fields": [ + { + "path": "firstName", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" }}, + "bsonType": "string", + "queries": {"queryType": "equality"} + }, + { + "path": "ssn", + "keyId": { "$binary": { "subType": "04", "base64": "BBBBBBBBBBBBBBBBBBBBBB==" }}, + "bsonType": "string" + } + ] +} +``` + +The acronyms within `encryptedFields` are defined as follows: + +- ECOC: Encrypted Compaction Collection +- ESC: Encrypted State Collection + +## Introduction + +Client side encryption enables users to specify what fields in a collection must be encrypted, and the driver +automatically encrypts commands and decrypts results. Automatic encryption is enterprise only. But users can manually +encrypt and decrypt with a new ClientEncryption object. + +Client side encryption requires MongoDB 4.2 compatible drivers, and is only supported against 4.2 or higher servers. See +[Why is a 4.2 server required?](#why-is-a-42-server-required). + +The following shows basic usage of the new API. + +```python +# The schema map identifies fields on collections that must undergo encryption. + +schema_map = open("./schemas.json", "r").read() + +# AWS KMS is used to decrypt data keys stored in the key vault collection. + +aws_creds = open("./aws_credentials.json", "r").read() + +# A client is configured for automatic encryption and decryption by passing +# AutoEncryptionOpts. Automatic encryption is an enterprise only feature. + +opts = AutoEncryptionOpts( + kms_providers={"aws": aws_creds}, + key_vault_namespace="db.datakeys", + schema_map=schema_map) + +db = MongoClient(auto_encryption_opts=opts).db + +# Commands are encrypted, as determined by the JSON Schema from the schema_map. +db.coll.insert_one({"ssn": "457-55-5462"}) + +# Replies are decrypted. +print(db.coll.find_one()) # { "ssn": "457-55-5462" } but stored and transported as ciphertext. + +# A ClientEncryption object is used for explicit encryption, decryption, and creating data keys. +opts = ClientEncryptionOpts(kms_providers=kms, key_vault_namespace="db.datakeys") +clientencryption = ClientEncryption(client, opts) + +# Use a ClientEncryption to create new data keys. +# The master key identifies the KMS key on AWS KMS to use for encrypting the data key. +master_key = open("./aws_masterkey.json", "r").read() +opts = DataKeyOpts (master_key=master_key) +created_key_id = clientencryption.create_data_key("aws", opts) + +# Use a ClientEncryption to explicitly encrypt and decrypt. +opts = EncryptOpts(key_id=created_key_id, + algorithm="AEAD_AES_256_CBC_HMAC_SHA_512-Random") +encrypted = clientencryption.encrypt("secret text", opts) +# Decryption does not require the key ID or algorithm. The ciphertext indicates the key ID and algorithm used. +decrypted = clientencryption.decrypt(encrypted) +``` + +There are many moving parts to client side encryption with lots of similar sounding terms. Before proceeding to +implement the specification, the following background should provide some context. + +The driver interacts with multiple components to implement client side encryption. + +![image](includes/components.png) + +The driver communicates with… + +- **MongoDB cluster** to get remote JSON Schemas. +- **MongoDB key vault collection** to get encrypted data keys and create new data keys. +- **A KMS Provider** to decrypt fetched data keys and encrypt new data keys. +- **mongocryptd** to ask what values in BSON commands must be encrypted (unless [crypt_shared](#crypt_shared) is in + use). + +The MongoDB key vault may be the same as the MongoDB cluster. Users may choose to have data key stored on a separate +MongoDB cluster, or co-locate with their data. + +### MongoDB Key Vault collection + +The key vault collection is a special MongoDB collection containing key documents. See the appendix section +[Key vault collection schema for data keys](#key-vault-collection-schema-for-data-keys) for a description of the +documents. + +The key material in the key vault collection is encrypted with a separate KMS service. Therefore, encryption and +decryption requires access to a MongoDB cluster and the KMS service. + +### KMS Provider + +A KMS provider (AWS KMS, Azure Key Vault, GCP KMS, the local provider, or KMIP) is used to decrypt data keys after +fetching from the MongoDB Key Vault, and encrypt newly created data keys. Refer to [KMSProviders](#kmsproviders) for the +shape of the KMS provider options. + +### mongocryptd + +`mongocryptd` is a singleton local process needed for auto-encryption when no [crypt_shared](#crypt_shared) library is +used. It speaks the MongoDB wire protocol and the driver uses [mongocryptd](#mongocryptd) by connecting with a +MongoClient. By default, if [crypt_shared](#crypt_shared) is unavailable, the driver should attempt to automatically +spawn [mongocryptd](#mongocryptd). If the MongoClient is configured with `extraOptions.mongocryptdBypassSpawn` set to +`true`, OR `bypassAutoEncryption` is set to `true`, OR `bypassQueryAnalysis` is set to `true` then the driver will not +attempt to spawn [mongocryptd](#mongocryptd). + +The [mongocryptd](#mongocryptd) process is responsible for self terminating after idling for a time period. If +[extraOptions.cryptSharedLibRequired](#extraoptions.cryptsharedlibrequired) is set to `true`, the driver will not +connect to [mongocryptd](#mongocryptd) and instead rely on [crypt_shared](#crypt_shared) being available. + +### crypt_shared + +[crypt_shared](#crypt_shared) is a dynamically-loaded C++ library providing query analysis for auto-encryption. It +replaces [mongocryptd](#mongocryptd) for performing query analysis to - +[mark-up sensitive fields within a command](../bson-binary-encrypted/binary-encrypted.md#intent-to-encrypt). + +Drivers are not required to load and interact with [crypt_shared](#crypt_shared) directly. Instead, they inform +[libmongocrypt](#libmongocrypt) where to find [crypt_shared](#crypt_shared) and [libmongocrypt](#libmongocrypt) will +handle [crypt_shared](#crypt_shared) communication automatically. + +See also: [Enabling crypt_shared](#enabling-crypt_shared) for information on using enabling the +[crypt_shared](#crypt_shared) library. + +### libmongocrypt + +libmongocrypt is a C library providing crypto and coordination with external components. +[Located here](https://github.com/mongodb/libmongocrypt). + +**libmongocrypt is responsible for…** + +- orchestrating an internal state machine. +- asking the driver to perform I/O, then handling the responses. + - includes constructing KMS HTTP requests and parsing KMS responses. +- doing encryption and decryption. +- caching data keys. +- caching results of listCollections. +- creating key material. + +**The driver is responsible for…** + +- performing all I/O needed at every state: + - speaking to [mongocryptd](#mongocryptd) to mark commands (unless [crypt_shared](#crypt_shared) is used). + - fetching encrypted data keys from key vault collection (mongod). + - running listCollections on mongod. + - decrypting encrypted data keys with KMS over TLS. +- doing I/O asynchronously as needed. + +See [Why require including a C library?](#why-require-including-a-c-library) + +## User facing API + +Drivers MUST NOT preclude future options from being added to any of the new interfaces. + +Drivers MAY represent the options types in a way that is idiomatic to the driver or language. E.g. options MAY be a BSON +document or dictionary type. The driver MAY forego validating options and instead defer validation to the underlying +implementation. + +Drivers MAY deviate the spelling of option names to conform to their language's naming conventions and implement options +in an idiomatic way (e.g. keyword arguments, builder classes, etc.). + +Drivers MAY use a native UUID type in place of a parameter or return type specified as a BSON binary with subtype 0x04 +as described in [Handling of Native UUID Types](../bson-binary-uuid/uuid.md). + +### MongoClient Changes + + + +```typescript +class MongoClient { + MongoClient(... autoEncryptionOpts: AutoEncryptionOpts); + + // Implementation details. + private mongocrypt_t libmongocrypt_handle; // Handle to libmongocrypt. + private Optional mongocryptd_client; // Client to mongocryptd. + private MongoClient keyvault_client; // Client used to run find on the key vault collection. This is either an external MongoClient, the parent MongoClient, or internal_client. + private MongoClient metadata_client; // Client used to run listCollections. This is either the parent MongoClient or internal_client. + private Optional internal_client; // An internal MongoClient. Created if no external keyVaultClient was set, or if a metadataClient is needed + + // Exposition-only, used for caching automatic Azure credentials. The cached credentials may be globally cached. + private cachedAzureAccessToken?: AzureAccessToken; + private azureAccessTokenExpireTime?: PointInTime; +} +``` + + + +```typescript +class AutoEncryptionOpts { + keyVaultClient: Optional; + keyVaultNamespace: String; + kmsProviders: KMSProviders; + schemaMap: Optional>; // Maps namespace to a local schema + bypassAutoEncryption: Optional; // Default false. + extraOptions: Optional>; + tlsOptions?: KMSProvidersTLSOptions; // Maps KMS provider to TLS options. + encryptedFieldsMap: Optional>; // Maps namespace to encryptedFields. + // bypassQueryAnalysis disables automatic analysis of outgoing commands. + // Set bypassQueryAnalysis to true to use explicit encryption on indexed fields + // without the MongoDB Enterprise Advanced licensed crypt_shared library. + bypassQueryAnalysis: Optional; // Default false. + keyExpirationMS: Optional; // Default 60000. 0 means "never expire". + credentialProviders: Optional; +} +``` + +A MongoClient can be configured to automatically encrypt collection commands and decrypt results. + +Drivers MUST document that auto encryption is an enterprise-only feature and that auto encryption only occurs on +collection level operations by including the following in the driver documentation for AutoEncryptionOpts: + +> Automatic encryption is an enterprise only feature that only applies to operations on a collection. Automatic +> encryption is not supported for operations on a database or view, and operations that are not bypassed will result in +> error (see [libmongocrypt: Auto Encryption Allow-List](#libmongocrypt-auto-encryption-allow-list)). To bypass +> automatic encryption for all operations, set bypassAutoEncryption=true in AutoEncryptionOpts. + +Explicit encryption/decryption and automatic decryption is a community feature. A MongoClient configured with +bypassAutoEncryption=true will still automatically decrypt. + +Drivers MUST document that auto encryption requires the authenticated user to have the listCollections privilege action +by including the following in the driver documentation for MongoClient: + +> Automatic encryption requires the authenticated user to have the +> [listCollections privilege action](https://www.mongodb.com/docs/manual/reference/command/listCollections/#dbcmd.listCollections). + +See +[Why is client side encryption configured on a MongoClient?](#why-is-client-side-encryption-configured-on-a-mongoclient) + +#### keyVaultNamespace + +The key vault collection namespace refers to a collection that contains all data keys used for encryption and decryption +(aka the key vault collection). Data keys are stored as documents in a special MongoDB collection. Data keys are +protected with encryption by a KMS provider (AWS KMS, Azure key vault, GCP KMS, a local master key, or KMIP). + +#### keyVaultClient + +The key vault collection is assumed to reside on the same MongoDB cluster as indicated by the connecting URI. But the +optional keyVaultClient can be used to route data key queries to a separate MongoDB cluster. + +If a `keyVaultClient` is not passed, and the parent `MongoClient` is configured with a limited `maxPoolSize`, the +`keyVaultClient` is set to an internal `MongoClient`. See +[keyVaultClient, metadataClient, and the internal MongoClient](#keyvaultclient-metadataclient-and-the-internal-mongoclient) +for configuration behavior. + +See +[What's the deal with metadataClient, keyVaultClient, and the internal client?](#whats-the-deal-with-metadataclient-keyvaultclient-and-the-internal-client) + +#### keyVaultClient, metadataClient, and the internal MongoClient + +The following pseudo-code describes the configuration behavior for the three `MongoClient` objects: + +```python +def getOrCreateInternalClient (client, clientOpts): + if client.internalClient != None: + return client.internalClient + internalClientOpts = copy(clientOpts) + internalClientOpts.autoEncryptionOpts = None + internalClientOpts.minPoolSize = 0 + client.internalClient = MongoClient (internalClientOpts) + return client.internalClient + +def configureAutoEncryptionClients (client, clientOpts): + if clientOpts.autoEncryptionOpts.keyVaultClient != None: + client.keyVaultClient = clientOpts.autoEncryptionOpts.keyVaultClient + elif clientOpts.maxPoolSize == 0: + client.keyVaultClient = client + else: + client.keyVaultClient = getOrCreateInternalClient (client, clientOpts) + + if clientOpts.autoEncryptionOpts.bypassAutomaticEncryption: + client.metadataClient = None + elif clientOpts.maxPoolSize == 0: + client.metadataClient = client + else: + client.metadataClient = getOrCreateInternalClient (client, clientOpts) +``` + +Configuring the internal `MongoClient` MUST match the parent `MongoClient`, except `minPoolSize` is set to `0` and +`AutoEncryptionOpts` is omitted. This includes copying the options and host information from the URI, and other non-URI +configuration (monitoring callbacks, stable API, etc.). + +Drivers MUST document that an additional `MongoClient` may be created, using the following as a template: + +> If a `MongoClient` with a limited connection pool size (i.e a non-zero `maxPoolSize`) is configured with +> `AutoEncryptionOpts`, a separate internal `MongoClient` is created if any of the following are true: +> +> - `AutoEncryptionOpts.keyVaultClient` is not passed. +> - `AutoEncryptionOpts.bypassAutomaticEncryption` is `false`. +> +> If an internal `MongoClient` is created, it is configured with the same options as the parent `MongoClient` except +> `minPoolSize` is set to `0` and `AutoEncryptionOpts` is omitted. + +See +[What's the deal with metadataClient, keyVaultClient, and the internal client?](#whats-the-deal-with-metadataclient-keyvaultclient-and-the-internal-client) + + + + +#### credentialProviders + +The `credentialProviders` property may be specified on [ClientEncryptionOpts](#ClientEncryptionOpts) or +[AutoEncryptionOpts](#AutoEncryptionOpts). Current support is for AWS only, but is designed to be able to accommodate +additional providers in the future. If a custom credential provider is present, it MUST be used instead of the default +flow for fetching automatic credentials and if the `kmsProviders` are not configured for automatic credential fetching +an error MUST be thrown. + +```typescript +interface CredentialProviders { + aws?: AWSCredentialProvider +} + +// The type of the AWS credential provider is dictated by the AWS SDK's credential provider for the specific +// language. +type AWSCredentialProvider = Function | Object; +``` + +The following shows an example object of `CredentialProviders` for Node.js: + +```typescript +import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; + +const client = new MongoClient(process.env.MONGODB_URI, { + autoEncryption: { + keyVaultNamespace: 'keyvault.datakeys', + kmsProviders: { + // Set to empty map to use `credentialProviders`. + aws: {} + }, + credentialProviders: { + // Acquire credentials for AWS: + aws: fromNodeProviderChain() + } + } +} +``` + +#### kmsProviders + +The `kmsProviders` property may be specified on [ClientEncryptionOpts](#ClientEncryptionOpts) or +[AutoEncryptionOpts](#AutoEncryptionOpts). Multiple KMS providers may be specified, each using a specific property on +the [KMSProviders](#kmsproviders) object. The options differ for each KMS provider type. The "local" KMS provider type +is configured with master key material. The external providers are configured with credentials to authenticate. + +Throughout this document, the KMS provider is annotated as - `KMSProvider`, but this name is for *exposition only*: +drivers MUST accept arbitrary strings at runtime for forward-compatibility. + +```typescript +interface KMSProviders { + aws?: AWSKMSOptions | { /* Empty (See "Automatic Credentials") */ }; + azure?: AzureKMSOptions | { /* Empty (See "Automatic Credentials") */ }; + gcp?: GCPKMSOptions | { /* Empty (See "Automatic Credentials") */ }; + local?: LocalKMSOptions; + kmip?: KMIPKMSOptions; + + // KMS providers may be specified with an optional name suffix separated by a colon. + // Named KMS providers do not support "Automatic Credentials". + // Note: the named KMS providers strings below are not valid Typescript regexes. They are intended for exposition only. + "^aws:[a-zA-Z0-9_]+$"?: AWSKMSOptions; + "^azure:[a-zA-Z0-9_]+$"?: AzureKMSOptions; + "^gcp:[a-zA-Z0-9_]+$"?: GCPKMSOptions; + "^local:[a-zA-Z0-9_]+$"?: LocalKMSOptions; + "^kmip:[a-zA-Z0-9_]+$"?: KMIPKMSOptions; +}; + +// KMSProvider is a string identifying a KMS provider. Note: For forward +// compatibility, any string should be accepted. +type KMSProvider = string; + +interface AWSKMSOptions { + accessKeyId: string; + secretAccessKey: string; + sessionToken?: string; // Required for temporary AWS credentials. +}; + +type AzureKMSOptions = AzureKMSCredentials | AzureAccessToken; + +interface AzureKMSCredentials { + tenantId: string; + clientId: string; + clientSecret: string; + identityPlatformEndpoint?: string; // Defaults to login.microsoftonline.com +}; + +interface AzureAccessToken { + accessToken: string; +}; + +type GCPKMSOptions = GCPKMSCredentials | GCPKMSAccessToken + +interface GCPKMSCredentials { + email: string; + privateKey: byte[] | string; // May be passed as a base64 encoded string. + endpoint?: string; // Defaults to oauth2.googleapis.com +}; + +interface GCPKMSAccessToken { + accessToken: string; +} + +interface LocalKMSOptions { + key: byte[96] | string; // The master key used to encrypt/decrypt data keys. May be passed as a base64 encoded string. +}; + +interface KMIPKMSOptions { + endpoint: string; +}; +``` + +The following shows an example object of `KMSProviders`: + +```javascript +{ + // Pass credentials for AWS: + "aws": { "accessKeyId": "foo", "secretAccessKey": "bar" }, + // Use an empty document to enable "Automatic Credentials" for Azure: + "azure": {}, + // Pass an access token for GCP: + "gcp": { "accessToken": "foo" }, + // Pass a 96 byte base64 encoded string for the local KMS provider. + "local": { "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } + // Pass the endpoint for KMIP: + "kmip": { "endpoint": "localhost:5698" } + // Pass credentials for a different AWS account by appending a name. + // Note: credentials with a name do not support "Automatic Credentials". + "aws:name2": { "accessKeyId": "foo2", "secretAccessKey": "bar2" } +} +``` + + + +##### Automatic Credentials + +Certain values of [KMSProviders](#kmsproviders) indicate a request by the user that the associated KMS providers should +be populated lazily on-demand. The driver MUST be able to populate the respective options object on-demand +if-and-only-if such respective credentials are needed. The request for KMS credentials will be indicated by +[libmongocrypt](#libmongocrypt) only once they are needed. + +When such a state is detected, [libmongocrypt](#libmongocrypt) will call back to the driver by entering the +`MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` state, upon which the driver should fill in the KMS options automatically. + +Automatic credentials are only supported for the KMS provider types `aws`, `gcp`, and `azure`. KMS providers containing +a name (e.g. `aws:myname`) do not support automatic credentials. Attempting to configure a KMS provider with a name for +automatic credentials results in a runtime error from [libmongocrypt](#libmongocrypt). See +[Why do on-demand KMS credentials not support named KMS providers?](#why-do-on-demand-kms-credentials-not-support-named-kms-providers) + +> [!NOTE] +> Drivers MUST NOT eagerly fill an empty KMS options property. + +Once requested, drivers MUST create a new [KMSProviders](#kmsproviders) $P$ according to the following process: + +1. Let $K$ be the [kmsProviders](#kmsproviders) value provided by the user as part of the original + [ClientEncryptionOpts](#ClientEncryptionOpts) or [AutoEncryptionOpts](#AutoEncryptionOpts). +2. Initialize $P$ to an empty [KMSProviders](#kmsproviders) object. +3. If $K$ contains an `aws` property, and that property is an empty map: + 1. If a custom credential provider is supplied via the `credentialProviders.aws` applicable encryption option, use + that to fetch the credentials from AWS. + 2. Otherwise: + 1. Attempt to obtain credentials $C$ from the environment using similar logic as is detailed in + [the obtaining-AWS-credentials section from the Driver Authentication specification](../auth/auth.md#obtaining-credentials), + but ignoring the case of loading the credentials from a URI + 2. If credentials $C$ were successfully loaded, create a new [AWSKMSOptions](#AWSKMSOptions) map from $C$ and + insert that map onto $P$ as the `aws` property. +4. If $K$ contains an `gcp` property, and that property is an empty map: + 1. Attempt to obtain credentials $C$ from the environment logic as is detailed in + [Obtaining GCP Credentials](#obtaining-gcp-credentials). + 2. If credentials $C$ were successfully loaded, create a new [GCPKMSOptions](#GCPKMSOptions) map from $C$ and insert + that map onto $P$ as the `gcp` property. +5. If $K$ contains an `azure` property, and that property is an empty map: + 1. If there is a `cachedAzureAccessToken` AND the duration until `azureAccessTokenExpireTime` is greater than one + minute, insert `cachedAzureAccessToken` as the `azure` property on $P$. + 2. Otherwise: + 1. Let $t_0$ be the current time. + 2. Attempt to obtain an Azure VM Managed Identity Access Token $T$ as detailed in + [Obtaining an Access Token for Azure Key Vault](#obtaining-an-access-token-for-azure-key-vault). + 3. If a token $T$ with expire duration $d\_{exp}$ were obtained successfully, create a new + [AzureAccessToken](#AzureAccessToken) object with $T$ as the `accessToken` property. Insert that + [AzureAccessToken](#AzureAccessToken) object into $P$ as the `azure` property. Record the generated + [AzureAccessToken](#AzureAccessToken) in `cachedAzureAccessToken`. Record the `azureAccessTokenExpireTime` as + $t_0 + d\_{exp}$. +6. Return $P$ as the additional KMS providers to [libmongocrypt](#libmongocrypt). + + + +##### Obtaining GCP Credentials + +Set `HOST` to `metadata.google.internal`. + +Send an HTTP request to the URL `http:///computeMetadata/v1/instance/service-accounts/default/token` with the +header `Metadata-Flavor: Google`. + +If the HTTP response code is not 200, return an error including the body of the HTTP response in the error message. + +Parse the HTTP response body as JSON. If parsing fails, return an error including the body of the HTTP response in the +error message. + +Check the parsed JSON for the field "access_token". If "access_token" is not present, return an error including the body +of the HTTP response in the error message. + +Return "access_token" as the credential. + +##### Obtaining an Access Token for Azure Key Vault + +Virtual machines running on the Azure platform have one or more *Managed Identities* associated with them. From within +the VM, an identity can be used by obtaining an access token via HTTP from the *Azure Instance Metadata Service* (IMDS). +[See this documentation for more information](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http) + +> [!NOTE] +> To optimize for testability, it is recommended to implement an isolated abstraction for communication with IMDS. This +> will aide in the implementation of the prose tests of the communication with an IMDS server. + +The below steps should be taken: + +1. Let $U$ be a new URL, initialized from the URL string `"http://169.254.169.254/metadata/identity/oauth2/token"` + +2. Add a query parameter `api-version=2018-02-01` to $U$. + +3. Add a query parameter `resource=https://vault.azure.net/` to $U$. + +4. Prepare an HTTP GET request $Req$ based on $U$. + + > [!NOTE] + > All query parameters on $U$ should be appropriately percent-encoded + +5. Add HTTP headers `Metadata: true` and `Accept: application/json` to $Req$. + +6. Issue $Req$ to the Azure IMDS server `169.254.169.254:80`. Let $Resp$ be the response from the server. If the HTTP + response is not completely received within ten seconds, consider the request to have timed out, and return an + error instead of an access token. + +7. If $Resp\_{status} ≠ 200$, obtaining the access token has failed, and the HTTP response body of $Resp$ encodes + information about the error that occurred. Return an error including the HTTP response body instead of an access + token. + +8. Otherwise, let $J$ be the JSON document encoded in the HTTP response body of $Resp$. + +9. The result access token $T$ is given as the `access_token` string property of $J$. Return $T$ as the resulting + access token. + +10. The resulting "expires in" duration $d\_{exp}$ is a count of seconds given as an ASCII-encoded integer string + `expires_in` property of $J$. + +> [!NOTE] +> If JSON decoding of $Resp$ fails, or the `access_token` property is absent from $J$, this is a protocol error from +> IMDS. Indicate this error to the requester of the access token. + +> [!NOTE] +> If an Azure VM has more than one managed identity, requesting an access token requires additional query parameters to +> disambiguate the request. For simplicity, these parameters are omitted, and only VMs that have a single managed +> identity are supported. + +##### KMS provider TLS options + +Drivers MUST provide TLS options to configure TLS connections KMS providers. + +The TLS options SHOULD be consistent with the existing TLS options for MongoDB server TLS connections. The TLS options +MUST enable setting a custom client certificate, equivalent to the `tlsCertificateKeyFile` URI option. + +Drivers SHOULD provide API that is consistent with configuring TLS options for MongoDB server TLS connections. New API +to support the TLS options MUST support future addition of KMS providers without requiring a driver API change. The +following is an example: + +```typescript +class AutoEncryptionOpts { + // setTLSOptions accepts a map of KMS providers to TLSOptions. + // The TLSOptions apply to any TLS socket required to communicate + // with the KMS provider. + setTLSOptions (opts: KMSProvidersTLSOptions) +} + +class ClientEncryptionOpts { + // setTLSOptions accepts a map of KMS providers to TLSOptions. + // The TLSOptions apply to any TLS socket required to communicate + // with the KMS provider. + setTLSOptions (opts: KMSProvidersTLSOptions) +} +``` + +Drivers MUST raise an error if the TLS options are set to disable TLS. The error MUST contain the message "TLS is +required". + +Drivers SHOULD raise an error if insecure TLS options are set. The error MUST contain the message "Insecure TLS options +prohibited". This includes options equivalent to the following URI options: + +- `tlsInsecure` +- `tlsAllowInvalidCertificates` +- `tlsAllowInvalidHostnames` +- `tlsDisableCertificateRevocationCheck` + +Drivers MUST NOT raise an error if `tlsDisableOCSPEndpointCheck` is set. Setting `tlsDisableOCSPEndpointCheck` may +prevent operation errors when OCSP responders are unresponsive. + +See the OCSP specification for a description of the default values of +[tlsDisableOCSPEndpointCheck](../ocsp-support/ocsp-support.md#tlsdisableocspendpointcheck) and +[tlsDisableCertificateRevocationCheck](../ocsp-support/ocsp-support.md#tlsdisablecertificaterevocationcheck) Drivers +MUST NOT modify the default value of `tlsDisableOCSPEndpointCheck` and `tlsDisableCertificateRevocationCheck` for KMS +TLS connections. + +See [Why do KMS providers require TLS options?](#why-do-kms-providers-require-tls-options) + +#### schemaMap + +Automatic encryption is configured with an "encrypt" field in a collection's JSONSchema. By default, a collection's +JSONSchema is periodically polled with the listCollections command. But a JSONSchema may be specified locally with the +schemaMap option. Drivers MUST document that a local schema is more secure and MUST include the following in the driver +documentation for MongoClient: + +> Supplying a schemaMap provides more security than relying on JSON Schemas obtained from the server. It protects +> against a malicious server advertising a false JSON Schema, which could trick the client into sending unencrypted data +> that should be encrypted. + +Drivers MUST document that a local schema only applies to client side encryption, and specifying JSON Schema features +unrelated to encryption will result in error. Drivers MUST include the following in the driver documentation for +MongoClient: + +> Schemas supplied in the schemaMap only apply to configuring automatic encryption for client side encryption. Other +> validation rules in the JSON schema will not be enforced by the driver and will result in an error. + +#### bypassAutoEncryption + +Drivers MUST disable auto encryption when the 'bypassAutoEncryption' option is true and not try to spawn mongocryptd. +Automatic encryption may be disabled with the bypassAutoEncryption option. See +[Why is there a bypassAutoEncryption?](#why-is-there-a-bypassautoencryption). + +#### extraOptions + +The extraOptions relate to the mongocryptd process, an implementation detail described in the +[Implementation](#implementation) section: + +```typescript +{ + // Defaults to "mongodb://localhost:27020". + mongocryptdURI: Optional, + + // Defaults to false. + mongocryptdBypassSpawn: Optional, + + // Used for spawning. Defaults to empty string and spawns mongocryptd from system path. + mongocryptdSpawnPath: Optional, + + // Passed when spawning mongocryptd. If omitted, this defaults to ["--idleShutdownTimeoutSecs=60"] + mongocryptdSpawnArgs: Optional + + // Override the path used to load the crypt_shared library + cryptSharedLibPath: Optional; + + // If 'true', refuse to continue encryption without a crypt_shared library + cryptSharedLibRequired: boolean; +} +``` + +Drivers MUST implement extraOptions in a way that allows deprecating/removing options in the future without an API +break, such as with a BSON document or map type instead of a struct type with fixed fields. See +[Why are extraOptions and kmsProviders maps?](#why-are-extraoptions-and-kmsproviders-maps). + + + +##### `extraOptions.cryptSharedLibPath` + +- Type: `undefined | string` +- Default: `undefined` + +Allow the user to specify an absolute path to a [crypt_shared](#crypt_shared) dynamic library to load. Refer: + +- [Overriding the crypt_shared Library Path](#overriding-the-crypt_shared-library-path) +- [Path Resolution Behavior](#path-resolution-behavior) +- [Enabling crypt_shared](#enabling-crypt_shared) + + + +##### `extraOptions.cryptSharedLibRequired` + +- Type: `boolean` +- Default: `false` + +If `true`, the driver MUST refuse to continue unless [crypt_shared](#crypt_shared) was loaded successfully. + +If, after initializing a `libmongocrypt_handle`, [crypt_shared](#crypt_shared) is detected to be unavailable AND +[extraOptions.cryptSharedLibRequired](#extraoptions.cryptsharedlibrequired) is `true`, the driver MUST consider the +`libmongocrypt_handle` to be invalid and return an error to the user. Refer: + + + +#### encryptedFieldsMap + +`encryptedFieldsMap` maps a collection namespace to an `encryptedFields`. + +`encryptedFieldsMap` only applies to Queryable Encryption. + +If a collection is present on both the `encryptedFieldsMap` and `schemaMap`, [libmongocrypt](#libmongocrypt) will error +on initialization. See `fle2-and-fle1-error`. + +If a collection has a set of encrypted fields, the behavior of `CreateCollection()` and `Collection.Drop()` is altered. +An additional helper, `CreateEncryptedCollection()` has been defined as a convenience wrapper around +`CreateCollection()`. See `fle2-createcollection-drop`. + +Automatic encryption in Queryable Encryption is configured with the `encryptedFields`. + +If a collection is not present on the `encryptedFields` a server-side collection `encryptedFields` may be used by +[libmongocrypt](#libmongocrypt). Drivers MUST include the following in the documentation for MongoClient: + +> Supplying an `encryptedFieldsMap` provides more security than relying on an `encryptedFields` obtained from the +> server. It protects against a malicious server advertising a false `encryptedFields`. + +#### bypassQueryAnalysis + +See [Why is bypassQueryAnalysis needed?](#why-is-bypassqueryanalysis-needed). + +### Queryable Encryption Create and Drop Collection Helpers + +A collection supporting Queryable Encryption requires an index and three additional collections. + + + +#### Collection `encryptedFields` Lookup (GetEncryptedFields) + +The convenience methods support the following lookup process for finding the `encryptedFields` associated with a +collection. + +Assume an exposition-only function $GetEncryptedFields(opts, collName, dbName, askDb)$, where $opts$ is a set of +options, $collName$ is the name of the collection, $dbName$ is the name of the database associated with that collection, +and $askDb$ is a boolean value. The resulting `encryptedFields` $EF$ is found by: + +1. Let $QualName$ be the string formed by joining $dbName$ and $collName$ with an ASCII dot `"."`. +2. If $opts$ contains an `"encryptedFields"` property, then $EF$ is the value of that property. +3. Otherwise, if `AutoEncryptionOptions.encryptedFieldsMap` contains an element named by $QualName$, then $EF$ is the + value of that element. +4. Otherwise, if $askDb$ is `true`: + 1. Issue a `listCollections` command against the database named by $dbName$, filtered by `{name: }`. Let + the result be the document $L$. + 2. If $L$ contains an `options` document element, and that element contains an `encryptedFields` document element, + $EF$ is $L$ `["options"]["encryptedFields"]`. + 3. Otherwise, $EF$ is *not-found* +5. Otherwise, $EF$ is considered *not-found*. + + + +#### Create Collection Helper + +Drivers MUST support a BSON document option named `encryptedFields` for any +[create](https://www.mongodb.com/docs/manual/reference/command/create) command helpers (e.g. +`Database.createCollection()`). This option will be interpreted by the helper method and MUST be passed to the +[create](https://www.mongodb.com/docs/manual/reference/command/create) command. + +> [!NOTE] +> Users are not expected to set the `escCollection` and `ecocCollection` options in `encryptedFields`. +> [SERVER-74069](https://jira.mongodb.org/browse/SERVER-74069) added server-side validation for those fields and no +> longer allows names to deviate from the following: +> +> - `enxcol_..esc` +> - `enxcol_..ecoc` +> +> Drivers MUST NOT document the `escCollection` and `ecocCollection` options. + +For a helper function, `CreateCollection(collectionName, collectionOptions)` with the name of the database associated as +$dbName$, look up the encrypted fields `encryptedFields` for the collection as $GetEncryptedFields(collectionOptions, +collectionName, dbName, false)$ ([See here](#GetEncryptedFields)). + +If a set of `encryptedFields` was found, then do the following operations. If any of the following operations error, the +remaining operations are not attempted: + +- Check the wire version of the writable server. If the wire version is less than 21 (for server 7.0.0), return an error + containing the error message: "Driver support of Queryable Encryption is incompatible with server. Upgrade server to + use Queryable Encryption." +- Create the collection with name `encryptedFields["escCollection"]` as a clustered collection using the options + `{clusteredIndex: {key: {_id: 1}, unique: true}}`. If `encryptedFields["escCollection"]` is not set, use the + collection name `enxcol_..esc`. Creating this collection MUST NOT check if the collection namespace + is in the `AutoEncryptionOpts.encryptedFieldsMap`. the collection namespace is in the + `AutoEncryptionOpts.encryptedFieldsMap`. +- Create the collection with name `encryptedFields["ecocCollection"]` as a clustered collection using the options + `{clusteredIndex: {key: {_id: 1}, unique: true}}`. If `encryptedFields["ecocCollection"]` is not set, use the + collection name `enxcol_..ecoc`. Creating this collection MUST NOT check if the collection namespace + is in the `AutoEncryptionOpts.encryptedFieldsMap`. +- Create the collection `collectionName` with `collectionOptions` and the option `encryptedFields` set to the + `encryptedFields`. +- Create the the index `{"__safeContent__": 1}` on collection `collectionName`. + +#### Create Encrypted Collection Helper + +To support automatic generation of encryption data keys, a helper $CreateEncryptedCollection(CE, database, collName, +collOpts, kmsProvider, masterKey)$ is defined, where $CE$ is a [ClientEncryption](#clientencryption) object, +$kmsProvider$ is a [KMSProvider](#KMSProvider) and $masterKey$ is equivalent to the $masterKey$ defined in +[DataKeyOpts](#datakeyopts). It has the following behavior: + +- If $collOpts$ contains an `"encryptedFields"` property, then $EF$ is the value of that property. Otherwise, report an + error that there are no `encryptedFields` defined for the collection. +- Let $EF'$ be a copy of $EF$. Update $EF'$ in the following manner: + - Let $Fields$ be the `"fields"` element within $EF'$. + - If $Fields$ is present and is an array value, then for each element $F$ of `Fields`: + - If $F$ is not a document element, skip it. + - Otherwise, if $F$ has a `"keyId"` named element $K$ and $K$ is a `null` value: + - Create a [DataKeyOpts](#datakeyopts) named $dkOpts$ with the $masterKey$ argument. + - Let $D$ be the result of `CE.createDataKey(kmsProvider, dkOpts)`. + - If generating $D$ resulted in an error $E$, the entire $CreateEncryptedCollection$ must now fail with error $E$. + Return the partially-formed $EF'$ with the error so that the caller may know what datakeys have already been + created by the helper. + - Replace $K$ in $F$ with $D$. +- Create a new set of options $collOpts'$ duplicating $collOpts$. Set the `"encryptedFields"` named element of + $collOpts'$ to $EF'$. +- Invoke the `CreateCollection` helper as $CreateCollection(database, collName, collOpts')$. Return the resulting + collection and the generated $EF'$. If an error occurred, return the resulting $EF$ with the error so that the + caller may know what datakeys have already been created by the helper. + +Drivers MUST document that $createEncryptedCollection$ does not affect any auto encryption settings on existing +MongoClients that are already configured with auto encryption. Users must configure auto encryption after creating the +encrypted collection with the $createEncryptedCollection$ helper. + +#### Drop Collection Helper + +Drivers MUST support a BSON document option named `encryptedFields` for any +[drop](https://www.mongodb.com/docs/manual/reference/command/drop) command helpers (e.g. `Database.dropCollection()`, +`Collection.drop()`). This option will only be interpreted by the helper method and MUST NOT be passed to the +[drop](https://www.mongodb.com/docs/manual/reference/command/drop) command. + +> [!NOTE] +> Users are not expected to set the `escCollection` and `ecocCollection` options in `encryptedFields`. +> [SERVER-74069](https://jira.mongodb.org/browse/SERVER-74069) added server-side validation for those fields and no +> longer allows names to deviate from the following: +> +> - `enxcol_..esc` +> - `enxcol_..ecoc` +> +> Drivers SHOULD NOT document the `escCollection` and `ecocCollection` options. + +For a helper function `DropCollection(dropOptions)` with associated collection named $collName$ and database named +$dbName$, look up the encrypted fields `encryptedFields` as $GetEncryptedFields(dropOptions, collName, dbname, true)$ +([See here](#GetEncryptedFields)). + +If a set of `encryptedFields` was found, then perform the following operations. If any of the following operations +error, the remaining operations are not attempted. A `namespace not found` error returned from the server (server error +code 26) MUST be ignored: + +- Drop the collection with name `encryptedFields["escCollection"]`. If `encryptedFields["escCollection"]` is not set, + use the collection name `enxcol_..esc`. +- Drop the collection with name `encryptedFields["ecocCollection"]`. If `encryptedFields["ecocCollection"]` is not set, + use the collection name `enxcol_..ecoc`. +- Drop the collection `collectionName`. + +### ClientEncryption + +```typescript +class ClientEncryption { + ClientEncryption(opts: ClientEncryptionOpts); + + // The "Create Encrypted Collection" helper is a convenience function wrapping CreateCollection. It will + // create a collection with encrypted fields, automatically allocating and assigning new data encryption + // keys. It returns a handle to the new collection, as well as a document consisting of the generated + // "encryptedFields" options. Refer to "Create Encrypted Collection Helper" + createEncryptedCollection(database: Database, collName: string, collOpts, kmsProvider: KMSProvider, masterKey: Optional): [Collection, Document]; + + // Creates a new key document and inserts into the key vault collection. + // Returns the _id of the created document as a UUID (BSON binary subtype 0x04). + createDataKey(kmsProvider: KMSProvider, opts: DataKeyOpts | null): Binary; + + // Decrypts multiple data keys and (re-)encrypts them with a new masterKey, or with their current masterKey if a new one is not given. + // The updated fields of each rewrapped data key is updated in the key vault collection as part of a single bulk write operation. + // If no data key matches the given filter, no bulk write operation is executed. + // Returns a RewrapManyDataKeyResult. + rewrapManyDataKey(filter: Document, opts: RewrapManyDataKeyOpts | null): RewrapManyDataKeyResult; + + // Removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault collection. + // Returns the result of the internal deleteOne() operation on the key vault collection. + deleteKey(id: Binary): DeleteResult; + + // Finds a single key document with the given UUID (BSON binary subtype 0x04). + // Returns the result of the internal find() operation on the key vault collection. + getKey(id: Binary): Optional; + + // Finds all documents in the key vault collection. + // Returns the result of the internal find() operation on the key vault collection. + getKeys(): Iterable; + + // Adds a keyAltName to the keyAltNames array of the key document in the key vault collection with the given UUID (BSON binary subtype 0x04). + // Returns the previous version of the key document. + addKeyAltName(id: Binary, keyAltName: String): Optional; + + // Removes a keyAltName from the keyAltNames array of the key document in the key vault collection with the given UUID (BSON binary subtype 0x04). + // Returns the previous version of the key document. + removeKeyAltName(id: Binary, keyAltName: String): Optional; + + // Returns a key document in the key vault collection with the given keyAltName. + getKeyByAltName(keyAltName: String): Optional; + + // Encrypts a BsonValue with a given key and algorithm. + // Returns an encrypted value (BSON binary of subtype 6). The underlying implementation MAY return an error for prohibited BSON values. + encrypt(value: BsonValue, opts: EncryptOpts): Binary; + + // encryptExpression encrypts a Match Expression or Aggregate Expression to query a range index. + // `expr` is expected to be a BSON document of one of the following forms: + // 1. A Match Expression of this form: + // {$and: [{: {$gt: }}, {: {$lt: }}]} + // 2. An Aggregate Expression of this form: + // {$and: [{$gt: [, ]}, {$lt: [, ]}] + // $gt may also be $gte. $lt may also be $lte. + // Only supported when queryType is "range" and algorithm is "Range". + encryptExpression(expr: Document, opts: EncryptOpts): Document; + + // Decrypts an encrypted value (BSON binary of subtype 6). + // Returns the original BSON value. + decrypt(value: Binary): BsonValue; + + // Implementation details. + private mongocrypt_t libmongocrypt_handle; + private MongoClient keyvault_client; +} +``` + + + + + +```typescript +interface ClientEncryptionOpts { + keyVaultClient: MongoClient; + keyVaultNamespace: String; + kmsProviders: KMSProviders; + credentialProviders: CredentialProviders; + tlsOptions?: KMSProvidersTLSOptions; // Maps KMS provider to TLS options. + keyExpirationMS: Optional; // Default 60000. 0 means "never expire". +}; + +interface KMSProvidersTLSOptions { + // Map the KMS providers to a set of TLS options + [provider: KMSProvider]: TLSOptionsMap; +}; +``` + +The ClientEncryption encapsulates explicit operations on a key vault collection that cannot be done directly on a +MongoClient. Similar to configuring auto encryption on a MongoClient, it is constructed with a MongoClient (to a MongoDB +cluster containing the key vault collection), KMS provider configuration, keyVaultNamespace, and tlsOptions. It provides +an API for explicitly encrypting and decrypting values, and managing data keys in the key vault collection. + +See +[Why do we have a separate top level type for ClientEncryption?](#why-do-we-have-a-separate-top-level-type-for-clientencryption) +and +[Why do we need to pass a client to create a ClientEncryption?](#why-do-we-need-to-pass-a-client-to-create-a-clientencryption) + +When implementing behavior and error handling for key vault functions, Drivers SHOULD assume the presence of a unique +index in the key vault collection on the `keyAltNames` field with a partial index filter for only documents where +`keyAltNames` exists when implementing behavior of key management functions. Drivers MAY choose to not validate or +enforce the existence of the unique index, but MUST still be capable of handling errors that such a unique index may +yield. + +See +[Why aren't we creating a unique index in the key vault collection?](#why-arent-we-creating-a-unique-index-in-the-key-vault-collection) + +### DataKeyOpts + +```typescript +class DataKeyOpts { + masterKey: Optional + keyAltNames: Optional // An alternative to _id to reference a key. + keyMaterial: Optional +} +``` + +#### masterKey + +The masterKey document identifies a KMS-specific key used to encrypt the new data key. If kmsProvider has KMS provider +type "aws", the masterKey is required and has the following fields: + +```typescript +{ + region: String, + key: String, // The Amazon Resource Name (ARN) to the AWS customer master key (KMS key). + endpoint: Optional // An alternate host identifier to send KMS requests to. May include port number. Defaults to "kms..amazonaws.com" +} +``` + +If the kmsProvider has KMS provider type "azure", the masterKey is required and has the following fields: + +```typescript +{ + keyVaultEndpoint: String, // Host with optional port. Example: "example.vault.azure.net". + keyName: String, + keyVersion: Optional // A specific version of the named key, defaults to using the key's primary version. +} +``` + +If the kmsProvider has KMS provider type "gcp", the masterKey is required and has the following fields: + +```typescript +{ + projectId: String, + location: String, + keyRing: String, + keyName: String, + keyVersion: Optional, // A specific version of the named key, defaults to using the key's primary version. + endpoint: Optional // Host with optional port. Defaults to "cloudkms.googleapis.com". +} +``` + +If the kmsProvider has KMS provider type "local", the masterKey is not applicable. + +If the kmsProvider has KMS provider type "kmip", the masterKey is required and has the following fields: + +```javascript +{ + keyId: Optional, // keyId is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. + // If keyId is omitted, the driver creates a random 96 byte KMIP Secret Data managed object. + endpoint: Optional, // Host with optional port. + delegated: Optional // If true, this key should be decrypted by the KMIP server. +} +``` + +Drivers MUST document the expected fields in the masterKey document for the "aws", "azure", "gcp", and "kmip" KMS +provider types. Additionally, they MUST document that masterKey is **required** for these KMS provider types and is not +optional. + +The value of `endpoint` or `keyVaultEndpoint` is a host name with optional port number separated by a colon. E.g. +"kms.us-east-1.amazonaws.com" or "kms.us-east-1.amazonaws.com:443". It is assumed that the host name is not an IP +address or IP literal. Though drivers MUST NOT inspect the value of "endpoint" that a user sets when creating a data +key, a driver will inspect it when connecting to KMS to determine a port number if present. + +#### keyAltNames + +An optional list of string alternate names used to reference a key. If a key is created with alternate names, then +encryption may refer to the key by the unique alternate name instead of by `_id`. The following example shows creating +and referring to a data key by alternate name: + +```python +opts = DataKeyOpts(keyAltNames=["name1"]) +clientencryption.create_data_key ("local", opts) +# reference the key with the alternate name +opts = EncryptOpts(keyAltName="name1", algorithm="AEAD_AES_256_CBC_HMAC_SHA_512-Random") +clientencryption.encrypt("457-55-5462", opts) +``` + +#### keyMaterial + +An optional BinData of 96 bytes to use as custom key material for the data key being created. If `keyMaterial` is given, +the custom key material is used for encrypting and decrypting data. Otherwise, the key material for the new data key is +generated from a cryptographically secure random device. + +### rewrapManyDataKey + +If applicable, drivers MUST document that users must upgrade dependencies if necessary to avoid being impacted by +[MONGOCRYPT-464](https://jira.mongodb.org/browse/MONGOCRYPT-464). + +If applicable, drivers MUST return an error if rewrapManyDataKey is called with libmongocrypt 1.5.1 or 1.5.0. + +### RewrapManyDataKeyOpts + +```typescript +class RewrapManyDataKeyOpts { + provider: String + masterKey: Optional +} +``` + +The `masterKey` document MUST have the fields corresponding to the given `provider` as specified in +[masterKey](#masterkey). `masterKey` MUST NOT be given if it is not applicable for the given `provider`. + +### RewrapManyDataKeyResult + +```typescript +class RewrapManyDataKeyResult { + bulkWriteResult: Optional; +} +``` + +`bulkWriteResult` is the [result of the bulk write operation](../crud/crud.md#write-results) used to update the key +vault collection with one or more rewrapped data keys. If `rewrapManyDataKey()` does not find any matching keys to +rewrap, no bulk write operation will be executed and this field will be unset. This field may also be unset if the bulk +write operation is unacknowledged as permitted by the [CRUD API Spec](../crud/crud.md#write-results). + +See +[Why does rewrapManyDataKey return RewrapManyDataKeyResult instead of BulkWriteResult?](#why-does-rewrapmanydatakey-return-rewrapmanydatakeyresult-instead-of-bulkwriteresult) + +### EncryptOpts + +```typescript +class EncryptOpts { + keyId : Optional + keyAltName: Optional + algorithm: String, + contentionFactor: Optional, + queryType: Optional + rangeOpts: Optional + stringOpts: Optional +} + +// RangeOpts specifies index options for a Queryable Encryption field supporting "range" queries. +// min, max, trimFactor, sparsity, and precision must match the values set in the encryptedFields of the destination collection. +// For double and decimal128, min/max/precision must all be set, or all be unset. +class RangeOpts { + // min is the minimum value for the encrypted index. Required if precision is set. + min: Optional, + // max is the maximum value for the encrypted index. Required if precision is set. + max: Optional, + // trimFactor may be used to tune performance. When omitted, a default value is used. + trimFactor: Optional, + // sparsity may be used to tune performance. When omitted, a default value is used. + sparsity: Optional, + // precision determines the number of significant digits after the decimal point. May only be set for double or decimal128. + precision: Optional +} + +// StringOpts specifies options for a Queryable Encryption field supporting string queries. +class StringOpts { + // substring contains further options to support substring queries. + // NOTE: substring is currently unstable API and subject to backwards breaking changes. + substring: Optional, + // prefix contains further options to support prefix queries. + prefix: Optional, + // suffix contains further options to support suffix queries. + suffix: Optional, + // caseSensitive determines whether string indexes for this field are case sensitive. + caseSensitive: bool, + // diacriticSensitive determines whether string indexes for this field are diacritic sensitive. + diacriticSensitive: bool +} + +// NOTE: SubstringOpts is currently unstable API and subject to backwards breaking changes. +class SubstringOpts { + // strMaxLength is the maximum allowed length to insert. Inserting longer strings will error. + strMaxLength: Int32, + // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error. + strMinQueryLength: Int32, + // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error. + strMaxQueryLength: Int32, +} + +class PrefixOpts { + // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error. + strMinQueryLength: Int32, + // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error. + strMaxQueryLength: Int32, +} + +class SuffixOpts { + // strMinQueryLength is the minimum allowed query length. Querying with a shorter string will error. + strMinQueryLength: Int32, + // strMaxQueryLength is the maximum allowed query length. Querying with a longer string will error. + strMaxQueryLength: Int32, +} + +``` + +Explicit encryption requires a key and algorithm. Keys are either identified by `_id` or by alternate name. Exactly one +is required. + +#### keyId + +Identifies a data key by `_id`. The value is a UUID (binary subtype 4). + +#### keyAltName + +Identifies a key vault collection document by 'keyAltName'. + +#### algorithm + +One of the strings: + +- "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" +- "AEAD_AES_256_CBC_HMAC_SHA_512-Random" +- "Indexed" +- "Unindexed" +- "Range" +- "String" + +The result of explicit encryption with the "Indexed", "Range", or "String" algorithm must be processed by the server to +insert or query. Drivers MUST document the following behavior: + +> To insert or query with an "Indexed", "Range", or "String" encrypted payload, use a `MongoClient` configured with +> `AutoEncryptionOpts`. `AutoEncryptionOpts.bypassQueryAnalysis` may be true. `AutoEncryptionOpts.bypassAutoEncryption` +> must be false. The "prefixPreview", "suffixPreview", and "substringPreview" query types are in preview and should be +> used for experimental workloads only. This feature is unstable and its security is not guaranteed until released as +> Generally Available (GA). The GA version of this feature may not be backwards compatible with the preview version. + +#### contentionFactor + +contentionFactor may be used to tune performance. Only applies when algorithm is "Indexed", "Range", or "String". +libmongocrypt returns an error if contentionFactor is set for a non-applicable algorithm. + +#### queryType + +One of the strings: + +- "equality" +- "range" +- "prefix" / "prefixPreview" + - Used for the `$encStrStartsWith` operator. +- "suffix" / "suffixPreview" + - Used for the `$encStrEndsWith` operator. +- "substring" / "substringPreview" + - Used for the `$encStrContains` operator. + +queryType only applies when algorithm is "Indexed", "Range", or "String". libmongocrypt returns an error if queryType is +set for a non-applicable algorithm. + +#### rangeOpts + +rangeOpts only applies when algorithm is "Range". libmongocrypt returns an error if rangeOpts is set for a +non-applicable algorithm. + +#### stringOpts + +stringOpts only applies when algorithm is "String". libmongocrypt returns an error if stringOpts is set for a +non-applicable algorithm. + +## User facing API: When Auto Encryption Fails + +Auto encryption requires parsing the MongoDB query language client side (with the [mongocryptd](#mongocryptd) process or +[crypt_shared](#crypt_shared) library). For unsupported operations, an exception will propagate to prevent the +possibility of the client sending unencrypted data that should be encrypted. Drivers MUST include the following in the +documentation for MongoClient: + +> If automatic encryption fails on an operation, use a MongoClient configured with bypassAutoEncryption=true and use +> ClientEncryption.encrypt() to manually encrypt values. + +For example, currently an aggregate with `$lookup` into a foreign collection is unsupported ([mongocryptd](#mongocryptd) +and [crypt_shared](#crypt_shared) return errors): + +```python +opts = AutoEncryptionOpts ( + key_vault_namespace="keyvault.datakeys", + kms_providers=kms) +client = MongoClient(auto_encryption_opts=opts) +accounts = client.db.accounts +results = accounts.aggregate([ + { + "$lookup": { + "from": "people", + "pipeline": [ + { + "$match": { + "ssn": "457-55-5462" + } + } + ], + "as": "person" + } +]) # Raises an error + +print (next(results)["person"]["ssn"]) +``` + +In this case, the user should use explicit encryption on a client configured to bypass auto encryption. (Note, automatic +decryption still occurs). + +```python +opts = AutoEncryptionOpts ( + key_vault_namespace="keyvault.datakeys", + kms_providers=kms, + bypass_auto_encryption=True) +client = MongoClient(auto_encryption_opts=opts) + +opts = ClientEncryptionOpts ( + key_vault_client=client, + key_vault_namespace="keyvault.datakeys", + kms_providers=kms) +client_encryption = ClientEncryption(opts) + +accounts = client.db.accounts +results = accounts.aggregate([ + { + "$lookup": { + "from": "people", + "pipeline": [ + { + "$match": { + "ssn": client_encryption.encrypt("457-55-5462", EncryptOpts(key_alt_name="ssn", algorithm="AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")) + } + } + ], + "as": "person" + } +]) # Throws an exception + +print (next(results)["person"]["ssn"]) +``` + +## User facing API: View limitations + +Users cannot use auto encryption with views. Attempting to do so results in an exception. Drivers do not need to +validate when the user is attempting to enable auto encryption on a view, but may defer to the underlying +implementation. + +Although auto encryption does not work on views, users may still use explicit encrypt and decrypt functions on views on +a MongoClient without auto encryption enabled. + +See [Why do operations on views fail?](#why-do-operations-on-views-fail) + +## Implementation + +Drivers MUST integrate with libmongocrypt. libmongocrypt exposes a simple state machine to perform operations. Follow +[the guide to integrating libmongocrypt](https://github.com/mongodb/libmongocrypt/blob/master/integrating.md). + +Drivers SHOULD take a best-effort approach to store sensitive data securely when interacting with KMS since responses +may include decrypted data key material (e.g. use secure malloc if available). + +All errors from the MongoClient to [mongocryptd](#mongocryptd) or the [crypt_shared](#crypt_shared) error category MUST +be distinguished in some way (e.g. exception type) to make it easier for users to distinguish when a command fails due +to auto encryption limitations. + +All errors from the MongoClient interacting with the key vault collection MUST be distinguished in some way (e.g. +exception type) to make it easier for users to distinguish when a command fails due to behind-the-scenes operations +required for encryption, decryption, or key management. + +Drivers MUST apply timeouts to operations executed as part of client-side encryption per +[Client Side Operations Timeout: Client Side Encryption](../client-side-operations-timeout/client-side-operations-timeout.md#client-side-encryption). + +## Integrating with libmongocrypt + +Each ClientEncryption instance MUST have one `libmongocrypt_handle`. + +[The libmongocrypt C API documentation](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h) For +information on how to initialize, encrypt, decrypt with libmongocrypt. + +[The Guide to Integrating libmongocrypt](https://github.com/mongodb/libmongocrypt/blob/master/integrating.md) For +information about integrating the libmongocrypt library in a driver. + +libmongocrypt exposes logging capabilities. If a driver provides a logging mechanism, it MUST enable this logging and +integrate. E.g. if your driver exposes a logging callback that a user can set, it SHOULD be possible to get log messages +from libmongocrypt. + +Drivers MUST propagate errors from libmongocrypt in whatever way is idiomatic to the driver (exception, error object, +etc.). These errors MUST be distinguished in some way (e.g. exception type) to make it easier for users to distinguish +when a command fails due to client side encryption. + + + +## Enabling Command Marking with the `crypt_shared` Library + +The MongoDB Enterprise distribution includes a dynamic library named `mongo_crypt_v1` (with the appropriate file +extension or filename suffix for the host platform). This library will be loaded by [libmongocrypt](#libmongocrypt) when +the `mongocrypt_init` function is invoked +[(from the libmongocrypt C API)](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h) based on the +search criteria that are provided by the driver. + +[libmongocrypt](#libmongocrypt) allows the driver to specify an arbitrary list of directory +[search paths](#search-paths) in which to search for the [crypt_shared](#crypt_shared) dynamic library. The user-facing +API does not expose this full search path functionality. This extended search path customization is intended to +facilitate driver testing with [crypt_shared](#crypt_shared) (Refer: +[Search Paths for Testing](#search-paths-for-testing) and [Path Resolution Behavior](#path-resolution-behavior)). + +> [!NOTE] +> The driver MUST NOT manipulate or do any validation on the [crypt_shared](#crypt_shared) path options provided in +> [extraOptions](#extraoptions). They should be passed through to [libmongocrypt](#libmongocrypt) unchanged. + + + +### Setting Search Paths + +For the user-facing API the driver MUST append the literal string - `"$SYSTEM"` to the search paths for the +`libmongocrypt_handle` if `bypassAutoEncryption` is not set to `true`, and MUST NOT append to the search path if it is +set to `true` or if the [libmongocrypt](#libmongocrypt) instance is used for explicit encryption only (i.e. on the +ClientEncryption class). For purposes of testing, a driver may use a different set of search paths. + +**Explanation** + +The [search paths](#search-paths) array in [libmongocrypt](#libmongocrypt) allows the driver to customize the way that +[libmongocrypt](#libmongocrypt) searches and loads the [crypt_shared](#crypt_shared) library. For testing purposes, the +driver may change the paths that it appends for [crypt_shared](#crypt_shared) searching to better isolate the test +execution from the ambient state of the host system. + +Refer to: [Path Resolution Behavior](#path-resolution-behavior) and +[Search Paths for Testing](#search-paths-for-testing) + + + +### Overriding the `crypt_shared` Library Path + +If [extraOptions.cryptSharedLibPath](#extraoptions.cryptsharedlibpath) was specified by the user, the driver MUST set +the [crypt_shared](#crypt_shared) path override on the `libmongocrypt_handle`. + +> [!NOTE] +> If a path override is set on a `libmongocrypt_handle` and [libmongocrypt](#libmongocrypt) fails to load +> [crypt_shared](#crypt_shared) from that filepath, then that will result in a hard-error when initializing +> [libmongocrypt](#libmongocrypt). + +### Path Resolution Behavior + +Drivers should include and note the following information regarding the behavior of [crypt_shared](#crypt_shared) path +options in [extraOptions](#extraoptions): + +- If used, the [override path](#override-path) must be given as a path to the [crypt_shared](#crypt_shared) dynamic + library file *itself*, and not simply the directory that contains it. + +- If the given [override path](#override-path) is a relative path and the first path component is the literal string + `"$ORIGIN"`, the `"$ORIGIN"` component will be replaced by the absolute path to the directory containing the + [libmongocrypt](#libmongocrypt) library that is performing the [crypt_shared](#crypt_shared) search. This behavior + mimics the `$ORIGIN` behavior of the `RUNPATH`/`RPATH` properties of ELF executable files. This permits bundling the + [crypt_shared](#crypt_shared) library along with [libmongocrypt](#libmongocrypt) for creating portable application + distributions without relying on a externally/globally available [crypt_shared](#crypt_shared) library. + + > [!NOTE] + > No other `RPATH`/`RUNPATH`-style substitutions are available. + +- If the [override path](#override-path) is given as a relative path, that path will be resolved relative to the working + directory of the operating system process. + +- If an [override path](#override-path) was specified and [libmongocrypt](#libmongocrypt) fails to load + [crypt_shared](#crypt_shared) from that filepath, [libmongocrypt](#libmongocrypt) will fail to initialize with a + hard-error. [libmongocrypt](#libmongocrypt) will not attempt to search for [crypt_shared](#crypt_shared) in any + other locations. + +- If [libmongocrypt](#libmongocrypt) fails to load the [crypt_shared](#crypt_shared) library after searching the system + (and no [override path](#override-path) is specified), [libmongocrypt](#libmongocrypt) will proceed without error + and presume that [crypt_shared](#crypt_shared) is unavailable. + +#### Search Paths for Testing + +Drivers can make use of different [search paths](#search-paths) settings for testing purposes. These search paths use +the following behavior: + +- For [crypt_shared](#crypt_shared) [search paths](#search-paths), if a search path string is `"$SYSTEM"`, then — + instead of [libmongocrypt](#libmongocrypt) searching for [crypt_shared](#crypt_shared) in a directory named + "`$SYSTEM`" — [libmongocrypt](#libmongocrypt) will defer to the operating system's own dynamic-library resolution + mechanism when processing that search-path. For this reason, `"$SYSTEM"` is the only search path appended when the + driver is used via the user-facing API. + +- The [search paths](#search-paths) also support the `$ORIGIN` substitution string. + +- Like with the [override path](#override-path), if a [search path](#search-path) is given as a relative path, that path + will be resolved relative to the working directory of the operating system process. + +- If no [search paths](#search-paths) are appended to the `libmongocrypt_handle`, the resulting search paths will be an + empty array, effectively [disabling crypt_shared](#disabling-crypt_shared) searching. + + In this case, unless an [override path](#override-path) is specified, [libmongocrypt](#libmongocrypt) is guaranteed + not to load [crypt_shared](#crypt_shared). + +### Detecting [crypt_shared](#crypt_shared) Availability + +[crypt_shared](#crypt_shared) availability can only be reliably detected *after* initializing the +`libmongocrypt_handle`. + +After initializing the `libmongocrypt_handle`, the driver can detect whether [crypt_shared](#crypt_shared) was +successfully loaded by asking [libmongocrypt](#libmongocrypt) for the [crypt_shared](#crypt_shared) version string. If +the result is an empty string, [libmongocrypt](#libmongocrypt) did not load [crypt_shared](#crypt_shared) and the driver +must rely on [mongocryptd](#mongocryptd) to mark command documents for encryption. + + + +### "Disabling" `crypt_shared` + +For purposes of testing, a driver can "disable" [crypt_shared](#crypt_shared) searching to ensure that +[mongocryptd](#mongocryptd) is used instead, even if a [crypt_shared](#crypt_shared) library would be available. + +As noted in [Path Resolution Behavior](#path-resolution-behavior), [crypt_shared](#crypt_shared) can be "disabled" on a +`libmongocrypt_handle` by omission: + +1. Do not specify any [search paths](#search-paths), +2. AND do not specify a [crypt_shared](#crypt_shared) library [override path](#override-path) + ([extraOptions.cryptSharedLibPath](#extraoptions.cryptsharedlibpath)). + +This will have the effect that [libmongocrypt](#libmongocrypt) will not attempt to search or load +[crypt_shared](#crypt_shared) during initialization. + +At the current time, no user-facing API is exposed that allows users to opt-out of [crypt_shared](#crypt_shared). + +### Loading [crypt_shared](#crypt_shared) Multiple Times + +Due to implementation restrictions, there must not be more than one [crypt_shared](#crypt_shared) dynamic library loaded +simultaneously in a single operating system process. `libmongocrypt` will do its best to enforce this at the time that +it loads [crypt_shared](#crypt_shared) while initializing a `libmongocrypt_handle`. `libmongocrypt` will keep track of +the open [crypt_shared](#crypt_shared) library globally, and any subsequent attempt to use a +[crypt_shared](#crypt_shared) library that does not exactly match the filepath of the already-loaded +[crypt_shared](#crypt_shared) will result in an error. + +If at least one `libmongocrypt_handle` exists in an operating system process that has an open handle to a +[crypt_shared](#crypt_shared) library, subsequent attempts to initialize an additional `libmongocrypt_handle` will fail +if: + +1. The new `libmongocrypt_handle` wants [crypt_shared](#crypt_shared) (i.e. at least one [search path](#search-path) was + specified or an [override path](#override-path) was specified). +2. AND the initialization of that `libmongocrypt_handle` does not successfully find and load the same + [crypt_shared](#crypt_shared) library that was loaded by the existing `libmongocrypt_handle` that is already using + [crypt_shared](#crypt_shared). + +Drivers MUST document this limitation for users along with the documentation on the `cryptShared*` options in +[extraOptions](#extraoptions) by including the following: + +> All `MongoClient` objects in the same process should use the same setting for +> [extraOptions.cryptSharedLibPath](#extraoptions.cryptsharedlibpath), as it is an error to load more that one +> [crypt_shared](#crypt_shared) dynamic library simultaneously in a single operating system process. + +Once all open handles to a [crypt_shared](#crypt_shared) library are closed, it is possible to load a different +[crypt_shared](#crypt_shared) library than was previously loaded. The restriction only applies to simultaneous open +handles within a single process. + +## Managing mongocryptd + +If the following conditions are met: + +- The user's `MongoClient` is configured for client-side encryption (i.e. `bypassAutoEncryption` is not `false`) +- **AND** the user has not disabled `mongocryptd` spawning (i.e. by setting `extraOptions.mongocryptdBypassSpawn` to + `true`), +- **AND** the [crypt_shared](#crypt_shared) library is unavailable (Refer: + [Detecting crypt_shared Availability](#detecting-crypt_shared-availability)), +- **AND** the [extraOptions.cryptSharedLibRequired](#extraoptions.cryptsharedlibrequired) option is `false`. + +**then** `mongocryptd` MUST be spawned by the driver. + +If the [extraOptions.cryptSharedLibRequired](#extraoptions.cryptsharedlibrequired) option is `true` then the driver MUST +NOT attempt to spawn or connect to `mongocryptd`. + +> [!NOTE] +> Since spawning [mongocryptd](#mongocryptd) requires checking whether [crypt_shared](#crypt_shared) is loaded, and +> checking whether [crypt_shared](#crypt_shared) is available can only be done *after* having initialized the +> `libmongocrypt_handle`, drivers will need to defer spawning [mongocryptd](#mongocryptd) until *after* initializing +> [libmongocrypt](#libmongocrypt) and checking for [crypt_shared](#crypt_shared). + +### Spawning [mongocryptd](#mongocryptd) + +If a MongoClient is configured for Client Side Encryption (eg. `bypassAutoEncryption=false`), then by default (unless +`mongocryptdBypassSpawn=true`), mongocryptd MUST be spawned by the driver. Spawning MUST include the command line +argument `--idleShutdownTimeoutSecs`. If the user does not supply one through `extraOptions.mongocryptdSpawnArgs` (which +may be either in the form `--idleShutdownTimeoutSecs=60` or as two consecutive arguments +`["--idleShutdownTimeoutSecs", 60]`, then the driver MUST append `--idleShutdownTimeoutSecs=60` to the arguments. This +tells mongocryptd to automatically terminate after 60 seconds of non-use. The stdout and stderr of the spawned process +MUST not be exposed in the driver (e.g. redirect to `/dev/null`). Users can pass the argument `--logpath` to +`extraOptions.mongocryptdSpawnArgs` if they need to inspect mongocryptd logs. + +Upon construction, the MongoClient MUST create a MongoClient to mongocryptd configured with +`serverSelectionTimeoutMS=10000`. + +If spawning is necessary, the driver MUST spawn mongocryptd whenever server selection on the MongoClient to mongocryptd +fails. If the MongoClient fails to connect after spawning, the server selection error is propagated to the user. + +### Connecting to [mongocryptd](#mongocryptd) + +If the [crypt_shared](#crypt_shared) library is loaded, the driver MUST NOT attempt to connect to +[mongocryptd](#mongocryptd). (Refer: [Detecting crypt_shared Availability](#detecting-crypt_shared-availability)). + +Single-threaded drivers MUST connect with +[serverSelectionTryOnce=false](../server-selection/server-selection.md#serverselectiontryonce), +`connectTimeoutMS=10000`, and MUST bypass +[cooldownMS](../server-discovery-and-monitoring/server-monitoring.md#cooldownms) when connecting to mongocryptd. See +[Why are serverSelectionTryOnce and cooldownMS disabled for single-threaded drivers connecting to mongocryptd?](#why-are-serverselectiontryonce-and-cooldownms-disabled-for-single-threaded-drivers-connecting-to-mongocryptd) + +If the ClientEncryption is configured with `mongocryptdBypassSpawn=true`, then the driver is not responsible for +spawning mongocryptd. If server selection ever fails when connecting to mongocryptd, the server selection error is +propagated to the user. + +> [!NOTE] +> A correctly-behaving driver will never attempt to connect to [mongocryptd](#mongocryptd) when +> [extraOptions.cryptSharedLibRequired](#extraoptions.cryptsharedlibrequired) is set to `true` or +> [crypt_shared](#crypt_shared) is loaded. + +## ClientEncryption + +The new ClientEncryption type interacts uses libmongocrypt to perform ClientEncryption operations. See the +[libmongocrypt API documentation](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h) for more +information. + +The ClientEncryption contains a MongoClient connected to the MongoDB cluster containing the key vault collection. It +does not contain a MongoClient to mongocryptd. + +See +[Why does ClientEncryption have key management functions when Drivers can use existing CRUD operations instead?](#why-does-clientencryption-have-key-management-functions-when-drivers-can-use-existing-crud-operations-instead). + +## Key Vault collection + +The key vault collection is the specially designated collection containing encrypted data keys. There is no default +collection (user must specify). The key vault collection is used for automatic and explicit encryption/decryption as +well as key management functions. + +For key management functions that require creating, updating, or deleting key documents in the key vault collection, the +corresponding operations MUST be done with write concern majority. + +For encryption/decryption and key management functions that require reading key documents from the key vault collection, +the corresponding operations MUST be done with read concern majority. + +Some key management functions may require multiple commands to complete their operation. Key management functions +currently assume there is no concurrent access of the key vault collection being operated on. Concurrent access to a key +vault collection being operated on may result in unexpected or undefined behavior. Support for concurrent key management +may be considered for future work. + +See [Support sessions in key management functions](#support-sessions-in-key-management-functions). + +## Auto encrypt and decrypt + +An encrypted MongoClient automatically encrypts values for filtering and decrypts results. + +The driver MUST use libmongocrypt to initiate auto encryption and decryption. Create the BSON command meant to be sent +over the wire, then pass that through the libmongocrypt state machine and use the returned BSON command in its place. +The state machine is created with the libmongocrypt function `mongocrypt_ctx_new` and initialized with a +`mongocrypt_ctx_encrypt_init` or `mongocrypt_ctx_decrypt_init`. See the +[libmongocrypt API documentation](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h) for more +information. + +An encrypted MongoClient configured with bypassAutoEncryption MUST NOT attempt automatic encryption for any command. + +Otherwise, an encrypted MongoClient MUST attempt to auto encrypt all commands. Note, the underlying implementation may +determine no encryption is necessary, or bypass many checks if the command is deemed to not possibly contain any +encrypted data (e.g. ping). See the appendix section: +[libmongocrypt: Auto Encryption Allow-List](#libmongocrypt-auto-encryption-allow-list). + +An encrypted MongoClient MUST attempt to auto decrypt the results of all commands. + +Drivers MUST raise an error when attempting to auto encrypt a command if the maxWireVersion is less than 8. The error +message MUST contain "Auto-encryption requires a minimum MongoDB version of 4.2". + +Note, all client side features (including all of `ClientEncryption`) are only supported against 4.2 or higher servers. +However, errors are only raised for automatic encryption/decryption against older servers. See +[Why is a 4.2 server required?](#why-is-a-42-server-required) + +## Interaction with Command Monitoring + +Unencrypted data MUST NOT appear in the data of any command monitoring events. Encryption MUST occur before generating a +CommandStartedEvent, and decryption MUST occur after generating a CommandSucceededEvent. + +## Size limits for Write Commands + +Automatic encryption requires the driver to serialize write commands as a single BSON document before automatically +encrypting with libmongocrypt (analogous to constructing [OP_MSG payload type 0](../message/OP_MSG.md#sections), not a +document sequence). Automatic encryption returns a single (possibly modified) BSON document as the command to send. + +Because automatic encryption increases the size of commands, the driver MUST split bulk writes at a reduced size limit +before undergoing automatic encryption. The write payload MUST be split at 2MiB (2097152). Where batch splitting occurs +relative to automatic encryption is implementation-dependent. + +Drivers MUST not reduce the size limits for a single write before automatic encryption. I.e. if a single document has +size larger than 2MiB (but less than `maxBsonObjectSize`) proceed with automatic encryption. + +Drivers MUST document the performance limitation of enabling client side encryption by including the following +documentation in MongoClient: + +> Enabling Client Side Encryption reduces the maximum write batch size and may have a negative performance impact. + +## Appendix + +### Appendix terms + +**intent-to-encrypt marking** + +One of the data formats of BSON binary subtype 6, representing an encoded BSON document containing plaintext and +metadata. + +### Key vault collection schema for data keys + +Data keys are stored in the MongoDB key vault collection with the following schema: + +| | | | +| ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | +| **Name** | **Type** | **Description** | +| \_id | UUID | A unique identifier for the key. | +| version | Int64 | A numeric identifier for the schema version of this document. Implicitly 0 if unset. | +| keyAltNames | Array of strings | Alternate names to search for keys by. Used for a per-document key scenario in support of GDPR scenarios. | +| keyMaterial | BinData | Encrypted data key material, BinData type General | +| creationDate | Date | The datetime the wrapped data key material was imported into the Key Database. | +| updateDate | Date | The datetime the wrapped data key material was last modified. On initial import, this value will be set to creationDate. | +| status | Int | 0 = enabled, 1 = disabled | +| masterKey | Document | Per provider master key definition, see below | + +#### masterKey contents + +| | | | +| -------- | -------- | --------------------------------------------------------------------- | +| **Name** | **Type** | **Description** | +| provider | "aws" | | +| key | String | AWS ARN. Only applicable for "aws" provider. | +| region | String | AWS Region that contains AWS ARN. Only applicable for "aws" provider. | +| endpoint | String | Alternate AWS endpoint (needed for FIPS endpoints) | + +| | | | +| ---------------- | -------- | ------------------------------------------------------------- | +| **Name** | **Type** | **Description** | +| provider | "azure" | | +| keyVaultEndpoint | String | Required key vault endpoint. (e.g. "example.vault.azure.net") | +| keyName | String | Required key name. | +| keyVersion | String | Optional key version. | + +| | | | +| ---------- | -------- | ---------------------------------------------------------------- | +| **Name** | **Type** | **Description** | +| provider | "gcp" | | +| projectId | String | Required project ID. | +| location | String | Required location name (e.g. "global") | +| keyRing | String | Required key ring name. | +| keyName | String | Required key name. | +| keyVersion | String | Optional key version. | +| endpoint | String | Optional, KMS URL, defaults to | + +| | | | +| -------- | -------- | --------------- | +| **Name** | **Type** | **Description** | +| provider | "local" | | + +| | | | +| --------- | -------- | -------------------------------------------------------------------------------------- | +| **Name** | **Type** | **Description** | +| provider | "kmip" | | +| endpoint | String | Optional. Defaults to kmip.endpoint from KMS providers. | +| delegated | Boolean | Optional. Defaults to false. | +| keyId | String | Required. keyId is the Unique Identifier to a 96 byte KMIP Secret Data managed object. | + +Data keys are needed for encryption and decryption. They are identified in the intent-to-encrypt marking and ciphertext. +Data keys may be retrieved by querying the "\_id" with a UUID or by querying the "keyAltName" with a string. + +Note, "status" is unused and is purely informational. + +#### Example data key document + +```javascript +{ + "_id" : UUID("00000000-0000-0000-0000-000000000000"), + "status" : 1, + "masterKey" : { + "provider" : "aws", + "key" : "arn:aws...", + "region" : "us-east-1" + }, + "updateDate" : ISODate("2019-03-18T22:53:50.483Z"), + "keyMaterial" : BinData(0,"AQICAH..."), + "creationDate" : ISODate("2019-03-18T22:53:50.483Z"), + "keyAltNames" : [ + "altname", + "another_altname" + ] +} +``` + +### BSON binary subtype 6 + +BSON Binary Subtype 6 has a one byte leading identifier. The following is a quick reference. + +```typescript +struct { + uint8 subtype; + [more data - see individual type definitions] +} +``` + +#### Type 0: Intent-to-encrypt marking + +```typescript +struct { + uint8 subtype = 0; + [ bson ]; +} +``` + +#### Types 1 and 2: Ciphertext + +```typescript +struct { + uint8 subtype = (1 or 2); + uint8 key_uuid[16]; + uint8 original_bson_type; + uint32 ciphertext_length; + uint8 ciphertext[ciphertext_length]; +} +``` + +See [Driver Spec: BSON Binary Encrypted](../bson-binary-encrypted/binary-encrypted.md) for more information. + +### JSONSchema "encrypt" + +The additional support for encryption in JSONSchema will be documented in the MongoDB manual. But the following is an +example: + +```typescript +encrypt : { + bsonType: "int" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + keyId: [UUID(...)] +} +``` + +Each field is briefly described as follows: + +| Name | Type | Description | +| --------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| bsonType | string | The bsonType of the underlying encrypted field. | +| algorithm | string | "AEAD_AES_256_CBC_HMAC_SHA_512-Random" or
"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" | +| keyId | string or array of UUID | If string, it is a JSON pointer to a field with a scalar value
identifying a key by keyAltName.
If array, an array of eligible keys. | + +### libmongocrypt: Prohibitions and warnings + +libmongocrypt MUST validate options. The following noteworthy cases are prohibited: + +- Explicit encryption using the deterministic algorithm on any of the following types: + - array + - document + - code with scope + - single value types: undefined, MinKey, MaxKey, Null + - decimal128 + - double + - bool +- Explicit encryption on a BSON binary subtype 6. + +The following cases MUST warn: + +- A local schema that does not include encrypted fields. + +### libmongocrypt: Collection info caching + +libmongocrypt will cache the collection infos so encryption with remote schemas need not run listCollections every time. +Collection infos (or lack thereof) are cached for one minute. This is not configurable. After expiration, subsequent +attempts to encrypt will result in libmongocrypt requesting a new collection info. + +A collection info result indicates if the collection is really a view. If it is, libmongocrypt returns an error since it +does not know the schema of the underlying collection. + +A collection info with validators that aside from one top level `$jsonSchema` are considered an error. + +### libmongocrypt: Data key caching + +Data keys are cached in libmongocrypt for one minute. This is not configurable, and there is no maximum number of keys +in the cache. The data key material is stored securely. It will not be paged to disk and the memory will be properly +zero'ed out after freeing. + +### libmongocrypt: Crypto implementation + +libmongocrypt uses AEAD_SHA256_CBC_HMAC512 for both "randomized" and "deterministic" encryption algorithms. It is +described in this [IETF document draft](https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05). For +"randomized", libmongocrypt securely creates a random IV. For "deterministic", libmongocrypt securely creates a random +IV key and any given encryption operation will derive the IV from the IV key and the field plaintext data. + +### libmongocrypt: Auto Encryption Allow-List + +libmongocrypt determines whether or not the command requires encryption (i.e. is sent to mongocryptd) based on the table +below. Commands not listed in this table will result in an error returned by libmongocrypt. + +| | | +| ------------------------ | ----------- | +| **Command** | **Action** | +| aggregate (collection) | AUTOENCRYPT | +| count | AUTOENCRYPT | +| distinct | AUTOENCRYPT | +| delete | AUTOENCRYPT | +| find | AUTOENCRYPT | +| findAndModify | AUTOENCRYPT | +| getMore | BYPASS | +| insert | AUTOENCRYPT | +| update | AUTOENCRYPT | +| authenticate | BYPASS | +| getnonce | BYPASS | +| logout | BYPASS | +| hello | BYPASS | +| legacy hello | BYPASS | +| abortTransaction | BYPASS | +| commitTransaction | BYPASS | +| endSessions | BYPASS | +| startSession | BYPASS | +| create | BYPASS | +| createIndexes | BYPASS | +| createSearchIndexes | BYPASS | +| drop | BYPASS | +| dropDatabase | BYPASS | +| dropIndexes | BYPASS | +| dropSearchIndex | BYPASS | +| killCursors | BYPASS | +| listCollections | BYPASS | +| listDatabases | BYPASS | +| listIndexes | BYPASS | +| renameCollection | BYPASS | +| explain | AUTOENCRYPT | +| ping | BYPASS | +| killAllSessions | BYPASS | +| killSessions | BYPASS | +| killAllSessionsByPattern | BYPASS | +| refreshSessions | BYPASS | +| updateSearchIndex | BYPASS | + +All AUTOENCRYPT commands are sent to mongocryptd, even if there is no JSONSchema. This is to ensure that commands that +reference other collections (e.g. aggregate with `$lookup`) are handled properly. + +## Test Plan + +See the [README.md](tests/README.md) in the test directory. + +## Rationale + +### Design Principles + +In addition to the [Driver Mantras](https://github.com/mongodb/specifications#driver-mantras) there are design +principles specific to this project. + +### 1. Make encryption easy to enable + +Users should be able to enable encryption with minimal application change. + +### 2. Minimize risk of exposing sensitive data + +Storing or querying with unencrypted data can have dire consequences, because users may not be made aware immediately. +When in doubt, we should error. It should be clear to the user when an operation gets encrypted and when one doesn't. + +### 3. Minimize API + +The first version of Client Side Encryption is to get signal. If it becomes popular, further improvements will be made +(removing mongocryptd process, support for more queries, better performance). But the public API we provide now will +stick around for the long-term. So let's keep it minimal to accomplish our goals. + +### How did we arrive at this API? + +The API for client side encryption underwent multiple iterations during the design process. + +#### Why is client side encryption configured on a MongoClient? + +There is state that must be shared among all auto encrypted collections: the MongoClient to mongocryptd and the handle +to libmongocrypt (because key caching + JSONSchema caching occurs in libmongocrypt). + +#### Why not make auto encryption "opt-in"? + +Because auto encryption is specified with a collection JSONSchema, we cannot auto encrypt database or client operations. +So we cannot know if the user is passing sensitive data as a filter to a database/client change stream or a currentOp +command for example. We also must always fail on view operations. We considered making auto encryption opt-in for +collections. But we decided against this. It is much simpler for users to enable auto encryption without enumerating all +collections with encryption in the common case of using remote JSONSchemas. + +Note, this takes the trade-off of a better user experience over less safety. If a user mistakenly assumes that auto +encryption occurs on a database, or on a collection doing a `$(graph)lookup` on a collection with auto encryption, they +may end up sending unencrypted data. + +#### Why are auto encrypted collections configured at level of MongoClient? + +In a previous iteration of the design, we proposed enabling auto encryption only in db.getCollection() for better +usability. But this better aligns with our design principles. + +- Safer. Users won't forget to enable auto encryption on one call to db.getCollection() +- Easier. It only requires changing MongoClient code instead of every db.getCollection() + +#### Why do we have a separate top level type for ClientEncryption? + +The encrypt/decrypt and createDataKey functions were originally placed on MongoClient. But, then we'd have API that +depends on optional configuration. A new top level type seemed warranted. + +#### Why not pass the ClientEncryption into db.getCollection() to enable auto encryption? + +As it is now, a ClientEncryption and a MongoClient cannot share state (libmongocrypt handle or MongoClient to +mongocryptd). Foreseeably, they could share state if auto encryption was enabled by passing a ClientEncryption object +like: + +```javascript +db.getCollection("coll", { autoEncrypt: { clientEncryption: clientEncryption } }) +``` + +But this would require a MongoCollection to peek into the internals of a ClientEncryption object. This is messy and +language dependent to implement and makes mocking out the ClientEncryption difficult for tests. + +#### Why do we need to pass a client to create a ClientEncryption? + +We need to support an external key vault collection (i.e. on another MongoDB cluster). + +#### Why are extraOptions and kmsProviders maps? + +Because we don't want AWS as part of the public types and we don't want to put [mongocryptd](#mongocryptd) and +[crypt_shared](#crypt_shared) options as types since mongocryptd is an implementation detail we'd like to hide as much +as possible. + +#### Why is there a bypassAutoEncryption? + +bypassAutoEncryption still supports auto decryption. In cases where [mongocryptd](#mongocryptd) or +[crypt_shared](#crypt_shared) cannot analyze a query, it's still useful to provide auto decryption. Just like static +program analysis cannot always prove that a runtime invariant holds, +[mongocryptd](#mongocryptd)/[crypt_shared](#crypt_shared) cannot always prove that a query will be safe with respect to +encryption at runtime. + +### Why not require compatibility between mongocryptd and the server? + +It isn't necessary or unsafe if mongocryptd parses an old version of MQL. Consider what happens when we add a new +operator, `$newOperator`. If it properly encrypts a value in the `$newOperator` expression and sends it to an old server +that doesn't have `$newOperator`, that's a mistake but not a security hole. Also if the app passes a query with +`$newOperator` to mongocryptd, and mongocryptd doesn't know about `$newOperator`, then it will error, +`"Unrecognized operator $newOperator"` or something. Also a mistake, not a security hole. + +So long as mongocryptd errors on unrecognized expressions, we don't need version compatibility between the mongocryptd +and server for the sake of security. + +### Why cache keys? + +We can't re-fetch the key on each operation, the performance goal for this project requires us to cache. We do need a +revocation mechanism, based upon periodic checking from the client. Initially this window will not be configurable. + +### Why require including a C library? + +- libmongocrypt deduplicates a lot of the work: JSONSchema cache, KMS message construction/parsing, key caching, and + encryption/decryption. +- Our "best-effort" of storing decrypted key material securely is best accomplished with a C library. +- Having crypto done in one centralized C library makes it much easier to audit the crypto code. + +### Why warn if a local schema does not have encrypted fields? + +Because that is the only use of local schemas. No other JSONSchema validators have any function. It's likely the user +misconfigured encryption. + +### Why limit to one top-level `$jsonSchema`? + +- If we allow siblings, we can run into cases where the user specifies a top-level `$and/$or` or any arbitrary + match-expression that could have nested `$jsonSchema`'s. +- Furthermore, the initial versions of [mongocryptd](#mongocryptd) and [crypt_shared](#crypt_shared) are only + implementing query analysis when the validator consists of a single `$jsonSchema` predicate. This helps to simplify + the [mongocryptd](#mongocryptd) and [crypt_shared](#crypt_shared) logic, and unifies it with the case where users + configure their schemas directly in the driver. + +### Why not allow schemas to be configured at runtime? + +We could have something like Collection::setEncryptionSchema(), but users can simply recreate the client to set new +local schemas. + +### Why not support other AWS auth mechanisms? + +We could potentially authenticate against AWS in a more sophisticated way, like read credentials from ~/.aws/credentials +or assuming a role with EC2 instance metadata. But we've decided to implement the simplest authentication mechanism for +v1, and defer more sophisticated ones as future work. + +### Why not pass a URI for external key vault collections instead of a MongoClient? + +Some configuration on a MongoClient can only be done programmatically. E.g. in Java TLS configuration can only be done +at runtime since it is abstracted in an SSLContext which cannot be accessed or altered by the driver. + +### What happened to multiple key vault collections? + +An earlier revision of this specification supported multiple active key vaults with the notion of a "key vault +collection alias". The key vault collection alias identified one of possibly many key vault collections that stored the +key to decrypt the ciphertext. However, enforcing one key vault collection is a reasonable restriction for users. There +isn't clear value in having multiple key vault collections. And having active multiple key vault collections is not +necessary to migrate key vault collections. + +### Why auto encrypt a command instead of a wire protocol message? + +- It is significantly easier to implement communication in drivers if libmongocrypt gives back BSON object that can be + passed to run command. +- mongocryptd cannot return document sequences, so it will return an array of documents anyway. +- Though it is foreseeable that a driver can take the final result of encryption and turn it into an OP_MSG document + sequence, it does not seem worthwhile to impose extra complexity in this case. + +### Why is a failure to decrypt always an error? + +In the original design we proposed *not* to error if decryption failed due to a missing key. But, it's not clear this is +a needed functionality, it goes against our principle of "Minimize API", and there's a simple recourse for users: bypass +mongocryptd and explicitly decrypt instead. + +### Why are there no APM events for mongocryptd? + +Though it may be helpful for debugging to expose APM events for mongocryptd, mongocryptd is an implementation detail +we'd like to have the freedom to remove in the future. So we want to expose mongocryptd as little as possible. + +### Why aren't we creating a unique index in the key vault collection? + +There should be a unique index on the `keyAltNames` field with a partial index filter for only documents where +`keyAltNames` exists. Although GridFS automatically creates indexes as a convenience upon first write, it has been +problematic before due to requiring the `createIndex` privilege, which a user might not have if they are just querying +the key vault collection with find and adding keys with insert. + +See also . + +### Why do operations on views fail? + +Currently, the driver does not resolve the entire view pipeline, which would be necessary to know the schema of the +underlying collection. But, the driver does know whether or not a namespace is a view based on the response to +listCollections. And the driver will run listCollections on all namespaces omitted from the schemaMap. + +### Why is a 4.2 server required? + +Limiting to 4.2 reduces testing complexity. Additionally The `encrypt` subdocument in JSON schema is only supported on +4.2 or higher servers. Although not technically necessary for client side encryption, it does provide a fallback against +accidentally sending unencrypted data from misconfigured clients. + +### Why are serverSelectionTryOnce and cooldownMS disabled for single-threaded drivers connecting to mongocryptd? + +By default, single threaded clients set serverSelectionTryOnce to true, which means server selection fails if a topology +scan fails the first time (i.e. it will not make repeat attempts until serverSelectionTimeoutMS expires). This behavior +is overridden since there may be a small delay between spawning mongocryptd (which the driver may be responsible for) +and for mongocryptd to listen on sockets. See the Server Selection spec description of +[serverSelectionTryOnce](../server-selection/server-selection.md#serverselectiontryonce). + +Similarly, single threaded clients will by default wait for 5 second cooldown period after failing to connect to a +server before making another attempt. Meaning if the first attempt to mongocryptd fails to connect, then the user would +observe a 5 second delay. This is not configurable in the URI, so this must be overridden internally. Since mongocryptd +is a local process, there should only be a very short delay after spawning mongocryptd for it to start listening on +sockets. See the SDAM spec description of +[cooldownMS](../server-discovery-and-monitoring/server-monitoring.md#cooldownms). + +Because single threaded drivers may exceed `serverSelectionTimeoutMS` by the duration of the topology scan, +`connectTimeoutMS` is also reduced. + +### What's the deal with metadataClient, keyVaultClient, and the internal client? + +When automatically encrypting a command, the driver runs: + +- a `listCollections` command to determine if the target collection has a remote schema. This uses the `metadataClient`. +- a `find` against the key vault collection to fetch keys. This uses the `keyVaultClient`. + +#### Why not reuse the parent MongoClient when maxPoolSize is limited? + +These operations MUST NOT reuse the same connection pool as the parent `MongoClient` configured with automatic +encryption to avoid possible deadlock situations. + +Drivers supporting a connection pool (see +[CMAP specification](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md)) support an option for +limiting the connection pool size: `maxPoolSize`. + +Drivers need to check out a connection before serializing the command. If the `listCollections` or `find` command during +automatic encryption uses the same connection pool as the parent MongoClient, the application is susceptible to +deadlocks. + +Using the same connection pool causes automatic encryption to check out multiple connections from the pool when +processing a single command. If maxPoolSize=1, this is an immediate deadlock. If maxPoolSize=2, and two threads check +out the first connection, they will deadlock attempting to check out the second. + +#### Why is keyVaultClient an exposed option, but metadataClient private? + +The `keyVaultClient` supports the use case where the key vault collection is stored on a MongoDB cluster separate from +the data-bearing cluster. + +The `metadataClient` is only used for `listCollections` against the data-bearing cluster. + +`listCollections` responses are cached by libmongocrypt for one minute. + +The use pattern of the `metadataClient` will likely greatly differ from the parent `MongoClient`. So it is configured +with `minPoolSize=0`. + +The `metadataClient` is not an exposed option because a user could misconfigure it to point to another MongoDB cluster, +which could be a security risk. + +#### Why is the metadataClient not needed if bypassAutoEncryption=true + +JSON schema data is only needed for automatic encryption but not for automatic decryption. `listCollections` is not run +when `bypassAutoEncryption` is `true`, making a metadataClient unnecessary. + +### Why are commands sent to mongocryptd on collections without encrypted fields? + +If a `MongoClient` is configured with automatic encryption, all commands on collections listed as `AUTOENCRYPT` in +[libmongocrypt: Auto Encryption Allow-List](#libmongocrypt-auto-encryption-allow-list) undergo the automatic encryption +process. Even if the collection does not have an associated schema, the command is sent to mongocryptd as a safeguard. A +collection may not have encrypted fields, but a command on the collection may could have sensitive data as part of the +command arguments. For example: + +```javascript +db.publicData.aggregate([ + {$lookup: {from: "privateData", localField: "_id", foreignField: "_id", as: "privateData"}}, + {$match: {"privateData.ssn": "123-45-6789"}}, +]) +``` + +The `publicData` collection does not have encrypted fields, but the `privateData` collection does. mongocryptd rejects +an aggregate with `$lookup` since there is no mechanism to determine encrypted fields of joined collections. + +### Why do KMS providers require TLS options? + +Drivers authenticate to KMIP servers with the client certificate presented in TLS connections. + +This specification assumes that TLS connections to KMIP servers may require different TLS options than TLS connections +to MongoDB servers. + +KMIP support in the MongoDB server is a precedent. The server supports `--kmipServerCAFile` and +`--kmipClientCertificateFile` to configure the encrypted storage engine KMIP. See +. + +TLS options may be useful for the AWS, Azure, and GCP KMS providers in a case where the default trust store does not +include the needed CA certificates. + +### Why is it an error to have an CSFLE and Queryable Encryption field in the same collection? + +There is no technical limitation to having a separate CSFLE field and Queryable Encryption field in the same collection. +Prohibiting CSFLE and Queryable Encryption in the same collection reduces complexity. From the product perspective, a +random CSFLE field and a non-queryable Queryable Encryption field have the same behavior and similar security +guarantees. A deterministic CSFLE field leaks more information then a deterministic Queryable Encryption field. There is +not a compelling use case to use both CSFLE and Queryable Encryption in the same collection. + +### Is it an error to set schemaMap and encryptedFieldsMap? + +No. CSFLE and Queryable Encryption fields can coexist in different collections. The same collection cannot be in the +`encryptedFieldsMap` and `schemaMap`. [libmongocrypt](#libmongocrypt) will error if the same collection is specified in +a `schemaMap` and `encryptedFieldsMap`. + +### Why is bypassQueryAnalysis needed? + +Commands containing payloads for encrypted indexed fields require a top-level "encryptionInformation" field for the +server processing. `bypassQueryAnalysis` enables the use case of Explicit Encryption without the MongoDB Enterprise +Advanced licensed [crypt_shared](#crypt_shared) library or mongocryptd process. + +Here is an example: + +```go +// No MongoDB Enterprise Advanced licensed 'crypt_shared' shared library. +aeo := options.AutoEncryption(). + SetKeyVaultNamespace("keyvault.datakeys"). + SetEncryptedFieldsMap(efcMap). + SetKmsProviders(kmsProviders). + SetBypassQueryAnalysis(true) + +co := options.Client(). + ApplyURI(uri). + SetAutoEncryptionOptions(aeo) + +encryptedClient, err := mongo.Connect(ctx, co) +defer encryptedClient.Disconnect(ctx) +if err != nil { + log.Fatalf("error in Connect: %v", err) +} + +coll := encryptedClient.Database("foo").Collection("bar") +// Explicit Encrypt an Queryable Encryption Indexed Field. +eo := options.Encrypt(). + SetEncryptIndexType(options.EncryptIndexEquality) +ciphertext, err := ce.Encrypt(ctx, val, eo) +// In InsertOne, libmongocrypt appends "encryptionInformation" to the insert command. +_, err = coll.InsertOne(ctx, bson.D{{"encryptedIndexed", ciphertext}}) +if err != nil { + log.Fatalf("error in InsertOne: %v", err) +} +``` + +A rejected alternative to adding `bypassQueryAnalysis` is to change the behavior of `bypassAutoEncryption`. +`bypassQueryAnalysis` is distinct from `bypassAutoEncryption`. `bypassAutoEncryption` bypasses all of libmongocrypt for +commands. Changing the behavior of `bypassAutoEncryption` could harm performance (e.g. by serializing as smaller +documents). + +### Why does rewrapManyDataKey return RewrapManyDataKeyResult instead of BulkWriteResult? + +`rewrapManyDataKey` is risky as it performs both a find and an update on the key vault collection. Using +`RewrapManyDataKeyResult` allows new fields to be added in the future and more easily deprecate the wrapped +`BulkWriteResult` if necessary. + +### Why does ClientEncryption have key management functions when Drivers can use existing CRUD operations instead? + +The key management functions are added for parity with mongosh to reduce friction between conducting operations using +mongosh and a Driver. Their inclusion assumes their value for users outweighs their cost of implementation and +maintenance. + +### Why are the QueryType and Algorithm options a String? + +Using an enum to represent QueryType was considered and rejected in +[DRIVERS-2352](https://jira.mongodb.org/browse/DRIVERS-2352). + +A string value helps with future compatibility. When new values of QueryType and IndexType are added in libmongocrypt, +users would only need to upgrade libmongocrypt, and not the driver, to use the new values. + +### Why is there an encryptExpression helper? + +Querying a range index requires encrypting a lower bound (value for `$gt` or `$gte`) and upper bound (value for `$lt` or +`$lte`) payload. A rejected alternative API is to encrypt the lower and upper bound payloads separately. The lower and +upper bound payloads must have a unique matching UUID. The lower and upper bound payloads are unique. This API requires +handling the UUID and distinguishing the upper and lower bounds. Here are examples showing possible errors: + +```javascript +uuid = UUID() +lOpts = EncryptOpts( + keyId=keyId, algorithm="range", queryType="range", uuid=uuid, bound="lower") +lower = clientEncryption.encrypt (value=30, lOpts) +uOpts = EncryptOpts( + keyId=keyId, algorithm="range", queryType="range", uuid=uuid, bound="upper") +upper = clientEncryption.encrypt (value=40, uOpts) + +# Both bounds match UUID ... OK +db.coll.find_one ({"age": {"$gt": lower, "$lt": upper }}) + +# Upper bound is used as a lower bound ... ERROR! +db.coll.find_one ({"age": {"$gt": upper }}) + +lower2 = clientEncryption.encrypt (value=35, lOpts) + +# UUID is re-used ... ERROR! +db.coll.find_one ({ "$or": [ + {"age": {"$gt": lower, "$lt": upper }}, + {"age": {"$gt": lower2 }} +]}) + +# UUID does not match between lower and upper bound ... ERROR! +db.coll.find_one ({ "age": {"$gt": lower2, "$lt": upper }}) +``` + +Requiring an Aggregate Expression or Match Expression hides the UUID and handles both bounds. + +Returning an Aggregate Expression or Match Expression as a BSON document motivated adding a new +`ClientEncryption.encryptExpression()` helper. `ClientEncryption.encrypt()` cannot be reused since it returns a Binary. + +To limit scope, only `$and` is supported. Support for other operators `($eq, $in)` can be added in the future if +desired. + +### Why do on-demand KMS credentials not support named KMS providers? + +libmongocrypt supports supplying KMS providers credentials on-demand. This enables obtaining credentials from the host +machine. Supporting on-demand credentials was added as part of these projects: +[DRIVERS-2280](https://jira.mongodb.org/browse/DRIVERS-2280) ("aws"), +[DRIVERS-2377](https://jira.mongodb.org/browse/DRIVERS-2377) ("gcp"), +[DRIVERS-2411](https://jira.mongodb.org/browse/DRIVERS-2411) ("azure"). + +On-demand credentials are primarily intended to support passing credentials assigned in an environment. Supporting +on-demand credentials for more than one KMS provider of the same type is not expected to be useful. Supporting on-demand +KMS credentials would require added work in drivers inspecting the KMS providers when obtaining credentials, as well as +additional test coverage. Supporting on-demand KMS credentials for named KMS providers can be considered as future work +if needed. + +### What is the KMIP `delegated` option? + +By default, the KMS will retrieve the key encryption key from the KMIP server and use it to encrypt the data key. If the +`delegated` option is set to true (recommended), the KMIP server will instead perform encryption and decryption locally, +ensuring that the key encryption key never leaves the server. + +## Future work + +### Make libmonogocrypt cache window configurable + +There's a principle at MongoDB, "no knobs", that we should honor wherever possible. Configurability is bad, mandating +one well-chosen value is good. But if our default caching behavior is discovered unsuitable for some use cases we may +add configuration as part of future work. + +### APM events for encryption or key service interaction + +APM events include the encrypted data before it is sent to the server, or before it is decrypted in a reply. Therefore, +app developers can determine whether or not encryption occurred by inspecting the values in their command monitoring +events. However, it might be useful to offer separate "encryption" and "decryption" events, as well as interactions with +the key service. + +### Remove mongocryptd + +A future version plans to remove the [mongocryptd](#mongocryptd) process and fold the logic into `libmongocrypt` using +the [crypt_shared](#crypt_shared) library. Therefore, this spec mandates that drivers use `libmongocrypt` to abstract +encryption logic, deduplicate work, and to provide a simpler future path to removing [mongocryptd](#mongocryptd). + +### Support external key vault collection discovery + +The only way to configure an external key vault collection is by passing a MongoClient. + +For apps like Compass, where it may not be possible for users to configure this app side, there should ideally be enough +information in the database to decrypt data. (Excluding KMS credentials, which are still passed as MongoClient options). + +We may want to store a URI to the external key vault collection somewhere in the data bearing cluster, so clients can +connect to the external key vault collection without additional user supplied configuration. + +### Batch listCollections requests on expired schema cache entries + +Currently libmongocrypt will refresh schemas one at a time. + +### Add a maximum size for the JSONSchema/key cache. + +They're unbounded currently. + +### Recalculate message size bounds dynamically + +Instead of using one reduced maxMessageSizeBytes, libmongocrypt could hide the complexity of properly resplitting bulk +writes after encryption. It could use a simple back-off algorithm: try marking a command with maxMessageSizeBytes=24MB. +If after marking we determine that's too large, try again with maxMessageSizeBytes=12MB and so on. And in the end +libmongocrypt would create multiple OP_MSGs to send. + +### Support sessions in key management functions + +Key management functions currently assume there are no concurrent accesses of the key vault collection being operated +on. To support concurrent access of the key vault collection, the key management functions may be overloaded to take an +explicit session parameter as described in the [Drivers Sessions Specification](../sessions/driver-sessions.md). + +## Changelog + +- 2026-06-22: Add stable support for substring queries + +- 2026-06-17: Restore `prefixPreview` and `suffixPreview` as experimental. + +- 2026-06-16: Update tests in response to server-side validation of payloads + ([SERVER-91887](https://jira.mongodb.org/browse/SERVER-91887)) + +- 2026-05-29: Add stable support for prefix and suffix queries + + - Replace `prefixPreview` with `prefix`. + - Replace `suffixPreview` with `suffix`. + - Replace `TextPreview` with `String`. + - Replace `TextOpts` with `StringOpts`. + +- 2025-09-30: Update `$lookup` prose test to reflect changes in + [MONGOCRYPT-793](https://jira.mongodb.org/browse/MONGOCRYPT-793). + +- 2025-08-06: Add `TextPreview` algorithm. + +- 2024-02-19: Add custom options AWS credential provider. + +- 2024-10-09: Add retry prose test. + +- 2024-07-29: Document range as stable. + +- 2024-07-22: Make `trimFactor` and `sparsity` optional. + +- 2024-06-13: Document range as unstable. + +- 2024-05-31: Replace rangePreview with range. + +- 2024-03-20: Add `delegated` option to "kmip" KMS provider + +- 2024-02-27: Migrated from reStructuredText to Markdown. + +- 2024-01-10: Add named KMS providers + +- 2023-03-30: Remove ECC collection + +- 2023-02-01: Replace `DataKeyOpts` with `masterKey` in `createEncryptedCollection`. + +- 2023-01-31: `createEncryptedCollection` does not check AutoEncryptionOptions for the encryptedFieldsMap. + +- 2023-01-30: Return `encryptedFields` on `CreateCollection` error. + +- 2023-01-26: Use [GetEncryptedFields](#GetEncryptedFields) in more helpers. + +- 2022-11-30: Add `Range`. + +- 2022-11-28: Permit `tlsDisableOCSPEndpointCheck` in KMS TLS options. + +- 2022-11-27: Fix typo for references to `cryptSharedLibRequired` option. + +- 2022-11-10: Defined a `CreateEncryptedCollection` helper for creating new encryption keys automatically for the + queryable encrypted fields in a new collection. + +- 2022-11-07: Reformat changelog. + +- 2022-11-04: Permit global cache for Azure credentials. + +- 2022-10-26: Do not connect to `mongocryptd` if shared library is loaded. + +- 2022-10-11: Specify a timeout on Azure IMDS HTTP requests and fix the resource URL + +- 2022-10-05: Remove spec front matter and `versionadded` RST macros (since spec version was removed) + +- 2022-09-26: Add behavior for automatic Azure KeyVault credentials for `kmsProviders`. + +- 2022-09-09: Prohibit `rewrapManyDataKey` with libmongocrypt \<= 1.5.1. + +- 2022-07-20: Add behavior for automatic GCP credential loading in `kmsProviders`. + +- 2022-06-30: Add behavior for automatic AWS credential loading in `kmsProviders`. + +- 2022-06-29: Clarify bulk write operation expectations for `rewrapManyDataKey()`. + +- 2022-06-27: Remove `createKey`. + +- 2022-06-24: Clean up kmsProviders to use more TypeScript-like type definitions. + +- 2022-06-23: Make `RewrapManyDataKeyResult.bulkWriteResult` optional. + +- 2022-06-16: Change `QueryType` to a string. + +- 2022-06-15: Clarify description of date fields in key documents. + +- 2022-06-08: Add `Queryable Encryption` to abstract. + +- 2022-06-02: Rename `FLE 2` to `Queryable Encryption` + +- 2022-05-31: Rename `csfle` to `crypt_shared` + +- 2022-05-27: Define ECC, ECOC, and ESC acronyms within encryptedFields + +- 2022-05-26: Clarify how `encryptedFields` interacts with `create` and `drop` commands + +- 2022-05-24: Add key management API functions + +- 2022-05-18: Add createKey and rewrapManyDataKey + +- 2022-05-11: Update create state collections to use clustered collections. Drop data collection after state collection. + +- 2022-05-03: Add queryType, contentionFactor, and "Indexed" and "Unindexed" to algorithm. + +- 2022-04-29: Add bypassQueryAnalysis option + +- 2022-04-11: Document the usage of the new `csfle` library (Note: Later renamed to `crypt_shared`) + +- 2022-02-24: Rename Versioned API to Stable API + +- 2022-01-19: Require that timeouts be applied per the CSOT spec + +- 2021-11-04: Add "kmip" KMS provider + +- 2021-04-08: Updated to use hello and legacy hello + +- 2021-01-22: Add sessionToken option to "aws" KMS provider + +- 2020-12-12: Add metadataClient option and internal client + +- 2020-10-19: Add "azure" and "gcp" KMS providers + +- 2019-10-11: Add "endpoint" to AWS masterkey + +- 2019-12-17: Clarified `bypassAutoEncryption` and managing `mongocryptd` diff --git a/source/client-side-encryption/client-side-encryption.rst b/source/client-side-encryption/client-side-encryption.rst new file mode 100644 index 0000000000..ee7393c567 --- /dev/null +++ b/source/client-side-encryption/client-side-encryption.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `client-side-encryption.md `_. diff --git a/source/client-side-encryption/corpus/corpus-encrypted.json b/source/client-side-encryption/corpus/corpus-encrypted.json new file mode 100644 index 0000000000..1b72aa8a39 --- /dev/null +++ b/source/client-side-encryption/corpus/corpus-encrypted.json @@ -0,0 +1,9515 @@ +{ + "_id": "client_side_encryption_corpus", + "altname_aws": "aws", + "altname_local": "local", + "aws_double_rand_auto_id": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAABchrWPF5OPeuFpk4tUV325TmoNpGW+L5iPSXcLQIr319WJFIp3EDy5QiAHBfz2rThI7imU4eLXndIUrsjM0S/vg==", + "subType": "06" + } + } + }, + "aws_double_rand_auto_altname": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAABga5hXFiFvH/wOr0wOHSHFWRZ4pEs/UCC1XJWf46Dod3GY9Ry5j1ZyzeHueJxc4Ym5M8UHKSmJuXmNo9m9ZnkiA==", + "subType": "06" + } + } + }, + "aws_double_rand_explicit_id": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAABjTYZbsro/YxLWBb88qPXEIDQdzY7UZyK4UaZZ8h62OTxp43Zp9j6WvOEzKhXt4oJPMxlAxyTdqO6MllX5bsDrw==", + "subType": "06" + } + } + }, + "aws_double_rand_explicit_altname": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAABqkyXdeS3aWH2tRFoKxsIIL3ZH05gkiAEbutrjrdfw0b110iPhuCCOb0gP/nX/NRNCg1kCFZ543Vu0xZ0BRXlvQ==", + "subType": "06" + } + } + }, + "aws_double_det_explicit_id": { + "kms": "aws", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$numberDouble": "1.234" } + }, + "aws_double_det_explicit_altname": { + "kms": "aws", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$numberDouble": "1.234" } + }, + "aws_string_rand_auto_id": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAACAsI5E0rVT8TpIONY3TnbRvIxUjKsiy9ynVd/fE7U1lndE7KR6dTzs8QWK13kdKxO+njKPeC2ObBX904QmJ65Sw==", + "subType": "06" + } + } + }, + "aws_string_rand_auto_altname": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAACgBE6J6MRxPSDe+gfJPL8nBvuEIRBYxNS/73LqBTDJYyN/lsHQ6UlFDT5B4EkIPmHPTe+UBMOhZQ1bsP+DK8Aog==", + "subType": "06" + } + } + }, + "aws_string_rand_explicit_id": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAACbdTVDBWn35M5caKZgLFoiSVeFGKRj5K/QtupKNc8/dPIyCE+/a4PU51G/YIzFpYmp91nLpyq7lD/eJ/V0q66Zw==", + "subType": "06" + } + } + }, + "aws_string_rand_explicit_altname": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAACa4O+kE2BaqM0E+yiBrbCuE0YEGTrZ7L/+SuWm9gN3UupxwAQpRfxXAuUCTc9u1CXnvL+ga+VJMcWD2bawnn/Rg==", + "subType": "06" + } + } + }, + "aws_string_det_auto_id": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", + "subType": "06" + } + } + }, + "aws_string_det_explicit_id": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", + "subType": "06" + } + } + }, + "aws_string_det_explicit_altname": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", + "subType": "06" + } + } + }, + "aws_object_rand_auto_id": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAADI+/afY6Eka8j1VNThWIeGkDZ7vo4/l66a01Z+lVUFFnVLeUV/nz9kM6uTTplNRUa+RXmNmwkoR/BHRnGc7wRNA==", + "subType": "06" + } + } + }, + "aws_object_rand_auto_altname": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAADzN4hVXWXKerhggRRtwWnDu2W2wQ5KIWb/X1WCZJKTjQSQ5LNHVasabBCa4U1q46PQ5pDDM1PkVjW6o+zzl/4xw==", + "subType": "06" + } + } + }, + "aws_object_rand_explicit_id": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAADhSs5zKFMuuux3fqFFuPito3N+bp5TgmkUtJtFXjmA/EnLuexGARvEeGUsMJ/n0VzKbbsiE8+AsUNY3o9YXutqQ==", + "subType": "06" + } + } + }, + "aws_object_rand_explicit_altname": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAADpj8MSov16h26bFDrHepsNkW+tOLOjRP7oj1Tnj75qZ+uqxxVkQ5B/t/Ihk5fikHTJGAcRBR5Vv6kJ/ulMaDnvQ==", + "subType": "06" + } + } + }, + "aws_object_det_explicit_id": { + "kms": "aws", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "x": { "$numberInt": "1" } } + }, + "aws_object_det_explicit_altname": { + "kms": "aws", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "x": { "$numberInt": "1" } } + }, + "aws_array_rand_auto_id": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAETWDOZ6zV39H2+W+BkwZIoxI3BNF6phKoiBZ9+i4T9uEoyU3TmoTPjuI0YNwR1v/p5/9rlVCG0KLZd16eeMb3zxZXjqh6IAJqfhsBQ7bzBYI=", + "subType": "06" + } + } + }, + "aws_array_rand_auto_altname": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAE1xeHbld2JjUiPB1k+xMZuIzNSai7mv1iusCswxKEfYCZ7YtR0GDQTxN4676CwhcodSDiysjgOxSFIGlptKCvl0k46LNq0EGypP9yWBLvdjQ=", + "subType": "06" + } + } + }, + "aws_array_rand_explicit_id": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAEFVa4U2uW65MGihhdOmpZFgnwGTs3VeN5TXXbXJ5cfm0CwXF3EPlzAVjy5WO/+lbvFufpQnIiLH59/kVygmwn+2P9zPNJnSGIJW9gaV8Vye8=", + "subType": "06" + } + } + }, + "aws_array_rand_explicit_altname": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAE11VXbfg7DJQ5/CB9XdBO0hCrxOkK3RrEjPGJ0FXlUo76IMna1uo+NVmDnM63CRlGE3/TEbZPpp0w0jn4vZLKvBmGr7o7WQusRY4jnRf5oH4=", + "subType": "06" + } + } + }, + "aws_array_det_explicit_id": { + "kms": "aws", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { "$numberInt": "1" }, + { "$numberInt": "2" }, + { "$numberInt": "3" } + ] + }, + "aws_array_det_explicit_altname": { + "kms": "aws", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { "$numberInt": "1" }, + { "$numberInt": "2" }, + { "$numberInt": "3" } + ] + }, + "aws_binData=00_rand_auto_id": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFpZYSktIHzGLZ6mcBFxywICqxdurqLVJcQR34ngix5YIOOulCYEhBSDzzSEyixEPCuU6cEzeuafpZRHX4qgcr9Q==", + "subType": "06" + } + } + }, + "aws_binData=00_rand_auto_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFshzESR9SyR++9r2yeaEjJYScMDez414s8pZkB3C8ihDa+rsyaxNy4yrF7qNEWjFrdFaH7zD2LdlPx+TKZgROlg==", + "subType": "06" + } + } + }, + "aws_binData=00_rand_explicit_id": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFpYwZRPDom7qyAe5WW/QNSq97/OYgRT8xUEaaR5pkbQEFd/Cwtl8Aib/3Bs1CT3MVaHVWna2u5Gcc4s/v18zLhg==", + "subType": "06" + } + } + }, + "aws_binData=00_rand_explicit_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFBq1RIU1YGHKAS1SAtS42fKtQBHQ/BCQzRutirNdvWlrXxF81LSaS7QgQyycZ2ePiOLsSm2vZS4xaQETeCgRC4g==", + "subType": "06" + } + } + }, + "aws_binData=00_det_auto_id": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", + "subType": "06" + } + } + }, + "aws_binData=00_det_explicit_id": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", + "subType": "06" + } + } + }, + "aws_binData=00_det_explicit_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", + "subType": "06" + } + } + }, + "aws_binData=04_rand_auto_id": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFM5685zqlM8pc3xubtCFuf724g/bWXsebpNzw5E5HrxUqSBBVOvjs3IJH74+Supe169qejY358nOG41mLZvO2wJByvT14qmgUGpgBaLaxPR0=", + "subType": "06" + } + } + }, + "aws_binData=04_rand_auto_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFfLqOzpfjz/XYHDLnliUAA5ehi6s+OIjvrLa59ubqEf8DuoCEWlO13Dl8X42IBB4hoSsO2RUeWtc9MeH4SdIUh/xJN3qS7qzjh/H+GvZRdAM=", + "subType": "06" + } + } + }, + "aws_binData=04_rand_explicit_id": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFkmKfKAbz9tqVaiM9MRhYttiY3vgDwXpdYLQ4uUgWX89KRayLADWortYL+Oq+roFhO3oiwB9vjeWGIdgbj5wSh/50JT/2Gs85TXFe1GFjfWs=", + "subType": "06" + } + } + }, + "aws_binData=04_rand_explicit_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAFKbufv83ddN+07Q5Ocq0VxUEV+BesSrVM7Bol3cMlWjHi7P+MrdwhNEa94xlxlDwU3b+RD6kW+AuNEQ2byA3CX2JjZE1gHwN7l0ukXuqpD0A=", + "subType": "06" + } + } + }, + "aws_binData=04_det_auto_id": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", + "subType": "06" + } + } + }, + "aws_binData=04_det_explicit_id": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", + "subType": "06" + } + } + }, + "aws_binData=04_det_explicit_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", + "subType": "06" + } + } + }, + "aws_undefined_rand_explicit_id": { + "kms": "aws", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$undefined": true } + }, + "aws_undefined_rand_explicit_altname": { + "kms": "aws", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$undefined": true } + }, + "aws_undefined_det_explicit_id": { + "kms": "aws", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$undefined": true } + }, + "aws_undefined_det_explicit_altname": { + "kms": "aws", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$undefined": true } + }, + "aws_objectId_rand_auto_id": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAHASE+V+LlkmwgF9QNjBK8QBvC973NaTMk6wbd57VB2EpQzrgxMtR5gYzVeqq4xaaHqrncyZCOIxDJkFlaim2NqA==", + "subType": "06" + } + } + }, + "aws_objectId_rand_auto_altname": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAHf/+9Qj/ozcDoUb8RNBnajU1d9hJ/6fE17IEZnw+ma6v5yH8LqZk9w3dtm6Sfw1unMhcMKrmIgs6kxqRWhNREJg==", + "subType": "06" + } + } + }, + "aws_objectId_rand_explicit_id": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAHzX8ejVLhoarQ5xgWsJitU/9eBm/Hlt2IIbZtS0SBc80qzkkWTaP9Zl9wrILH/Hwwx8RFnts855eKII3NJFa3BA==", + "subType": "06" + } + } + }, + "aws_objectId_rand_explicit_altname": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAHG5l6nUCY8f/6xO6TsPDrZHcdPRyMe3muMlY2DxHwv9GJNDR5Ne5VEAzUjnbgoy+B29SX4oY8cXJ6XhVz8mt3Eg==", + "subType": "06" + } + } + }, + "aws_objectId_det_auto_id": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", + "subType": "06" + } + } + }, + "aws_objectId_det_explicit_id": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", + "subType": "06" + } + } + }, + "aws_objectId_det_explicit_altname": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", + "subType": "06" + } + } + }, + "aws_bool_rand_auto_id": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAISm4UFt1HC2j0ObpTBg7SvF2Dq31i9To2ED4F3JcTihhq0fVzaSCsUz9VTJ0ziHmeNPNdfPPZO6qA/CDEZBO4jg==", + "subType": "06" + } + } + }, + "aws_bool_rand_auto_altname": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAIj93KeAa96DmZXdB8boFvW19jhJSMmtSs5ag5FDSkH8MdKG2d2VoBOdUlBrL+LHYELqeDHCszY7qCirvb5mIgZg==", + "subType": "06" + } + } + }, + "aws_bool_rand_explicit_id": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAIMbDFEuHIl5MNEsWnYLIand1vpK6EMv7Mso6qxrN4wHSVVwmxK+GCPgrKoUQsNuTssFWNCu0IhwrXOagDEfmlxw==", + "subType": "06" + } + } + }, + "aws_bool_rand_explicit_altname": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAIkIaWfmPdxgAV5Rtb6on6T0NGt9GPFDScQD5I/Ch0ngiTCCKceJOjU0ljd3YTgfWRA1p/MlMIV0I5YAWZXKTHlg==", + "subType": "06" + } + } + }, + "aws_bool_det_explicit_id": { + "kms": "aws", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "aws_bool_det_explicit_altname": { + "kms": "aws", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "aws_date_rand_auto_id": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAJz1VG4+QnQXEE+TGu/pzfPugGMVTiC1xnenG1ByRdPvsERVw9WComWl1tb9tt9oblD7H/q0y1+y8HevkDqohB2Q==", + "subType": "06" + } + } + }, + "aws_date_rand_auto_altname": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAJa1kI2mIIYWjf7zjf5dD9+psvAQpjZ3nnsoXA5upcIwEtZaC8bxKKHVpOLOP3rTbvT5EV6vLhXkferGoyaqd/8w==", + "subType": "06" + } + } + }, + "aws_date_rand_explicit_id": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAJ9Q5Xe4UuOLQTUwosk47A6xx40XJcNoICCNtKrHqsUYy0QLCFRc5v4nA0160BVghURizbUtX8iuIp11pnsDyRtA==", + "subType": "06" + } + } + }, + "aws_date_rand_explicit_altname": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAJkHOdUc/4U82wxWJZ0SYABkJjQqNApkH2Iy/5S+PoatPgynoeSFTU9FmAbuWV/gbtIfBiaCOIjlsdonl/gf9+5w==", + "subType": "06" + } + } + }, + "aws_date_det_auto_id": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", + "subType": "06" + } + } + }, + "aws_date_det_explicit_id": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", + "subType": "06" + } + } + }, + "aws_date_det_explicit_altname": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", + "subType": "06" + } + } + }, + "aws_null_rand_explicit_id": { + "kms": "aws", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "aws_null_rand_explicit_altname": { + "kms": "aws", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "aws_null_det_explicit_id": { + "kms": "aws", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "aws_null_det_explicit_altname": { + "kms": "aws", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "aws_regex_rand_auto_id": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAALnhViSt3HqTDzyLN4mWO9srBU8TjRvPWsAJYfj/5sgI/yFuWdrggMs3Aq6G+K3tRrX3Yb+osy5CLiFCxq9WIvAA==", + "subType": "06" + } + } + }, + "aws_regex_rand_auto_altname": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAALbL2RS2tGQLBZ+6LtXLKAWFKcoKui+u4+gMIlFemLgpdO2eLqrMJB53ccqZImX8ons9UgAwDkiD68hWy8e7KHfg==", + "subType": "06" + } + } + }, + "aws_regex_rand_explicit_id": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAALa0+ftF6W/0Ul4J9VT/3chXFktE1o+OK4S14h2kyOqDVNA8yMKuyCK5nWl1yZvjJ76TuhEABte23oxcBP5QwalQ==", + "subType": "06" + } + } + }, + "aws_regex_rand_explicit_altname": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAALS4Yo9Fwk6OTx2CWdnObFT2L4rHngeIbdCyT4/YMJYd+jLU3mph14M1ptZZg+TBIgSPHq+BkvpRDifbMmOVr/Hg==", + "subType": "06" + } + } + }, + "aws_regex_det_auto_id": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", + "subType": "06" + } + } + }, + "aws_regex_det_explicit_id": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", + "subType": "06" + } + } + }, + "aws_regex_det_explicit_altname": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", + "subType": "06" + } + } + }, + "aws_dbPointer_rand_auto_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAMfCVAnMNbRGsThnoVGb2KDsCIU2ehcPtebk/TFG4GZvEmculscLLih813lEz5NHS2sAXBn721EzUS7d0TKAPbmEYFwUBnijIQIPvUoUO8AQM=", + "subType": "06" + } + } + }, + "aws_dbPointer_rand_auto_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAMvYJ5BtaMLVXV+qj85q5WqKRlzlHOBIIxZfUE/BBXUwqSTpJLdQQD++DDh6F2dtorBeYa3oUv2ef3ImASk5j23joU35Pm3Zt9Ci1pMNGodWs=", + "subType": "06" + } + } + }, + "aws_dbPointer_rand_explicit_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAMdsmYtPDw8kKjfB2kWfx5W1oNEkWWct1lRpesN303pUWsawDJpfBx40lg18So2X/g4yGIwpY3qfEKQZA4vCJeT+MTjhRXFjXA7eS/mxv8f3E=", + "subType": "06" + } + } + }, + "aws_dbPointer_rand_explicit_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAM0hcvS5zmY3mlTp0SfME/rINlflF/sx2KvP0eJTdH+Uk0WHuTkFIJAza+bXvV/gB7iNC350qyzUX3M6NHx/9s/5yBpY8MawTZTZ7WCQIA+ZI=", + "subType": "06" + } + } + }, + "aws_dbPointer_det_auto_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", + "subType": "06" + } + } + }, + "aws_dbPointer_det_explicit_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", + "subType": "06" + } + } + }, + "aws_dbPointer_det_explicit_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", + "subType": "06" + } + } + }, + "aws_javascript_rand_auto_id": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAN3HzAC9BTD7Jgi0PR4RS/Z6L6QtAQ7VhbKRbX+1smmnYniH6jVBM6zyxMDM8h9YjMPNs8EJrGDnisuf33w5KI/A==", + "subType": "06" + } + } + }, + "aws_javascript_rand_auto_altname": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAANJpw+znlu3ecSiNyZ0EerVsow4aDRF2auI3Wy69EVexJkQlHO753PjRn8hG/x2kY8ROy5IUU43jaugP5AN1bwNQ==", + "subType": "06" + } + } + }, + "aws_javascript_rand_explicit_id": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAANzoDiq8uI0+l8COY8YdM9S3rpLvPOHOWmJqJNtOyS0ZXUx1SB5paRJ4W3Eg8KuXEeoFwvBDe9cW9YT66CzkjlBw==", + "subType": "06" + } + } + }, + "aws_javascript_rand_explicit_altname": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAN/JhtRongJweLC5SdrXHhsFz3p82q3cwXf8Sru21DK6S39S997y3uhVLn0xlX5d94PxK1XVYSjz1oVuMxZouZ7Q==", + "subType": "06" + } + } + }, + "aws_javascript_det_auto_id": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", + "subType": "06" + } + } + }, + "aws_javascript_det_explicit_id": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", + "subType": "06" + } + } + }, + "aws_javascript_det_explicit_altname": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", + "subType": "06" + } + } + }, + "aws_symbol_rand_auto_id": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAOBv1T9tleM0xwNe7efg/MlShyzvXe3Pmg1GzPl3gjFRHZGWXR578KqX+8oiz65eXGzNuyOFvcpnR2gYCs3NeKeQfctO5plEiIva6nzCI5SK8=", + "subType": "06" + } + } + }, + "aws_symbol_rand_auto_altname": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAOwLgGws8CMh+GgkEJFAx8tDIflyjsgG+/1FmZZobKAg8NOKqfXjtbnNCbvR28OCk6g/8SqBm8m53G6JciwvthJ0DirdfEexiUqu7IPtaeeyw=", + "subType": "06" + } + } + }, + "aws_symbol_rand_explicit_id": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAORQi3dNkXzZeruWu19kEhDu6fFD/h47ILzk+OVKQMoriAQC5YFyVRp1yAkIaWsrsPcyCHlfZ99FySSQeqSYbZZNj5FqyonWvDuPTduHDy3CI=", + "subType": "06" + } + } + }, + "aws_symbol_rand_explicit_altname": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAOj+Yl1pQPiJ6mESOISOyUYsKN/VIvC8f0derhxIPakXkwn57U0sxv+geUkrl3JZDxY3+cX5M1JZmY+PfjaYQhbTorf9RZaVC2Wwo2lMftWi0=", + "subType": "06" + } + } + }, + "aws_symbol_det_auto_id": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", + "subType": "06" + } + } + }, + "aws_symbol_det_explicit_id": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", + "subType": "06" + } + } + }, + "aws_symbol_det_explicit_altname": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", + "subType": "06" + } + } + }, + "aws_javascriptWithScope_rand_auto_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAPT31GSNkY1RM43miv1XPYtDX1vU/xORiM3U0pumjqA+JLU/HMhH++75OcMhcAQqMjm2nZtZScxdGJsJJPEEzqjbFNMJgYc9sqR5uLnzk+2dg=", + "subType": "06" + } + } + }, + "aws_javascriptWithScope_rand_auto_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAPUxgaKAxSQ1uzOZtzsbtrxtDT2P/zWY6lYsbChXuRUooqvyjXSkNDqKBBA7Gp5BdGiVB/JLR47Tihpbcw1s1yGhwQRvnqeDvPrf91nvElXRY=", + "subType": "06" + } + } + }, + "aws_javascriptWithScope_rand_explicit_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAPv8W0ZtquFCLTG0TqvRjdzKa/4mvqT2FuEGQ0mXG2k2BZh2LY5APr/kgW0tP4eLjHzVld6OLiM9ZKAvENCZ6/fKOvqSwpIfkdLWUIeB4REQg=", + "subType": "06" + } + } + }, + "aws_javascriptWithScope_rand_explicit_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAPMVhWjaxLffdAOkVgIJpjgNIldMS451NQs3C1jb+pzopHp3DlfZ+AHQpK9reMVVKjaqanhWBpL25q+feA60XVgZPCUDroiRYqMFqU//y0amw=", + "subType": "06" + } + } + }, + "aws_javascriptWithScope_det_explicit_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$code": "x=1", "$scope": {} } + }, + "aws_javascriptWithScope_det_explicit_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$code": "x=1", "$scope": {} } + }, + "aws_int_rand_auto_id": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAQFV5b3vsoZe+MT4z8soetpmrWJpm7be41FNu/rdEqHWTG32jCym6762PCNYH5+vA7ldCWQkdt+ncneHsxzPrm9w==", + "subType": "06" + } + } + }, + "aws_int_rand_auto_altname": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAQY9+QenvU1Tk/dEGZP11uOZJLHAJ9hWHbEhxbtxItt1LsdU/8gOZfypilIO5BUkLT/15PUuXV28GISNh6yIuWhw==", + "subType": "06" + } + } + }, + "aws_int_rand_explicit_id": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAQruCugbneumhcinuXm89WW1PXVuSOewttp9cpsPPsCRVqe/uAkZOdJnZ2KaEZ9zki2GeqaJTs1qDmaJofc6GMEA==", + "subType": "06" + } + } + }, + "aws_int_rand_explicit_altname": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAQb15qXl/tejk4pmgkc4pUxzt4eJrv/cetgzgcPVaROAQSzd8ptbgCjaV8vP46uqozRoaDFZbQ06t65c3f0x/Ucw==", + "subType": "06" + } + } + }, + "aws_int_det_auto_id": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", + "subType": "06" + } + } + }, + "aws_int_det_explicit_id": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", + "subType": "06" + } + } + }, + "aws_int_det_explicit_altname": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", + "subType": "06" + } + } + }, + "aws_timestamp_rand_auto_id": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAR63xXG8mrlixkQzD5VBIPE6NHicaWcS5CBhiIJDcZ0x8D9c5TgRJUfCeWhKvWFD4o0DoxcBQ2opPormFDpvmq/g==", + "subType": "06" + } + } + }, + "aws_timestamp_rand_auto_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAARAgY9LsUxP6gP4gYRvvzZ4iaHVQRNbycATiVag1YNSiDmEr4LYserYuBscdrIy4v3zgGaulFM9KV86bx0ItycZA==", + "subType": "06" + } + } + }, + "aws_timestamp_rand_explicit_id": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAARLneAZqPcHdzGGnXz2Ne5E7HP9cDC1+yoIwcA8OSF/IlzEjrrMAi3z6Izol6gWDlD7VOh7QYL3sASJOXyzF1hPQ==", + "subType": "06" + } + } + }, + "aws_timestamp_rand_explicit_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAARH2bU7KNo5SHxiO8JFEcT9wryuHNXyM7ADop1oPcESyay1Nc0WHPD3nr0yMAK481NxOkE3qXyaslu7bcP/744WA==", + "subType": "06" + } + } + }, + "aws_timestamp_det_auto_id": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", + "subType": "06" + } + } + }, + "aws_timestamp_det_explicit_id": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", + "subType": "06" + } + } + }, + "aws_timestamp_det_explicit_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", + "subType": "06" + } + } + }, + "aws_long_rand_auto_id": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAASZbes2EdR78crt2pXVElW2YwAQh8HEBapYYeav2VQeg2syXaV/qZuD8ofnAVn4v/DydTTMVMmK+sVU/TlnAu2eA==", + "subType": "06" + } + } + }, + "aws_long_rand_auto_altname": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAASt+7fmMYH+fLHgybc+sng8/UmKP3YPUEPCz1SXVQljQp6orsCILSgtgGPsdeGnN5NSxh3XzerHs6zlR92fWpZCw==", + "subType": "06" + } + } + }, + "aws_long_rand_explicit_id": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAS01fF1uo6zYDToJnOT/EbDipzk7YZ6I+IspZF+avjU3XYfpRxT9NdAgKr0euWJwyAsdpWqqCwFummfrPeZOy04A==", + "subType": "06" + } + } + }, + "aws_long_rand_explicit_altname": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAS6tpH796bqy58mXf38rJvVtA1uBcxBE5yIGQ4RN44oypc/pvw0ouhFI1dkoneKMtAFU/5RygZV+RvQhRtgKn76A==", + "subType": "06" + } + } + }, + "aws_long_det_auto_id": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", + "subType": "06" + } + } + }, + "aws_long_det_explicit_id": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", + "subType": "06" + } + } + }, + "aws_long_det_explicit_altname": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", + "subType": "06" + } + } + }, + "aws_decimal_rand_auto_id": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAATgf5zW9EgnWHPxj4HAGt472eN9UXP41TaF8V2J7S2zqSpiBZGKDuOIjw2FBSqaNp53vvfl9HpwAuQBJZhrwkBCKRkKV/AAR3/pTpuoqhSKaM=", + "subType": "06" + } + } + }, + "aws_decimal_rand_auto_altname": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAATPRfvZWdupE2N0W1DXUx7X8Zz7g43jawJL7PbQtTYetI78xRETkMdygwSEHgs+cvnUBBtYIeKRVkOGZQkwf568OclhDiPxUeD38cR5blBq/U=", + "subType": "06" + } + } + }, + "aws_decimal_rand_explicit_id": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAAT+ZnCg2lSMIohZ9RJ4CNs3LZ0g+nV04cYAmrxTSrTSBPGlZ7Ywh5A2rCss7AUijYZiKiYyZbuAzukbOuVRhdCtm+xo9+DyLAwTezF18okk6Y=", + "subType": "06" + } + } + }, + "aws_decimal_rand_explicit_altname": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AgFkgAAAAAAAAAAAAAAAAAATlnQYASsTZRRHzFjcbCClXartcXBVRrYv7JImMkDmAj6EAjf/ZqpjeykkS/wohMhXaNwyZBdREr+n+GDV7imYoL4WRBOLnqB6hrYidlWqNzE=", + "subType": "06" + } + } + }, + "aws_decimal_det_explicit_id": { + "kms": "aws", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$numberDecimal": "1.234" } + }, + "aws_decimal_det_explicit_altname": { + "kms": "aws", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$numberDecimal": "1.234" } + }, + "aws_minKey_rand_explicit_id": { + "kms": "aws", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$minKey": 1 } + }, + "aws_minKey_rand_explicit_altname": { + "kms": "aws", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$minKey": 1 } + }, + "aws_minKey_det_explicit_id": { + "kms": "aws", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$minKey": 1 } + }, + "aws_minKey_det_explicit_altname": { + "kms": "aws", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$minKey": 1 } + }, + "aws_maxKey_rand_explicit_id": { + "kms": "aws", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "aws_maxKey_rand_explicit_altname": { + "kms": "aws", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "aws_maxKey_det_explicit_id": { + "kms": "aws", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "aws_maxKey_det_explicit_altname": { + "kms": "aws", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "local_double_rand_auto_id": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAABGF195CB8nRmK9+KxYO7T96MeXucC/ILQtEEQAS4zrwj3Qz7YEQrf/apvbKTCkn3siN2XSDLQ/7dmddZa9xa9yQ==", + "subType": "06" + } + } + }, + "local_double_rand_auto_altname": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAABY8g18z6ZOjGtfNxaAmU95tXMdoM6qbtDMpB72paqiHZTW1UGB22HPXiEnVz05JTBzzX4fc6tOldX6aJel812Zg==", + "subType": "06" + } + } + }, + "local_double_rand_explicit_id": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAABDlHwN8hYyScEhhx64TdJ2Qp2rmKRg8983zdqIL1914tyPwRQq7ySCOhmFif2S7v4KT+r0uOfimYvKD1n9rKHlg==", + "subType": "06" + } + } + }, + "local_double_rand_explicit_altname": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAB2VnTFlaCRzAZZTQiMWQORFNgXIuAJlHJXIHiYow2eO6JbVghWTpH+MsdafBNPVnc0zKuZBL0Qs2Nuk1xiQaqhA==", + "subType": "06" + } + } + }, + "local_double_det_explicit_id": { + "kms": "local", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$numberDouble": "1.234" } + }, + "local_double_det_explicit_altname": { + "kms": "local", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$numberDouble": "1.234" } + }, + "local_string_rand_auto_id": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAC5NBAPM8q2n9fnkwQfE9so/XcO51plPBNs5VlBRbDw68k9T6/uZ2TWsAvTYtVooY59zHHr2QS3usKbGQB6J61rA==", + "subType": "06" + } + } + }, + "local_string_rand_auto_altname": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACM/EjGMrkYHvSZra26m74upuvLkfKXTs+tTWquGzrgWYLnLt8I6XBIwx1VymS9EybrCU/ewmtgjLUNUFQacIeXA==", + "subType": "06" + } + } + }, + "local_string_rand_explicit_id": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACn4tD26UG8lO9gTZaxen6yXzHo/a2lokeY1ClxHMtJODoJr2JZzIDHP3A9aZ8L4+Vu+nyqphaWyGaGONKu8gpcQ==", + "subType": "06" + } + } + }, + "local_string_rand_explicit_altname": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACZfoO2LjY+IB31FZ1Tq7pHr0DCFKGJqWcXcOrnZ7bV9Euc9f101motJc31sp8nF5CTCfd83VQE0319eQrxDDaSw==", + "subType": "06" + } + } + }, + "local_string_det_auto_id": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", + "subType": "06" + } + } + }, + "local_string_det_explicit_id": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", + "subType": "06" + } + } + }, + "local_string_det_explicit_altname": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", + "subType": "06" + } + } + }, + "local_object_rand_auto_id": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAADlekcUsETAkkKTjCVx5EISJN+sftrQax/VhaWXLyRgRz97adXXmwZkMyt+035SHZsF91i2LaXziMA4RHoP+nKFw==", + "subType": "06" + } + } + }, + "local_object_rand_auto_altname": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAADpaQmy5r6q9gLqEm+FIi/OyQgcuUnrICCP9rC4S3wR6qUHd82IW/3dFQUzwTkaXxgStjopamQMuZ4ESRj0xx0bA==", + "subType": "06" + } + } + }, + "local_object_rand_explicit_id": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAADCHRJCINzWY0u4gZPWEmHg/JoQ8IW4yMfUyzYJCQrEMp4rUeupIuxqSuq2QyLBYZBBv0r7t3lNH49I5qDeav2vA==", + "subType": "06" + } + } + }, + "local_object_rand_explicit_altname": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAADrHQQUnLF1jdNmFY/V266cS28XAB4nOKetHAcSbwkeUxNzgZT1g+XMQaYfcNMMv/ywypKU1KpgLMsEOpm4qcPkQ==", + "subType": "06" + } + } + }, + "local_object_det_explicit_id": { + "kms": "local", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "x": { "$numberInt": "1" } } + }, + "local_object_det_explicit_altname": { + "kms": "local", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "x": { "$numberInt": "1" } } + }, + "local_array_rand_auto_id": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAEXa7bQ5vGPNsLdklM/H+sop8aCL4vlDiVUoVjTAGjTngn2WLcdKLWxaNSyMdJpsI/NsxQJ58YrcwP+yHzi9rZVtRdbg7m8p+CYcq1vUm6UoQ=", + "subType": "06" + } + } + }, + "local_array_rand_auto_altname": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAEVlZlOvtRmGIhcYi/qPl3HKi/qf0yRQrkbVo9rScYkxDCBN9wA55pAWHDQ/5Sjy4d0DwL57k+M1G9e7xSIrv8xXKwoIuuabhSWaIX2eJHroY=", + "subType": "06" + } + } + }, + "local_array_rand_explicit_id": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAEYBLSYHHt2rohezMF4lMjNdqy9CY33EHf+pgRbJwVXZScLDgn9CcqeRsdU8bW5h2qgNpQvoSMBB7pW+Dgp1RauTHZSOd4PcZpAGjwoFDWSSM=", + "subType": "06" + } + } + }, + "local_array_rand_explicit_altname": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAES1IJ8S2NxWekolQockxLJvzFSGfKQ9Xbi55vO8LyWo0sIG9ZgPQXtVQkZ301CsdFduvx9A0vDqQ0MGYc4plxNnpUTizJPRUDyez5dOgZ9tI=", + "subType": "06" + } + } + }, + "local_array_det_explicit_id": { + "kms": "local", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { "$numberInt": "1" }, + { "$numberInt": "2" }, + { "$numberInt": "3" } + ] + }, + "local_array_det_explicit_altname": { + "kms": "local", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { "$numberInt": "1" }, + { "$numberInt": "2" }, + { "$numberInt": "3" } + ] + }, + "local_binData=00_rand_auto_id": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAF+hgWs4ZCo9GnmhSM9SDSWzWX4E7Tlp4TwlEy3zfO/rrMREECGB4u8LD8Ju9b8YP+xcZhMI1tcz/vrQS87NffUg==", + "subType": "06" + } + } + }, + "local_binData=00_rand_auto_altname": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAFtEvaXWpGfXC1GlKu0AeRDaeBKHryGoS0tAUr48vfYk7umCr+fJKyXCY9vSv7wCiQxWLe8V/EZWkHsu0zqhJw9w==", + "subType": "06" + } + } + }, + "local_binData=00_rand_explicit_id": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAF/1L5bvmMX3Bk2nAw8KvvRd/7nZ82XHVasT0jrlPhSiJU7ehJMeUCOb7HCHU6KgCzZB9C2W3NoVhLKIhE9ZnYdg==", + "subType": "06" + } + } + }, + "local_binData=00_rand_explicit_altname": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAFK0W5IWKzggR4UU+fhwA2p8YCHLfmx5y1OEtHc/9be9eEYTORACDmWY6207Vd4LhBJCedd+Q5qMm7NRZjjhyLEQ==", + "subType": "06" + } + } + }, + "local_binData=00_det_auto_id": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", + "subType": "06" + } + } + }, + "local_binData=00_det_explicit_id": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", + "subType": "06" + } + } + }, + "local_binData=00_det_explicit_altname": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", + "subType": "06" + } + } + }, + "local_binData=04_rand_auto_id": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAFxq38aA4k/tYHPwJFRK0pahlo/3zjCe3VHJRqURRA+04lbJCvdkQTawxWlf8o+3Pcetl1UcPTQigdYp5KbIkstuPstLbT+TZXHVD1os9LTRw=", + "subType": "06" + } + } + }, + "local_binData=04_rand_auto_altname": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAFTXNWchCPmCSY0+AL22/kCBmAoDJDX5T18jpJHLdvZtHs0zwD64b9hLvfRK268BlNu4P37KDFE6LT0QzjG7brqzFJf3ZaadDCKeIw1q7DWQs=", + "subType": "06" + } + } + }, + "local_binData=04_rand_explicit_id": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAF7XgMgKjQmWYWmobrYWKiGYCKsy5kTgVweFBuzvFISaZjFsq2hrZB2DwUaOeT6XUPH/Onrdjc3fNElf3FdQDHif4rt+1lh9jEX+nMbRw9i3s=", + "subType": "06" + } + } + }, + "local_binData=04_rand_explicit_altname": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAFGoA/1H0waFLor6LbkUCLC2Wm9j/ZT7yifPbf0G7WvO0+gBLlffr3aJIQ9ik5vxPbmDDMCoYlbEYgb8i9I5tKC17WPhjVH2N2+4l9y7aEmS4=", + "subType": "06" + } + } + }, + "local_binData=04_det_auto_id": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", + "subType": "06" + } + } + }, + "local_binData=04_det_explicit_id": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", + "subType": "06" + } + } + }, + "local_binData=04_det_explicit_altname": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", + "subType": "06" + } + } + }, + "local_undefined_rand_explicit_id": { + "kms": "local", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$undefined": true } + }, + "local_undefined_rand_explicit_altname": { + "kms": "local", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$undefined": true } + }, + "local_undefined_det_explicit_id": { + "kms": "local", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$undefined": true } + }, + "local_undefined_det_explicit_altname": { + "kms": "local", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$undefined": true } + }, + "local_objectId_rand_auto_id": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAHfvxWRZOzfao3faE3RglL0IcDpBcNwqiGL5KgSokmRxWjjWeiel88Mbo5Plo0SswwNQ2H7C5GVG21L+UbvcW63g==", + "subType": "06" + } + } + }, + "local_objectId_rand_auto_altname": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAHhd9lSOO7bHE7PM+Uxa2v3X1FF66IwyEr0wqnyTaOM+cHQLmec/RlEaRIQ1x2AiW7LwmmVgZ0xBMK9CMh0Lhbyw==", + "subType": "06" + } + } + }, + "local_objectId_rand_explicit_id": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAHETwT9bo+JtboBVW/8GzzMQCpn22iiNJnlxYfyO45jvYJQRs29RRIouCsnFkmC7cfAO3GlVxv113euYjIO7AlAg==", + "subType": "06" + } + } + }, + "local_objectId_rand_explicit_altname": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAHhsguAMBzQUFBAitpJDzKEaMDGUGfvCzmUUhf4rnp8xeall/p91TUudaSMcU11XEgJ0Mym4IbYRd8+TfUai0nvw==", + "subType": "06" + } + } + }, + "local_objectId_det_auto_id": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", + "subType": "06" + } + } + }, + "local_objectId_det_explicit_id": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", + "subType": "06" + } + } + }, + "local_objectId_det_explicit_altname": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", + "subType": "06" + } + } + }, + "local_bool_rand_auto_id": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAIxGld4J/2vSWg5tjQulpkm9C6WeUcLbv2yfKRXPAbmLpv3u4Yrmr5qisJtqmDPTcb993WosvCYAh0UGW+zpsdEg==", + "subType": "06" + } + } + }, + "local_bool_rand_auto_altname": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAIpUFPiS2uoW1Aqs0WQkBa201OBmsuJ8WUKcv5aBPASkcwfaw9qSWs3QrbEDR2GyoU4SeYOByCAQMzXCPoIYAFdQ==", + "subType": "06" + } + } + }, + "local_bool_rand_explicit_id": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAIJuzu1a60meYlU3LMjw/7G4Vh/lqKopxdpGWoLXEmY/NoHgX6Fkv9iTwxv/Nv8rZwtawpFV+mQUG/6A1IHMBASQ==", + "subType": "06" + } + } + }, + "local_bool_rand_explicit_altname": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAIn9VjxL5TdGgJLckNHRrIaL32L31q5OERRZG2M5OYKk66TnrlfEs+ykcDvGwMGKpr/PYjY5kBHDc/oELGJJbWRQ==", + "subType": "06" + } + } + }, + "local_bool_det_explicit_id": { + "kms": "local", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "local_bool_det_explicit_altname": { + "kms": "local", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "local_date_rand_auto_id": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAJPPv4MC5xzt2uxPGBHH9g2z03o9SQjjmuxt97Ub1UcKCCHsGED3bx6YSrocuEMiFFI4d5Fqgl8HNeS4j0PR0tYA==", + "subType": "06" + } + } + }, + "local_date_rand_auto_altname": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAJ6i2A9Hi4xWlOMjFMGpwaRctR1VFnb4El166n18RvjKic46V+WoadvLHS32RhPOvkLVYwIeU4C+vrO5isBNoUdw==", + "subType": "06" + } + } + }, + "local_date_rand_explicit_id": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAJHcniV7Q0C8ZTWrE0hp5i5bUPlrrRdNLZckfODw8XNVtVPDjbznglccQmI7w1t8kOVp65eKzVzUOXN0YkqA+1QA==", + "subType": "06" + } + } + }, + "local_date_rand_explicit_altname": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAJKCUCjC3hsmEKKYwGP3ceh3zR+ArE8LYFOQfN87aEsTr60VrzHXmsE8PvizRhhMnrp07ljzQkuat39L+0QSR2qQ==", + "subType": "06" + } + } + }, + "local_date_det_auto_id": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", + "subType": "06" + } + } + }, + "local_date_det_explicit_id": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", + "subType": "06" + } + } + }, + "local_date_det_explicit_altname": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", + "subType": "06" + } + } + }, + "local_null_rand_explicit_id": { + "kms": "local", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "local_null_rand_explicit_altname": { + "kms": "local", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "local_null_det_explicit_id": { + "kms": "local", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "local_null_det_explicit_altname": { + "kms": "local", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "local_regex_rand_auto_id": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAALXKw7zSgqQj1AKoWO0MoMxsBuu0cMB6KdJQCRKdupoLV/Y22owwsVpDDMv5sgUpkG5YIV+Fz7taHodXE07qHopw==", + "subType": "06" + } + } + }, + "local_regex_rand_auto_altname": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAALntOLXq7VW1+jwba/dSbidMo2bewNo7AtK9A1CPwk9XrjUQaEOQxfRpho3BYQEo2U67fQdsY/tyhaj4jduHn9JQ==", + "subType": "06" + } + } + }, + "local_regex_rand_explicit_id": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAALlMMG2iS/gEOEsVKR7sxBJP2IUzZ+aRbozDSkqADncresBvaPBSE17lng5NG7H1JRCAcP1rH/Te+0CrMd7JpRAQ==", + "subType": "06" + } + } + }, + "local_regex_rand_explicit_altname": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAL1YNnlVu5+njDLxh1LMhIPOH19RykAXhxrUbCy6TI5MLQsAOSgAJbXOTXeKr0D8/Ff0phToWOKl193gOOIp8yZQ==", + "subType": "06" + } + } + }, + "local_regex_det_auto_id": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", + "subType": "06" + } + } + }, + "local_regex_det_explicit_id": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", + "subType": "06" + } + } + }, + "local_regex_det_explicit_altname": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", + "subType": "06" + } + } + }, + "local_dbPointer_rand_auto_id": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAMUdAA9uOSk1tXJVe/CG3Ps6avYTEF1eHj1wSlCHkFxqlMtTO+rIQpikpjH0MrcXvEEdAO8g5hFZ01I7DWyK5AAxTxDqVF+kOaQ2VfKs6hyuo=", + "subType": "06" + } + } + }, + "local_dbPointer_rand_auto_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAMiNqvqLwZrPnsF235z+Obl1K9iEXdJ5GucMGpJdRG4lRvRE0Oy1vh6ztNTpYPY/tXyUFTBWlzl/lITalSEm/dT1Bnlh0iPAFrAiNySf662og=", + "subType": "06" + } + } + }, + "local_dbPointer_rand_explicit_id": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAM+Tn31YcKiowBTJWRYCYAEO7UARDE2/jTVGEKXCpiwEqqP3JSAS0b80zYt8dxo5mVhUo2a02ClKrB8vs+B6sU1kXrahSaVSEHZlRSGN9fWgo=", + "subType": "06" + } + } + }, + "local_dbPointer_rand_explicit_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAMdOZZUvpJIqG9qiOLy5x4BdftyHipPDZn/eeLEc7ir3v4jJsY3dsv6fQERo5U9lMynNGA9PJePVzq5tWsIMX0EcCQcMfGmosfkYDzN1OX99A=", + "subType": "06" + } + } + }, + "local_dbPointer_det_auto_id": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", + "subType": "06" + } + } + }, + "local_dbPointer_det_explicit_id": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", + "subType": "06" + } + } + }, + "local_dbPointer_det_explicit_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", + "subType": "06" + } + } + }, + "local_javascript_rand_auto_id": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAANNL2AMKwTDyMIvxLKhBxZKx50C0tBdkLwuXmuMcrUqZeH8bsvjtttoM9LWkkileMyeTWgxblJ1b+uQ+V+4VT6fA==", + "subType": "06" + } + } + }, + "local_javascript_rand_auto_altname": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAANBjBlHGw3K3TWQHpvfa1z0bKhNnVFC/lZArIexo3wjdGq3MdkGA5cuBIp87HHmOIv6o/pvQ9K74v48RQl+JH44A==", + "subType": "06" + } + } + }, + "local_javascript_rand_explicit_id": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAANjvM7u3vNVyKpyI7g5kbzBpHPzXzOQToDSng5/c9yjMG+qi4TPtOyassobJOnMmDYBLyqRXCl/GsDLprbg5jxuA==", + "subType": "06" + } + } + }, + "local_javascript_rand_explicit_altname": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAANMtO7KneuVx4gSOjX4MQjKL80zJhnt+efDBylkpNsqKyxBXB60nkiredGzwaK3/4QhIfGJrC1fQpwUwu/v1L17g==", + "subType": "06" + } + } + }, + "local_javascript_det_auto_id": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", + "subType": "06" + } + } + }, + "local_javascript_det_explicit_id": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", + "subType": "06" + } + } + }, + "local_javascript_det_explicit_altname": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", + "subType": "06" + } + } + }, + "local_symbol_rand_auto_id": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAOOuO2b23mekwI8b6gWeEgRy1lLOCsNyBKvdmizK7/oOVKCvd+3kwUn9a6TxygooiVAN/Aohr1cjb8jRlMPWpkP0iO0+Tt6+vkizgFsQW4iio=", + "subType": "06" + } + } + }, + "local_symbol_rand_auto_altname": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAOhN4QPOcmGnFKGvTfhz6TQleDA02X6oWULLHTnOUJYfE3OUSyf2ULEQh1yhdKdwXMuYVgGl28pMosiwkBShrXYe5ZlMjiZCIMZWSdUMV0tXk=", + "subType": "06" + } + } + }, + "local_symbol_rand_explicit_id": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAO9aWi9RliwQHdXHoJME9VyN6XgyGd95Eclx+ZFYfLxBGAuUnPNjSfVuNZwYdyKC8JX79+mYhk7IXmcGV4z+4486sxyLk3idi4Kmpz2ESqV5g=", + "subType": "06" + } + } + }, + "local_symbol_rand_explicit_altname": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAO/qev3DPfpkQoSW9aHOyalwfI/VYDQVN5VMINx4kw2vEqHiI1HRdZRPOz3q74TlQEy3TMNMTYdCvh5bpN/PptRZCTQbzP6ugz9dTp79w5/Ok=", + "subType": "06" + } + } + }, + "local_symbol_det_auto_id": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", + "subType": "06" + } + } + }, + "local_symbol_det_explicit_id": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", + "subType": "06" + } + } + }, + "local_symbol_det_explicit_altname": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", + "subType": "06" + } + } + }, + "local_javascriptWithScope_rand_auto_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAP5gLMvLOAc6vGAvC7bGmEC4eweptAiX3A7L0iCoHps/wm0FBLkfpF6F4pCjVYiY1lTID38wliRLPyhntCj+cfvlMfKSjouNgXMIWyQ8GKZ2c=", + "subType": "06" + } + } + }, + "local_javascriptWithScope_rand_auto_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAPVsw9Opn/P5SAdJhX4MTxIcsmaG8isIN4NKPi9k1u/Vj7AVkcxYqwurAghaJpmfoAgMruvzi1hcKvd05yHd9Nk0vkvODwDgnjJB6QO+qUce8=", + "subType": "06" + } + } + }, + "local_javascriptWithScope_rand_explicit_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAPLUa+nsrqiHkVdE5K1xl/ZsiZqQznG2yVXyA3b3loBylbcL2NEBp1JUeGnPZ0y5ZK4AmoL6NMH2Io313rW3V8FTArs/OOQWPRJSe6h0M3wXk=", + "subType": "06" + } + } + }, + "local_javascriptWithScope_rand_explicit_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAPzUKaXCH0JImSlY73HVop9g9c0YssNEiA7Dy7Vji61avxvnuJJfghDchdwwaY7Vc8+0bymoanUWcErRctLzjm+1uKeMnFQokR8wFtnS3PgpQ=", + "subType": "06" + } + } + }, + "local_javascriptWithScope_det_explicit_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$code": "x=1", "$scope": {} } + }, + "local_javascriptWithScope_det_explicit_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$code": "x=1", "$scope": {} } + }, + "local_int_rand_auto_id": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAQHXpXb3KlHA2KFTBgl0VoLCu0CUf1ae4DckkwDorbredVSqxvA5e+NvVudY5yuea6bC9F57JlbjI8NWYAUw4q0Q==", + "subType": "06" + } + } + }, + "local_int_rand_auto_altname": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAQSxXMF4+TKV+a3lcxXky8VepEqdg5wI/jg+C4CAUgNurq2XhgrxyqiMjkU8z07tfyoLYyX6P+dTrwj6nzvvchCw==", + "subType": "06" + } + } + }, + "local_int_rand_explicit_id": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAQmzteYnshCI8HBGd7UYUKvcg4xl6M8PRyi1xX/WHbjyQkAJXxczS8hO91wuqStE3tBNSmulUejz9S691ufTd6ZA==", + "subType": "06" + } + } + }, + "local_int_rand_explicit_altname": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAQLCHLru//++QSoWVEyw2v6TUfCnlrPJXrpLLezWf16vK85jTfm8vJbb2X2UzX04wGzVL9tCFFsWX6Z5gHXhgSBg==", + "subType": "06" + } + } + }, + "local_int_det_auto_id": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", + "subType": "06" + } + } + }, + "local_int_det_explicit_id": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", + "subType": "06" + } + } + }, + "local_int_det_explicit_altname": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", + "subType": "06" + } + } + }, + "local_timestamp_rand_auto_id": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAARntIycg0Xkd16GEa//VSJI4Rkl7dT6MpRa+D3MiTEeio5Yy8zGK0u2BtEP/9MCRQw2hJDYj5znVqwhdduM0OTiA==", + "subType": "06" + } + } + }, + "local_timestamp_rand_auto_altname": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAARWA9Ox5ejDPeWxfjbRgcGCtF/G5yrPMbBJD9ESDFc0NaVe0sdNNTisEVxsSkn7M/S4FCibKh+C8femr7xhu1iTw==", + "subType": "06" + } + } + }, + "local_timestamp_rand_explicit_id": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAARrEfOL4+4Qh7IkhHnHcBEANGfMF8n2wUDnsZ0lXEb0fACKzaN5OKaxMIQBs/3pFBw721qRfCHY+ByKeaQuABbzg==", + "subType": "06" + } + } + }, + "local_timestamp_rand_explicit_altname": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAARW8nwmnBt+LFIAcFWvOzX8llrGcveQKFhyYUIth9d7wtpTyc9myFp8GBQCnjDpKzA6lPmbqVYeLU0L9q0h6SHGQ==", + "subType": "06" + } + } + }, + "local_timestamp_det_auto_id": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", + "subType": "06" + } + } + }, + "local_timestamp_det_explicit_id": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", + "subType": "06" + } + } + }, + "local_timestamp_det_explicit_altname": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", + "subType": "06" + } + } + }, + "local_long_rand_auto_id": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAASrinKUOpHIB7MNRmCAPWcP4CjZwfr5JaRT3G/GqY9B/6csj3+N9jmo1fYvM8uHcnmf5hzDDOamaE2FF1jDKkrHw==", + "subType": "06" + } + } + }, + "local_long_rand_auto_altname": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAShWMPYDkCpTC2XLYyykPJMihASLKn6HHcB2Eh7jFwQb/8D1HCQoPmOHMyXaN4AtIKm1oqEfma6FSnEPENQoledQ==", + "subType": "06" + } + } + }, + "local_long_rand_explicit_id": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAASd2h34ZLib+GiYayrm/FIZ/weg8wF41T0PfF8NCLTJCoT7gIkdpNRz2zkkQgZMR31efNKtsM8Bs4wgZbkrXsXWg==", + "subType": "06" + } + } + }, + "local_long_rand_explicit_altname": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAASPAvdjz+a3FvXqDSjazaGqwZxrfXlfFB5/VjQFXQB0gpodCEaz1qaLSKfCWBg83ftrYKa/1sa44gU5NBthDfDwQ==", + "subType": "06" + } + } + }, + "local_long_det_auto_id": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", + "subType": "06" + } + } + }, + "local_long_det_explicit_id": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", + "subType": "06" + } + } + }, + "local_long_det_explicit_altname": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", + "subType": "06" + } + } + }, + "local_decimal_rand_auto_id": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAATLnMMDZhnGSn5F5xHhsJXxiTGXd61Eq6fgppOlxUNVlsZNYyr5tZ3owfTTqRuD9yRg97x65WiHewBBnJJSeirCTAy9zZxWPVlJSiC0gO7rbM=", + "subType": "06" + } + } + }, + "local_decimal_rand_auto_altname": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAATenMh7NKQioGjpuEojIrYKFaJhbuGxUgu2yTTbe3TndhgHryhW9GXiUqo8WTpnXqpC5E/z03ZYLWfCbe7qGdL6T7bbrTpaTaWZnnAm3XaCqY=", + "subType": "06" + } + } + }, + "local_decimal_rand_explicit_id": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAT9vqXuKRh+2HxeCMr+pQYdhYNw7xrTdU4dySWz0X6tCK7LZO5AV72utmRJxID7Bqv1ZlXAk00V92oDLyKG9kHeG5+S34QE/aLCPsAWcppfxY=", + "subType": "06" + } + } + }, + "local_decimal_rand_explicit_altname": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAATtqOCFMbOkls3LikQNXlnlkRr5gJns1+5Kvbt7P7texMa/QlXkYSHhtwESyfOcCQ2sw1T0eZ9DDuNaznpdK2KIqZBkVEC9iMoxqIqXF7Nab0=", + "subType": "06" + } + } + }, + "local_decimal_det_explicit_id": { + "kms": "local", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$numberDecimal": "1.234" } + }, + "local_decimal_det_explicit_altname": { + "kms": "local", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$numberDecimal": "1.234" } + }, + "local_minKey_rand_explicit_id": { + "kms": "local", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$minKey": 1 } + }, + "local_minKey_rand_explicit_altname": { + "kms": "local", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$minKey": 1 } + }, + "local_minKey_det_explicit_id": { + "kms": "local", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$minKey": 1 } + }, + "local_minKey_det_explicit_altname": { + "kms": "local", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$minKey": 1 } + }, + "local_maxKey_rand_explicit_id": { + "kms": "local", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "local_maxKey_rand_explicit_altname": { + "kms": "local", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "local_maxKey_det_explicit_id": { + "kms": "local", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "local_maxKey_det_explicit_altname": { + "kms": "local", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { "$maxKey": 1 } + }, + "payload=0,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACcsBdT93ivCyvtkfQz9qb1A9Ll+I6hnGE0kFy3rmVG6xAvipmRJSoVq3iv7iUEDvaqmPXfjeH8h8cPYT86v3XSg==", + "subType": "06" + } + } + }, + "payload=1,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACQOzpNBEGSrANr3Wl8uYpqeIc7pjc8e2LS2FaSrb8tM9F3mR1FqGgfJtn3eD+HZf3Y3WEDGK8975a/1BufkMqIQ==", + "subType": "06" + } + } + }, + "payload=2,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACyGJEcuN1pG5oSEyxuKFwqddGHVU5Untbib7LkmtoJe9HngTofkOpeHZH/hV6Z3CFxLu6WFliJoySsFFbnFy9ag==", + "subType": "06" + } + } + }, + "payload=3,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACLbp4w6mx45lR1vvgmeRja/y8U+WnR2oH4IpfrDi4lKM+JPVnJweiN3/1wAy+sXSy0S1Yh9yxmhh9ISoTkAuVxw==", + "subType": "06" + } + } + }, + "payload=4,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACG0qMY/GPZ/2CR61cxbuizywefyMZVdeTCn5KFjqwejgxeBwX0JmGNHKKWbQIDQykRFv0q0WHUgsRmRhaotNCyQ==", + "subType": "06" + } + } + }, + "payload=5,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACJI1onNpQfZhaYWrPEzHvNaJRqUDZK2xoyonB5E473BPgp3zvn0Jmz1deL8GzS+HlkjCrx39OvHyVt3+3S0kYYw==", + "subType": "06" + } + } + }, + "payload=6,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAClyKY9tZBjl7SewSXr3MdoWRDUNgLaXDUjENpjyYvi/54EQ9a+J/LAAh1892i+mLpYxEUAmcftPyfX3VhbCgUQw==", + "subType": "06" + } + } + }, + "payload=7,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACAMbEA+kNvnVV7B//ds2/QoVot061kbazoMwB/psB5eFdLVB5qApAXEWgQEMwkNnsTUYbtSduQz6uGwdagtNBRw==", + "subType": "06" + } + } + }, + "payload=8,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACzdSK/d7Ni6D8qUgNopnEU5ia1K5llhBGk3O1Tf71t4ThnQjYW9eI/rIohWmev5CGWLHhwuvvKUtFcTAe+NMQww==", + "subType": "06" + } + } + }, + "payload=9,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACzQcEa+ktF2EZf35TtyatnSGGaIVvFhZNuo5P3VwQvoONJrK2cSad7PBDAv3xDAB+VPZAigXAGQvd051sHooOHg==", + "subType": "06" + } + } + }, + "payload=10,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACpfoDmApsR5xOD3TDhcHeD7Jco3kPFuuWjDpHtMepMOJ3S0c+ngGGhzPGZtEz2xuD/E7AQn1ryp/WAQ+WwkaJkQ==", + "subType": "06" + } + } + }, + "payload=11,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACICMRXmx3oKqYv0IpmzkSMBIGT4Li3MPBF4Lw1s5F69WvZApD58glIKB6b7koIrF5qc2Wrb1/Nw+stRv0zvQ8Y9CcFV4OHm6WoEw+XDlWXJ4=", + "subType": "06" + } + } + }, + "payload=12,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACTArUn0WUTojQC4fSvq3TwJVTsZNhWAK2WB057u2EnkUzMC0xsbU6611W6Okx6idZ7pMudXpBC34fRDrJPXOu3BxK+ZLCOWS2FqsvWq3HeTY=", + "subType": "06" + } + } + }, + "payload=13,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACU1Ojn7EM2i+5KK2Beh1gPLhryK3Y7PtaZ/v4JvstxuAV4OHOR9yROP7pwenHXxczkWXvcyMY9OCdmHO8pkQkXO21798IPkDDN/ejJUFI0Uw=", + "subType": "06" + } + } + }, + "payload=14,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAAC0ZLwSliCbcr/e1uiYWk6gRuD/5qiyulQ7IUNWjhpBR6SLUfX2+yExLzps9hoOp53j9zRSKIzyleZ8yGLTLeN+Lz9BUe2ZT+sV8NiqZz3pkA=", + "subType": "06" + } + } + }, + "payload=15,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACQ9pmlQeFDr+jEhFwjL/eGVxdv70JdnkLaKdJ3/jkvCX1VPU5HmQIi+JWY3Rrw844E/6sBR6zIODn5aM0WfyP8a2zKRAWaVQZ7n+QE9hDN/8=", + "subType": "06" + } + } + }, + "payload=16,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AizggCwAAAAAAAAAAAAAAAACiOcItInDGHqvkH0I3udp5nnX32XzDeqya/3KDjgZPT5GHek1vFTZ4924JVxFqFQz+No9rOVmyxm8O2fxjTK2vsjtADzKGnMTtFYZqghYCuc=", + "subType": "06" + } + } + }, + "payload=0,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACijFptWQy7a1Y0rpXEvamXWI9v9dnx0Qj84/mKUsVpc3agkQ0B04uPYeROdt2MeEeiZoEKVWV0NjBocAQCEz7dw==", + "subType": "06" + } + } + }, + "payload=1,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAChR90taVWsZk+++sgibX6CnFeQQHNoB8V+n2gmDe3CIT/t+WvhMf9D+mQipbAlrUyHgGihKMHcvAZ5RZ/spaH4Q==", + "subType": "06" + } + } + }, + "payload=2,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAC67wemDv1Xdu7+EMR9LMBTOxfyAqsGaxQibwamZItzplslL/Dp3t9g9vPuNzq0dWwhnfxQ9GBe8OA3dtRaifYCA==", + "subType": "06" + } + } + }, + "payload=3,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACVLxch+uC7weXrbtylCo1m4HYZmh0sd9JCrlTECO2M56JK1X9a30i2BDUdhPuoTvvODv74CGXkZKdist3o0mGAQ==", + "subType": "06" + } + } + }, + "payload=4,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACexfIZGkOYaCGktOUc6cgAYg7Bd/C5ZYmdb7b8+rd5BKWbthW6N6CxhDIyh/DHvkPAeIzfTYA2/9w6tsjfD/TPQ==", + "subType": "06" + } + } + }, + "payload=5,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACjUH/dPW4egOvFMJJnpWK8v27MeLkbXC4GFl1j+wPqTsIEeIWkzEmcXjHLTQGE2GplHHc/zxwRwD2dXdbzvsCDw==", + "subType": "06" + } + } + }, + "payload=6,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACzvS+QkGlvb05pNn+vBMml09yKmE8yM6lwccNIST5uZSsUxXf2hrxPtO7Ylc4lmBAJt/9bcM59JIeT9fpYMc75w==", + "subType": "06" + } + } + }, + "payload=7,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACSf2RxHJpRuh4j8nS1dfonUtsJEwgqfWrwOsfuT/tAGXgDN0ObUpzL2K7G2vmePjP4dwycCSIL3+2j34bqBJK1Q==", + "subType": "06" + } + } + }, + "payload=8,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACu96YYeLXXoYdEZYNU9UAZjSd6G4fOE1edrA6/RjZKVGWKxftmvj5g1VAOiom0XuTZUe1ihbnwhvKexeoa3Vc8Q==", + "subType": "06" + } + } + }, + "payload=9,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACX+UjBKo9+N0Z+mbyqZqkQv2ETMSn6aPTONWgJtw5nWklcxKjUSSLI+8LW/6M6Xf9a7177GsqmV2f/yCRF58Xtw==", + "subType": "06" + } + } + }, + "payload=10,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACL6TVscFzIJ9+Zj6LsCZ9xhaZuTZdvz1nJe4l69nKyj9hCjnyuiV6Ve4AXwQ5W1wiPfkJ0fCZS33NwiHw7QQ/vg==", + "subType": "06" + } + } + }, + "payload=11,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACPLq7IcWhTVwkKmy0flN7opoQzx7tTe1eD9JIc25FC9B6KGQkdcRDglDDR7/m6+kBtTnq88y63vBgomTxA8ZxQE+3pB7zCiBhX0QznuXvP44=", + "subType": "06" + } + } + }, + "payload=12,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACxv7v4pKtom5z1g9FUuyjEWAbdzJ3ytPNZlOfVr6KZnUPhIH7PfCz3/lTdYYWBTj01+SUZiC/7ruof9QDhsSiNWP7nUyHpQ/C3joI/BBjtDA=", + "subType": "06" + } + } + }, + "payload=13,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACZhiElQ/MvyVMwMkZPu8pT54Ap6TlpVSEbE4nIQzzeU3XKVuspMdI5IXvvgfULXKXc+AOu6oQXZ+wAJ1tErVOsb48HF1g0wbXbBA31C5qLEM=", + "subType": "06" + } + } + }, + "payload=14,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACdp8mDOeDuDLhE0LzTOT2p0CMaUsAQrGCzmiK6Ab9xvaIcPPcejUcpdO3XXAS/pPab4+TUwO5GbI5pDJ29zwaOiOz2H3OJ2m2p5BHQp9mCys=", + "subType": "06" + } + } + }, + "payload=15,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAACmtLohoP/gotuon2IvnGeLEfCWHRMhG9Wp4tPu/vbJJkJkbQTP35HRG9VrMV7KKrEQbOsJ2Y6UDBra4tyjn0fIkwwc/0X9i+xaP+TrwpNabE=", + "subType": "06" + } + } + }, + "payload=16,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "ASzggCwAAAAAAAAAAAAAAAAC6s9eUtSneKWj3/A7S+bPZLj3t1WtUh7ltW80b8jCRzA+kOI26j1MEb1tt68HgcnH1IJ3YQ/+UHlV95OgwSnIxlib/HJn3U0s8mpuCWe1Auo=", + "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/source/client-side-encryption/corpus/corpus-key-aws.json b/source/client-side-encryption/corpus/corpus-key-aws.json new file mode 100644 index 0000000000..eca6cf912e --- /dev/null +++ b/source/client-side-encryption/corpus/corpus-key-aws.json @@ -0,0 +1,33 @@ +{ + "status": { + "$numberInt": "1" + }, + "_id": { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "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", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyAltNames": ["aws"] +} \ No newline at end of file diff --git a/source/client-side-encryption/corpus/corpus-key-azure.json b/source/client-side-encryption/corpus/corpus-key-azure.json new file mode 100644 index 0000000000..31a564edb8 --- /dev/null +++ b/source/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/source/client-side-encryption/corpus/corpus-key-gcp.json b/source/client-side-encryption/corpus/corpus-key-gcp.json new file mode 100644 index 0000000000..79d6999b08 --- /dev/null +++ b/source/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/source/client-side-encryption/corpus/corpus-key-kmip.json b/source/client-side-encryption/corpus/corpus-key-kmip.json new file mode 100644 index 0000000000..7c7069700e --- /dev/null +++ b/source/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/source/client-side-encryption/corpus/corpus-key-local.json b/source/client-side-encryption/corpus/corpus-key-local.json new file mode 100644 index 0000000000..b3fe0723b0 --- /dev/null +++ b/source/client-side-encryption/corpus/corpus-key-local.json @@ -0,0 +1,31 @@ +{ + "status": { + "$numberInt": "1" + }, + "_id": { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + }, + "updateDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyAltNames": [ "local" ] +} \ No newline at end of file diff --git a/source/client-side-encryption/corpus/corpus-schema.json b/source/client-side-encryption/corpus/corpus-schema.json new file mode 100644 index 0000000000..e74bc914f5 --- /dev/null +++ b/source/client-side-encryption/corpus/corpus-schema.json @@ -0,0 +1,6335 @@ +{ + "bsonType": "object", + "properties": { + "aws_double_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "aws_double_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "aws_double_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_double_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_string_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "aws_string_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "aws_string_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_string_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_string_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + } + }, + "aws_string_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_string_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_object_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "aws_object_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "aws_object_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_object_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_array_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "aws_array_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "aws_array_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_array_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=00_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "aws_binData=00_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "aws_binData=00_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=00_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=00_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "aws_binData=00_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=00_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=04_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "aws_binData=04_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "aws_binData=04_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=04_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=04_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "aws_binData=04_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_binData=04_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_objectId_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "aws_objectId_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "aws_objectId_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_objectId_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_objectId_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "objectId" + } + } + } + }, + "aws_objectId_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_objectId_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_bool_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "aws_bool_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "aws_bool_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_bool_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_date_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "aws_date_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "aws_date_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_date_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_date_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "date" + } + } + } + }, + "aws_date_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_date_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_regex_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "aws_regex_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "aws_regex_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_regex_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_regex_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "regex" + } + } + } + }, + "aws_regex_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_regex_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_dbPointer_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "aws_dbPointer_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "aws_dbPointer_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_dbPointer_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_dbPointer_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "dbPointer" + } + } + } + }, + "aws_dbPointer_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_dbPointer_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascript_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "aws_javascript_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "aws_javascript_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascript_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascript_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "javascript" + } + } + } + }, + "aws_javascript_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascript_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_symbol_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "aws_symbol_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "aws_symbol_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_symbol_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_symbol_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "symbol" + } + } + } + }, + "aws_symbol_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_symbol_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascriptWithScope_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "aws_javascriptWithScope_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "aws_javascriptWithScope_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_javascriptWithScope_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_int_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "aws_int_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "aws_int_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_int_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_int_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "int" + } + } + } + }, + "aws_int_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_int_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_timestamp_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "aws_timestamp_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "aws_timestamp_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_timestamp_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_timestamp_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "timestamp" + } + } + } + }, + "aws_timestamp_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_timestamp_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_long_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "aws_long_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "aws_long_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_long_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_long_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "long" + } + } + } + }, + "aws_long_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_long_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_decimal_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AWSAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "aws_decimal_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_aws", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "aws_decimal_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "aws_decimal_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_double_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "local_double_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "double" + } + } + } + }, + "local_double_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_double_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_string_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "local_string_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "string" + } + } + } + }, + "local_string_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_string_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_string_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + } + }, + "local_string_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_string_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_object_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "local_object_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "object" + } + } + } + }, + "local_object_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_object_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_array_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "local_array_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "array" + } + } + } + }, + "local_array_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_array_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=00_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "local_binData=00_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "local_binData=00_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=00_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=00_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "local_binData=00_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=00_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=04_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "local_binData=04_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "binData" + } + } + } + }, + "local_binData=04_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=04_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=04_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "binData" + } + } + } + }, + "local_binData=04_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_binData=04_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_objectId_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "local_objectId_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "objectId" + } + } + } + }, + "local_objectId_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_objectId_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_objectId_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "objectId" + } + } + } + }, + "local_objectId_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_objectId_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_bool_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "local_bool_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "bool" + } + } + } + }, + "local_bool_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_bool_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_date_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "local_date_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "date" + } + } + } + }, + "local_date_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_date_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_date_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "date" + } + } + } + }, + "local_date_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_date_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_regex_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "local_regex_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "regex" + } + } + } + }, + "local_regex_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_regex_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_regex_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "regex" + } + } + } + }, + "local_regex_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_regex_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_dbPointer_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "local_dbPointer_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "dbPointer" + } + } + } + }, + "local_dbPointer_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_dbPointer_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_dbPointer_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "dbPointer" + } + } + } + }, + "local_dbPointer_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_dbPointer_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascript_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "local_javascript_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascript" + } + } + } + }, + "local_javascript_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascript_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascript_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "javascript" + } + } + } + }, + "local_javascript_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascript_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_symbol_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "local_symbol_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "symbol" + } + } + } + }, + "local_symbol_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_symbol_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_symbol_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "symbol" + } + } + } + }, + "local_symbol_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_symbol_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascriptWithScope_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "local_javascriptWithScope_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "javascriptWithScope" + } + } + } + }, + "local_javascriptWithScope_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_javascriptWithScope_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_int_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "local_int_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "int" + } + } + } + }, + "local_int_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_int_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_int_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "int" + } + } + } + }, + "local_int_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_int_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_timestamp_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "local_timestamp_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "timestamp" + } + } + } + }, + "local_timestamp_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_timestamp_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_timestamp_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "timestamp" + } + } + } + }, + "local_timestamp_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_timestamp_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_long_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "local_long_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "long" + } + } + } + }, + "local_long_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_long_rand_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_long_det_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "long" + } + } + } + }, + "local_long_det_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_long_det_explicit_altname": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_decimal_rand_auto_id": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "local_decimal_rand_auto_altname": { + "bsonType": "object", + "properties": { + "value": { + "encrypt": { + "keyId": "/altname_local", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + "bsonType": "decimal" + } + } + } + }, + "local_decimal_rand_explicit_id": { + "bsonType": "object", + "properties": { + "value": { + "bsonType": "binData" + } + } + }, + "local_decimal_rand_explicit_altname": { + "bsonType": "object", + "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/source/client-side-encryption/corpus/corpus.json b/source/client-side-encryption/corpus/corpus.json new file mode 100644 index 0000000000..559711b347 --- /dev/null +++ b/source/client-side-encryption/corpus/corpus.json @@ -0,0 +1,8619 @@ +{ + "_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", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_double_rand_auto_altname": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_double_rand_explicit_id": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_double_rand_explicit_altname": { + "kms": "aws", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_double_det_explicit_id": { + "kms": "aws", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_double_det_explicit_altname": { + "kms": "aws", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "aws_string_rand_auto_id": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "aws_string_rand_auto_altname": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "aws_string_rand_explicit_id": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "aws_string_rand_explicit_altname": { + "kms": "aws", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "aws_string_det_auto_id": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "aws_string_det_explicit_id": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "aws_string_det_explicit_altname": { + "kms": "aws", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "aws_object_rand_auto_id": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_object_rand_auto_altname": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_object_rand_explicit_id": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_object_rand_explicit_altname": { + "kms": "aws", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_object_det_explicit_id": { + "kms": "aws", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_object_det_explicit_altname": { + "kms": "aws", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "aws_array_rand_auto_id": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_array_rand_auto_altname": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_array_rand_explicit_id": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_array_rand_explicit_altname": { + "kms": "aws", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_array_det_explicit_id": { + "kms": "aws", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_array_det_explicit_altname": { + "kms": "aws", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "aws_binData=00_rand_auto_id": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_rand_auto_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_rand_explicit_id": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_rand_explicit_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_det_auto_id": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_det_explicit_id": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=00_det_explicit_altname": { + "kms": "aws", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "aws_binData=04_rand_auto_id": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_rand_auto_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_rand_explicit_id": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_rand_explicit_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_det_auto_id": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_det_explicit_id": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_binData=04_det_explicit_altname": { + "kms": "aws", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "aws_undefined_rand_explicit_id": { + "kms": "aws", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "aws_undefined_rand_explicit_altname": { + "kms": "aws", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "aws_undefined_det_explicit_id": { + "kms": "aws", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "aws_undefined_det_explicit_altname": { + "kms": "aws", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "aws_objectId_rand_auto_id": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_rand_auto_altname": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_rand_explicit_id": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_rand_explicit_altname": { + "kms": "aws", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_det_auto_id": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_det_explicit_id": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_objectId_det_explicit_altname": { + "kms": "aws", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "aws_bool_rand_auto_id": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": true + }, + "aws_bool_rand_auto_altname": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": true + }, + "aws_bool_rand_explicit_id": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": true + }, + "aws_bool_rand_explicit_altname": { + "kms": "aws", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": true + }, + "aws_bool_det_explicit_id": { + "kms": "aws", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "aws_bool_det_explicit_altname": { + "kms": "aws", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "aws_date_rand_auto_id": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_rand_auto_altname": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_rand_explicit_id": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_rand_explicit_altname": { + "kms": "aws", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_det_auto_id": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_det_explicit_id": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_date_det_explicit_altname": { + "kms": "aws", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "aws_null_rand_explicit_id": { + "kms": "aws", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "aws_null_rand_explicit_altname": { + "kms": "aws", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "aws_null_det_explicit_id": { + "kms": "aws", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "aws_null_det_explicit_altname": { + "kms": "aws", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "aws_regex_rand_auto_id": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_rand_auto_altname": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_rand_explicit_id": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_rand_explicit_altname": { + "kms": "aws", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_det_auto_id": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_det_explicit_id": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_regex_det_explicit_altname": { + "kms": "aws", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "aws_dbPointer_rand_auto_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_rand_auto_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_rand_explicit_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_rand_explicit_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_det_auto_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_det_explicit_id": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_dbPointer_det_explicit_altname": { + "kms": "aws", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "aws_javascript_rand_auto_id": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_rand_auto_altname": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_rand_explicit_id": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_rand_explicit_altname": { + "kms": "aws", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_det_auto_id": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_det_explicit_id": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_javascript_det_explicit_altname": { + "kms": "aws", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "aws_symbol_rand_auto_id": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_rand_auto_altname": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_rand_explicit_id": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_rand_explicit_altname": { + "kms": "aws", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_det_auto_id": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_det_explicit_id": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_symbol_det_explicit_altname": { + "kms": "aws", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "aws_javascriptWithScope_rand_auto_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_javascriptWithScope_rand_auto_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_javascriptWithScope_rand_explicit_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_javascriptWithScope_rand_explicit_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_javascriptWithScope_det_explicit_id": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_javascriptWithScope_det_explicit_altname": { + "kms": "aws", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "aws_int_rand_auto_id": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_rand_auto_altname": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_rand_explicit_id": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_rand_explicit_altname": { + "kms": "aws", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_det_auto_id": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_det_explicit_id": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_int_det_explicit_altname": { + "kms": "aws", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "aws_timestamp_rand_auto_id": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_rand_auto_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_rand_explicit_id": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_rand_explicit_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_det_auto_id": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_det_explicit_id": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_timestamp_det_explicit_altname": { + "kms": "aws", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "aws_long_rand_auto_id": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_rand_auto_altname": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_rand_explicit_id": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_rand_explicit_altname": { + "kms": "aws", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_det_auto_id": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_det_explicit_id": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_long_det_explicit_altname": { + "kms": "aws", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "aws_decimal_rand_auto_id": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_decimal_rand_auto_altname": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_decimal_rand_explicit_id": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_decimal_rand_explicit_altname": { + "kms": "aws", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_decimal_det_explicit_id": { + "kms": "aws", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_decimal_det_explicit_altname": { + "kms": "aws", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "aws_minKey_rand_explicit_id": { + "kms": "aws", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "aws_minKey_rand_explicit_altname": { + "kms": "aws", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "aws_minKey_det_explicit_id": { + "kms": "aws", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "aws_minKey_det_explicit_altname": { + "kms": "aws", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "aws_maxKey_rand_explicit_id": { + "kms": "aws", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "aws_maxKey_rand_explicit_altname": { + "kms": "aws", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "aws_maxKey_det_explicit_id": { + "kms": "aws", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "aws_maxKey_det_explicit_altname": { + "kms": "aws", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "local_double_rand_auto_id": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "local_double_rand_auto_altname": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "local_double_rand_explicit_id": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "local_double_rand_explicit_altname": { + "kms": "local", + "type": "double", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDouble": "1.234" + } + }, + "local_double_det_explicit_id": { + "kms": "local", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "local_double_det_explicit_altname": { + "kms": "local", + "type": "double", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDouble": "1.234" + } + }, + "local_string_rand_auto_id": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "local_string_rand_auto_altname": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "local_string_rand_explicit_id": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "local_string_rand_explicit_altname": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "local_string_det_auto_id": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "local_string_det_explicit_id": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "mongodb" + }, + "local_string_det_explicit_altname": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": "mongodb" + }, + "local_object_rand_auto_id": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_object_rand_auto_altname": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_object_rand_explicit_id": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_object_rand_explicit_altname": { + "kms": "local", + "type": "object", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_object_det_explicit_id": { + "kms": "local", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_object_det_explicit_altname": { + "kms": "local", + "type": "object", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "x": { + "$numberInt": "1" + } + } + }, + "local_array_rand_auto_id": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_array_rand_auto_altname": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_array_rand_explicit_id": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_array_rand_explicit_altname": { + "kms": "local", + "type": "array", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_array_det_explicit_id": { + "kms": "local", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_array_det_explicit_altname": { + "kms": "local", + "type": "array", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": [ + { + "$numberInt": "1" + }, + { + "$numberInt": "2" + }, + { + "$numberInt": "3" + } + ] + }, + "local_binData=00_rand_auto_id": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_rand_auto_altname": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_rand_explicit_id": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_rand_explicit_altname": { + "kms": "local", + "type": "binData=00", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_det_auto_id": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_det_explicit_id": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=00_det_explicit_altname": { + "kms": "local", + "type": "binData=00", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AQIDBA==", + "subType": "00" + } + } + }, + "local_binData=04_rand_auto_id": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_rand_auto_altname": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_rand_explicit_id": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_rand_explicit_altname": { + "kms": "local", + "type": "binData=04", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_det_auto_id": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_det_explicit_id": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_binData=04_det_explicit_altname": { + "kms": "local", + "type": "binData=04", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$binary": { + "base64": "AAECAwQFBgcICQoLDA0ODw==", + "subType": "04" + } + } + }, + "local_undefined_rand_explicit_id": { + "kms": "local", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "local_undefined_rand_explicit_altname": { + "kms": "local", + "type": "undefined", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "local_undefined_det_explicit_id": { + "kms": "local", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$undefined": true + } + }, + "local_undefined_det_explicit_altname": { + "kms": "local", + "type": "undefined", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$undefined": true + } + }, + "local_objectId_rand_auto_id": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_rand_auto_altname": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_rand_explicit_id": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_rand_explicit_altname": { + "kms": "local", + "type": "objectId", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_det_auto_id": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_det_explicit_id": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_objectId_det_explicit_altname": { + "kms": "local", + "type": "objectId", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$oid": "01234567890abcdef0123456" + } + }, + "local_bool_rand_auto_id": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": true + }, + "local_bool_rand_auto_altname": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": true + }, + "local_bool_rand_explicit_id": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": true + }, + "local_bool_rand_explicit_altname": { + "kms": "local", + "type": "bool", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": true + }, + "local_bool_det_explicit_id": { + "kms": "local", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": true + }, + "local_bool_det_explicit_altname": { + "kms": "local", + "type": "bool", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": true + }, + "local_date_rand_auto_id": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_rand_auto_altname": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_rand_explicit_id": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_rand_explicit_altname": { + "kms": "local", + "type": "date", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_det_auto_id": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_det_explicit_id": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_date_det_explicit_altname": { + "kms": "local", + "type": "date", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$date": { + "$numberLong": "12345" + } + } + }, + "local_null_rand_explicit_id": { + "kms": "local", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "local_null_rand_explicit_altname": { + "kms": "local", + "type": "null", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "local_null_det_explicit_id": { + "kms": "local", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": null + }, + "local_null_det_explicit_altname": { + "kms": "local", + "type": "null", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": null + }, + "local_regex_rand_auto_id": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_rand_auto_altname": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_rand_explicit_id": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_rand_explicit_altname": { + "kms": "local", + "type": "regex", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_det_auto_id": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_det_explicit_id": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_regex_det_explicit_altname": { + "kms": "local", + "type": "regex", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$regularExpression": { + "pattern": ".*", + "options": "" + } + } + }, + "local_dbPointer_rand_auto_id": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_rand_auto_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_rand_explicit_id": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_rand_explicit_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_det_auto_id": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_det_explicit_id": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_dbPointer_det_explicit_altname": { + "kms": "local", + "type": "dbPointer", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$dbPointer": { + "$ref": "db.example", + "$id": { + "$oid": "01234567890abcdef0123456" + } + } + } + }, + "local_javascript_rand_auto_id": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_rand_auto_altname": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_rand_explicit_id": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_rand_explicit_altname": { + "kms": "local", + "type": "javascript", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_det_auto_id": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_det_explicit_id": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_javascript_det_explicit_altname": { + "kms": "local", + "type": "javascript", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1" + } + }, + "local_symbol_rand_auto_id": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_rand_auto_altname": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_rand_explicit_id": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_rand_explicit_altname": { + "kms": "local", + "type": "symbol", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_det_auto_id": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_det_explicit_id": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_symbol_det_explicit_altname": { + "kms": "local", + "type": "symbol", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$symbol": "mongodb-symbol" + } + }, + "local_javascriptWithScope_rand_auto_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_javascriptWithScope_rand_auto_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_javascriptWithScope_rand_explicit_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_javascriptWithScope_rand_explicit_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_javascriptWithScope_det_explicit_id": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_javascriptWithScope_det_explicit_altname": { + "kms": "local", + "type": "javascriptWithScope", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$code": "x=1", + "$scope": {} + } + }, + "local_int_rand_auto_id": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_rand_auto_altname": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_rand_explicit_id": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_rand_explicit_altname": { + "kms": "local", + "type": "int", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_det_auto_id": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_det_explicit_id": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_int_det_explicit_altname": { + "kms": "local", + "type": "int", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberInt": "123" + } + }, + "local_timestamp_rand_auto_id": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_rand_auto_altname": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_rand_explicit_id": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_rand_explicit_altname": { + "kms": "local", + "type": "timestamp", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_det_auto_id": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_det_explicit_id": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_timestamp_det_explicit_altname": { + "kms": "local", + "type": "timestamp", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$timestamp": { + "t": 0, + "i": 12345 + } + } + }, + "local_long_rand_auto_id": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_rand_auto_altname": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_rand_explicit_id": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_rand_explicit_altname": { + "kms": "local", + "type": "long", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_det_auto_id": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_det_explicit_id": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_long_det_explicit_altname": { + "kms": "local", + "type": "long", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberLong": "456" + } + }, + "local_decimal_rand_auto_id": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_decimal_rand_auto_altname": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "auto", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_decimal_rand_explicit_id": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_decimal_rand_explicit_altname": { + "kms": "local", + "type": "decimal", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": true, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_decimal_det_explicit_id": { + "kms": "local", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_decimal_det_explicit_altname": { + "kms": "local", + "type": "decimal", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$numberDecimal": "1.234" + } + }, + "local_minKey_rand_explicit_id": { + "kms": "local", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "local_minKey_rand_explicit_altname": { + "kms": "local", + "type": "minKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "local_minKey_det_explicit_id": { + "kms": "local", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "local_minKey_det_explicit_altname": { + "kms": "local", + "type": "minKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$minKey": 1 + } + }, + "local_maxKey_rand_explicit_id": { + "kms": "local", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "local_maxKey_rand_explicit_altname": { + "kms": "local", + "type": "maxKey", + "algo": "rand", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "local_maxKey_det_explicit_id": { + "kms": "local", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": false, + "value": { + "$maxKey": 1 + } + }, + "local_maxKey_det_explicit_altname": { + "kms": "local", + "type": "maxKey", + "algo": "det", + "method": "explicit", + "identifier": "altname", + "allowed": false, + "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", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "" + }, + "payload=1,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "a" + }, + "payload=2,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aa" + }, + "payload=3,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaa" + }, + "payload=4,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaa" + }, + "payload=5,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaa" + }, + "payload=6,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaa" + }, + "payload=7,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaa" + }, + "payload=8,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaa" + }, + "payload=9,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaa" + }, + "payload=10,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaa" + }, + "payload=11,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaa" + }, + "payload=12,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaa" + }, + "payload=13,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaa" + }, + "payload=14,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaa" + }, + "payload=15,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaaa" + }, + "payload=16,algo=rand": { + "kms": "local", + "type": "string", + "algo": "rand", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaaaa" + }, + "payload=0,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "" + }, + "payload=1,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "a" + }, + "payload=2,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aa" + }, + "payload=3,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaa" + }, + "payload=4,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaa" + }, + "payload=5,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaa" + }, + "payload=6,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaa" + }, + "payload=7,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaa" + }, + "payload=8,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaa" + }, + "payload=9,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaa" + }, + "payload=10,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaa" + }, + "payload=11,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaa" + }, + "payload=12,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaa" + }, + "payload=13,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaa" + }, + "payload=14,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaa" + }, + "payload=15,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaaa" + }, + "payload=16,algo=det": { + "kms": "local", + "type": "string", + "algo": "det", + "method": "explicit", + "identifier": "id", + "allowed": true, + "value": "aaaaaaaaaaaaaaaa" + } +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/encryptedFields-c10.json b/source/client-side-encryption/etc/data/encryptedFields-c10.json new file mode 100644 index 0000000000..e9ed08bb39 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-c10.json @@ -0,0 +1,30 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "10" + } + } + }, + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedUnindexed", + "bsonType": "string" + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-ci-di.json b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-ci-di.json new file mode 100644 index 0000000000..3002c642b2 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-ci-di.json @@ -0,0 +1,40 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "prefix", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": 0, + "caseSensitive": false, + "diacriticSensitive": false + }, + { + "queryType": "suffix", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": 0, + "caseSensitive": false, + "diacriticSensitive": false + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-preview.json b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-preview.json new file mode 100644 index 0000000000..047064beb1 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-preview.json @@ -0,0 +1,44 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "prefixPreview", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + }, + { + "queryType": "suffixPreview", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix.json b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix.json new file mode 100644 index 0000000000..a96e616723 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix.json @@ -0,0 +1,44 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "prefix", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + }, + { + "queryType": "suffix", + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "10" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-substring-ci-di.json b/source/client-side-encryption/etc/data/encryptedFields-substring-ci-di.json new file mode 100644 index 0000000000..657faa5343 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-substring-ci-di.json @@ -0,0 +1,31 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "substring", + "strMaxLength": { + "$numberInt": "10" + }, + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "6" + }, + "contention": 0, + "caseSensitive": false, + "diacriticSensitive": false + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-substring-preview.json b/source/client-side-encryption/etc/data/encryptedFields-substring-preview.json new file mode 100644 index 0000000000..a519edc627 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-substring-preview.json @@ -0,0 +1,33 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "substringPreview", + "strMaxLength": { + "$numberInt": "10" + }, + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "6" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields-substring.json b/source/client-side-encryption/etc/data/encryptedFields-substring.json new file mode 100644 index 0000000000..5e80f4fc95 --- /dev/null +++ b/source/client-side-encryption/etc/data/encryptedFields-substring.json @@ -0,0 +1,33 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "substring", + "strMaxLength": { + "$numberInt": "10" + }, + "strMinQueryLength": { + "$numberInt": "2" + }, + "strMaxQueryLength": { + "$numberInt": "6" + }, + "contention": { + "$numberLong": "0" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] +} diff --git a/source/client-side-encryption/etc/data/encryptedFields.json b/source/client-side-encryption/etc/data/encryptedFields.json new file mode 100644 index 0000000000..88abe5a604 --- /dev/null +++ b/source/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/source/client-side-encryption/etc/data/keys/key1-document.json b/source/client-side-encryption/etc/data/keys/key1-document.json new file mode 100644 index 0000000000..566b56c354 --- /dev/null +++ b/source/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/source/client-side-encryption/etc/data/keys/key1-id.json b/source/client-side-encryption/etc/data/keys/key1-id.json new file mode 100644 index 0000000000..7d18f52ebb --- /dev/null +++ b/source/client-side-encryption/etc/data/keys/key1-id.json @@ -0,0 +1,6 @@ +{ + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } +} diff --git a/source/client-side-encryption/etc/data/keys/key2-document.json b/source/client-side-encryption/etc/data/keys/key2-document.json new file mode 100644 index 0000000000..a654d980ba --- /dev/null +++ b/source/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/source/client-side-encryption/etc/data/keys/key2-id.json b/source/client-side-encryption/etc/data/keys/key2-id.json new file mode 100644 index 0000000000..6e9b87bbc2 --- /dev/null +++ b/source/client-side-encryption/etc/data/keys/key2-id.json @@ -0,0 +1,6 @@ +{ + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } +} diff --git a/source/client-side-encryption/etc/data/lookup/key-doc.json b/source/client-side-encryption/etc/data/lookup/key-doc.json new file mode 100644 index 0000000000..566b56c354 --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/key-doc.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/source/client-side-encryption/etc/data/lookup/schema-csfle.json b/source/client-side-encryption/etc/data/lookup/schema-csfle.json new file mode 100644 index 0000000000..29ac9ad5da --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/schema-csfle.json @@ -0,0 +1,19 @@ +{ + "properties": { + "csfle": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" +} diff --git a/source/client-side-encryption/etc/data/lookup/schema-csfle2.json b/source/client-side-encryption/etc/data/lookup/schema-csfle2.json new file mode 100644 index 0000000000..3f1c02781c --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/schema-csfle2.json @@ -0,0 +1,19 @@ +{ + "properties": { + "csfle2": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" +} diff --git a/source/client-side-encryption/etc/data/lookup/schema-non-csfle.json b/source/client-side-encryption/etc/data/lookup/schema-non-csfle.json new file mode 100644 index 0000000000..3edd12c8f4 --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/schema-non-csfle.json @@ -0,0 +1,3 @@ +{ + "bsonType": "object" +} diff --git a/source/client-side-encryption/etc/data/lookup/schema-qe.json b/source/client-side-encryption/etc/data/lookup/schema-qe.json new file mode 100644 index 0000000000..9428ea1b45 --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/schema-qe.json @@ -0,0 +1,20 @@ +{ + "escCollection": "enxcol_.qe.esc", + "ecocCollection": "enxcol_.qe.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "qe", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": 0 + } + } + ] +} diff --git a/source/client-side-encryption/etc/data/lookup/schema-qe2.json b/source/client-side-encryption/etc/data/lookup/schema-qe2.json new file mode 100644 index 0000000000..77d5bd37cb --- /dev/null +++ b/source/client-side-encryption/etc/data/lookup/schema-qe2.json @@ -0,0 +1,20 @@ +{ + "escCollection": "enxcol_.qe2.esc", + "ecocCollection": "enxcol_.qe2.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "qe2", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": 0 + } + } + ] +} diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-Date.json b/source/client-side-encryption/etc/data/range-encryptedFields-Date.json new file mode 100644 index 0000000000..defa6e37ff --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-Date.json @@ -0,0 +1,36 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json b/source/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json new file mode 100644 index 0000000000..dbe28e9c10 --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-DecimalNoPrecision.json @@ -0,0 +1,26 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json b/source/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json new file mode 100644 index 0000000000..538ab20f0e --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-DecimalPrecision.json @@ -0,0 +1,35 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json b/source/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json new file mode 100644 index 0000000000..fb4f46d375 --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-DoubleNoPrecision.json @@ -0,0 +1,26 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json b/source/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json new file mode 100644 index 0000000000..07d1c84d6f --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-DoublePrecision.json @@ -0,0 +1,35 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-Int.json b/source/client-side-encryption/etc/data/range-encryptedFields-Int.json new file mode 100644 index 0000000000..4f0b4854e4 --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-Int.json @@ -0,0 +1,32 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/data/range-encryptedFields-Long.json b/source/client-side-encryption/etc/data/range-encryptedFields-Long.json new file mode 100644 index 0000000000..32fe1ea15d --- /dev/null +++ b/source/client-side-encryption/etc/data/range-encryptedFields-Long.json @@ -0,0 +1,32 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/etc/generate-corpus.py b/source/client-side-encryption/etc/generate-corpus.py new file mode 100644 index 0000000000..d04136328f --- /dev/null +++ b/source/client-side-encryption/etc/generate-corpus.py @@ -0,0 +1,177 @@ +import json +import sys +import os + +description = """Generates the data and JSON Schema for the corpus test in the target directory. +""" + +if len(sys.argv) != 2: + print(description) + print("usage: python generate-corpus.py ") + print("example: python ./generate-corpus.py ./") + sys.exit(1) + +targetdir = sys.argv[1] + +# Generate test data from this matrix of axes. +axes = [ + ("kms", [ "aws", "local", "azure", "gcp", "kmip"]), + ("type", [ "double", "string", "object", "array", "binData=00", "binData=04", "undefined", "objectId", "bool", "date", "null", "regex", "dbPointer", "javascript", "symbol", "javascriptWithScope", "int", "timestamp", "long", "decimal", "minKey", "maxKey" ]), + ("algo", [ "rand", "det" ]), + ("method", [ "auto", "explicit" ]), + ("identifier", [ "id", "altname" ]) +] + +def allowed(map): + if map["type"] in ["undefined", "minKey", "maxKey", "null"]: + return False + if map["type"] in ["object", "array", "double", "decimal", "javascriptWithScope", "bool"] and map["algo"] == "det": + return False + if map["algo"] == "det" and map["identifier"] == "altname" and map["method"] == "auto": + # prohibited per SERVER-42010 + return False + return True + +def gen_schema (map): + fmt = """ { "bsonType": "object", "properties": { "value": { "encrypt": { "keyId": %s, "algorithm": "%s", "bsonType": "%s" } } } } """ + + if not allowed(map): + # We cannot even set a schema, don't test auto with prohibited fields. + return None + + if map["method"] == "explicit": + return """ { "bsonType": "object", "properties": { "value": { "bsonType": "binData" } } } """ + + if map["identifier"] == "id": + if map["kms"] == "local": + key_id = """[ { "$binary": { "base64": "LOCALAAAAAAAAAAAAAAAAA==", "subType": "04" } } ]""" + elif map["kms"] == "aws": + key_id = """[ { "$binary": { "base64": "AWSAAAAAAAAAAAAAAAAAAA==", "subType": "04" } } ]""" + elif map["kms"] == "azure": + key_id = """[ { "$binary": { "base64": "AZUREAAAAAAAAAAAAAAAAA==", "subType": "04" } } ]""" + elif map["kms"] == "gcp": + key_id = """[ { "$binary": { "base64": "GCPAAAAAAAAAAAAAAAAAAA==", "subType": "04" } } ]""" + elif map["kms"] == "kmip": + key_id = """[ { "$binary": { "base64": "KMIPAAAAAAAAAAAAAAAAAA==", "subType": "04" } } ]""" + else: + key_id = "\"/altname_" + map["kms"] + "\"" + + if map["algo"] == "rand": + algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + else: + algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + + type = map["type"] + if map["type"].startswith("binData"): + type = "binData" + + return fmt % (key_id, algorithm, type) + +def get_bson_value (bson_type): + if bson_type == "double": + return """{ "$numberDouble" : "1.234" }""" + if bson_type == "string": + return """ "mongodb" """ + if bson_type == "object": + return """ { "x" : { "$numberInt": "1" } } """ + if bson_type == "array": + return """ [ { "$numberInt": "1" }, { "$numberInt": "2" }, { "$numberInt": "3" } ] """ + if bson_type == "binData=00": + return """ { "$binary": { "base64": "AQIDBA==", "subType": "00" } } """ + if bson_type == "binData=04": + return """ { "$binary": { "base64": "AAECAwQFBgcICQoLDA0ODw==", "subType": "04" } }""" + if bson_type == "objectId": + return """ { "$oid": "01234567890abcdef0123456" } """ + if bson_type == "bool": + return """ true """ + if bson_type == "date": + return """ { "$date": { "$numberLong": "12345" } } """ + if bson_type == "regex": + return """ { "$regularExpression": { "pattern": ".*", "options": "" } } """ + if bson_type == "dbPointer": + return """ { "$dbPointer": { "$ref": "db.example", "$id": { "$oid": "01234567890abcdef0123456" } } } """ + if bson_type == "javascript": + return """ { "$code": "x=1" } """ + if bson_type == "javascriptWithScope": + return """ { "$code": "x=1", "$scope": {} } """ + if bson_type == "symbol": + return """ { "$symbol": "mongodb-symbol" } """ + if bson_type == "int": + return """ { "$numberInt": "123" } """ + if bson_type == "timestamp": + return """ { "$timestamp": { "t": 0, "i": 12345 } } """ + if bson_type == "long": + return """ { "$numberLong": "456" } """ + if bson_type == "decimal": + return """ { "$numberDecimal": "1.234" } """ + if bson_type == "undefined": + return """ {"$undefined": true} """ + if bson_type == "null": + return "null" + if bson_type == "minKey": + return """{"$minKey": 1}""" + if bson_type == "maxKey": + return """{"$maxKey": 1}""" + +def field_name(map): + return "%s_%s_%s_%s_%s" % (map["kms"], map["type"], map["algo"], map["method"], map["identifier"]) + +def gen_field (map): + if not allowed(map) and map["method"] == "auto": + # We cannot even set a schema, don't test auto with prohibited fields. + return None + allow = "true" if allowed(map) else "false" + return """ { "kms": "%s", "type": "%s", "algo": "%s", "method": "%s", "identifier": "%s", "allowed": %s, "value": %s }""" % (map["kms"], map["type"], map["algo"], map["method"], map["identifier"], allow, get_bson_value (map["type"])) + +schema_sections = [] +corpus_sections = [ + """ "_id": "client_side_encryption_corpus" """ , + """ "altname_aws": "aws" """ , + """ "altname_local": "local" """ , + """ "altname_azure": "azure" """ , + """ "altname_gcp": "gcp" """ , + """ "altname_kmip": "kmip" """ , +] + +def enumerate_axis (map, axis, remaining): + name = axis[0] + for item in axis[1]: + map[name] = item + if remaining == []: + key = field_name (map) + schema_section = gen_schema (map) + corpus_section = gen_field (map) + if schema_section: + schema_sections.append(""" "%s": %s """ % (key, schema_section)) + if corpus_section: + corpus_sections.append(""" "%s": %s """ % (key, corpus_section)) + else: + enumerate_axis (map, remaining[0], remaining[1:]) + +enumerate_axis({}, axes[0], axes[1:]) + +# Add padding cases +for algo in ("rand", "det"): + for i in range(17): + key = "payload=%d,algo=%s" % (i, algo) + map = { + "kms": "local", + "type": "string", + "algo": algo, + "method": "explicit", + "identifier": "id", + "allowed": True, + "value": "a" * i + } + corpus_sections.append (""" "%s": %s """ % (key, json.dumps(map))) + + +def reformat (json_str): + as_json = json.loads(json_str) + return json.dumps (as_json, indent=2) + +schema = """{ "bsonType": "object", "properties": { %s } }""" % (",\n".join(schema_sections)) +open(os.path.join(targetdir, "corpus-schema.json"), "w").write(reformat(schema)) +corpus = "{%s}" % ",\n".join(corpus_sections) +open(os.path.join(targetdir, "corpus.json"), "w").write(reformat(corpus)) +print("Generated corpus.json and corpus-schema.json in target directory") \ No newline at end of file diff --git a/source/client-side-encryption/etc/generate-limits.py b/source/client-side-encryption/etc/generate-limits.py new file mode 100644 index 0000000000..2d57c12655 --- /dev/null +++ b/source/client-side-encryption/etc/generate-limits.py @@ -0,0 +1,30 @@ + +""" +A utility script written to generate the limits schema and data. +""" +from pymongo import MongoClient +from bson import json_util +import bson +from bson.son import SON +from bson.codec_options import CodecOptions +import base64 + + +codec_options = CodecOptions(uuid_representation=bson.binary.STANDARD) +json_options = json_util.JSONOptions(json_mode=json_util.JSONMode.CANONICAL, uuid_representation=bson.binary.STANDARD) +schema = SON() +doc = SON() + +for i in range(100): + key = "%02d" % i + doc[key] = "a" + schema[key] = { + "encrypt": { + "keyId": [bson.Binary(base64.b64decode("LOCALAAAAAAAAAAAAAAAAA=="), bson.binary.UUID_SUBTYPE)], + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + "bsonType": "string" + } + } + +open("limits-doc.json", "w").write(json_util.dumps(doc, json_options=json_options, indent=4)) +open("limits-schema.json", "w").write(json_util.dumps({"bsonType": "object", "properties": schema}, json_options=json_options, indent=4)) \ No newline at end of file diff --git a/source/client-side-encryption/etc/generate-test.py b/source/client-side-encryption/etc/generate-test.py new file mode 100644 index 0000000000..b135ca6a69 --- /dev/null +++ b/source/client-side-encryption/etc/generate-test.py @@ -0,0 +1,711 @@ +import bson +import os +import sys +import yaml +from jinja2 import Template +from pathlib import Path +description = """Generates YAML/JSON tests from a template file. + +This keeps key documents, JSONSchemas, and ciphertexts out of the +handwritten test files to make them more readable and easier +to change. +""" + +keys = { + "basic": { + "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"] + }, + "local": { + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": r"Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": {"$date": {"$numberLong": "1552949630483"}}, + "updateDate": {"$date": {"$numberLong": "1552949630483"}}, + "status": {"$numberInt": "0"}, + "masterKey": {"provider": "local"} + }, + "local:name2": { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + + "keyMaterial": { + "$binary": { + "base64": r"DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": {"$date": {"$numberLong": "1552949630483"}}, + "updateDate": {"$date": {"$numberLong": "1552949630483"}}, + "status": {"$numberInt": "0"}, + "masterKey": {"provider": "local:name2"} + }, + "azure": { + "_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"] + }, + "gcp": { + "_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"] + }, + "kmip": { + "_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"] + }, + "kmip_delegated": { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + }, + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "delegated": True, + "keyId": "11" + }, + "keyAltNames": ["delegated"] + } +} + +schemas = { + "basic": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + } + }, + # Same exact as fields as "encrypted_string" + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "all": { + "properties": { + "encrypted_string_aws": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_azure": { + "encrypt": { + "keyId": [keys["azure"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_gcp": { + "encrypt": { + "keyId": [keys["gcp"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_local": { + "encrypt": { + "keyId": [keys["local"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip": { + "encrypt": { + "keyId": [keys["kmip"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [keys["kmip_delegated"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"}}, + }, + "bsonType": "object" + }, + "encrypted_id": { + "properties": { + "_id": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + } + }, + "local": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [keys["local"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [keys["local"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + } + } + }, + "bsonType": "object" + }, + "invalid_array": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + }, + "bsonType": "array" + }, + "invalid_omitted_type": { + "properties": { + "foo": { + "properties": { + "bar": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + } + }, + "invalid_siblings": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + }, + "bsonType": "object" + } + } + }, + "logical_keywords": { + "anyOf": [ + { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + ] + }, + "noencryption": { + "properties": { + "test": { + "bsonType": "string" + } + }, + "bsonType": "object", + "required": ["test"] + }, + "local:name2": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [keys["local:name2"]["_id"]], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, +} + +ciphertexts = [ + { + "schema": "basic", + "field": "encrypted_string", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_string", + "plaintext": "string1", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_string", + "plaintext": "string2", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + } + }, + { + "schema": "local", + "field": "encrypted_string", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_objectId", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_symbol", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_int32", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_int64", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACKWM29kOcLsfSLfJJ3SSmLr+wgrTtpu1lads1NzDz80AjMyrstw/GMdCuzX+AS+JS84Si2cT1WPMemTkBdVdGAw==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_binData", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_javascript", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_timestamp", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_regex", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_dbPointer", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACKWM29kOcLsfSLfJJ3SSmLr+wgrTtpu1lads1NzDz80AjMyrstw/GMdCuzX+AS+JS84Si2cT1WPMemTkBdVdGAw==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "encrypted_date", + "plaintext": "test", + "data": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06" + } + } + }, + { + "schema": "basic", + "field": "random", + "plaintext": "abc", + "data": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + }, + { + "schema": "all", + "field": "encrypted_string_azure", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + }, + { + "schema": "all", + "field": "encrypted_string_gcp", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + }, + { + "schema": "all", + "field": "encrypted_string_kmip", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + }, + { + "schema": "all", + "field": "encrypted_string_kmip_delegated", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + }, + { + "schema": "local:name2", + "field": "encrypted_string", + "plaintext": "string0", + "data": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + }, +] + + +def schema(name="basic"): + return schemas[name] + + +def schema_w_type(type): + schema = { + "properties": {}, + "bsonType": "object" + } + schema["properties"]["encrypted_" + type] = {"encrypt": { + "keyId": [keys["basic"]["_id"]], + "bsonType": type, + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + return schema + + +def key(name="basic"): + return keys[name] + + +def ciphertext(plaintext, field, schema="basic"): + for ciphertext in ciphertexts: + if schema == ciphertext["schema"] and field == ciphertext["field"] and plaintext == ciphertext["plaintext"]: + return ciphertext["data"] + raise Exception("Ciphertext needs to be pre-generated") + + +def local_provider(name=None): + if name is not None: + if name == "name2": + return { + "key": {"$binary": {"base64": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", "subType": "00"}} + } + else: + raise Exception ("Unexpected name '{}'".format(name)) + + return { + "key": {"$binary": {"base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", "subType": "00"}} + } + +def yamlfile (relative_filepath): + filepath = Path("./source/client-side-encryption/etc/data") / Path(relative_filepath) + with open(filepath, "r") as file: + contents = file.read() + return yaml.safe_load (contents) + +if sys.version_info < (3, 0): + print("Use Python 3") + sys.exit(1) + +if len(sys.argv) < 3: + print(description) + print("usage: python generate-test.py ./test-templates/.yml.template [...] ") + print("example: python ./generate-test.py ./test-templates/bulk.yml.template ./") + sys.exit(1) + +targetdir = sys.argv[-1] + +for filepath in sys.argv[1:-1]: + filedir = os.path.dirname(filepath) + (filename, ext) = os.path.splitext(os.path.basename(filepath)) + if ext != ".template": + print("Input file must end with .yml.template") + sys.exit(1) + (filename, ext) = os.path.splitext(filename) + if ext != ".yml": + print("Input file must end with .yml.template") + sys.exit(1) + + template = Template(open(filepath, "r").read()) + injections = { + "schema": schema, + "ciphertext": ciphertext, + "key": key, + "local_provider": local_provider, + "schema_w_type": schema_w_type, + "yamlfile": yamlfile, + } + + rendered = template.render(**injections) + # check for valid YAML. + parsed = yaml.load(rendered, Loader=yaml.Loader) + open(f"{os.path.join(targetdir,filename + '.yml')}", "w").write(rendered) + print(f"Generated {os.path.join(targetdir,filename)}.yml") + print("""Run "make" from specifications/source directory to generate corresponding JSON file""") diff --git a/source/client-side-encryption/etc/test-templates/aggregate.yml.template b/source/client-side-encryption/etc/test-templates/aggregate.yml.template new file mode 100644 index 0000000000..97f51c3c0c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/aggregate.yml.template @@ -0,0 +1,120 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Aggregate with deterministic encryption" + skipReason: "SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + aggregate: *collection_name + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Aggregate with empty pipeline" + skipReason: "SERVER-40829 hides agg support behind enableTestCommands flag." + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: [] + result: + - { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + aggregate: *collection_name + pipeline: [] + cursor: {} + command_name: aggregate + # Needs to fetch key when decrypting results + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Aggregate should fail with random encryption" + skipReason: "SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { random: "abc" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "Database aggregate should fail" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + object: database + arguments: + pipeline: + - $currentOp: { allUsers: false, idleConnections: false, localOps: true } + - $match: { command.aggregate: { $eq: 1 } } + - $project: { command: 1 } + - $project: { command.lsid: 0 } + result: + errorContains: "non-collection command not supported for auto encryption: aggregate" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/awsTemporary.yml.template b/source/client-side-encryption/etc/test-templates/awsTemporary.yml.template new file mode 100644 index 0000000000..9293f10ee2 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/awsTemporary.yml.template @@ -0,0 +1,57 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert a document with auto encryption using the AWS provider with temporary credentials" + clientOptions: + autoEncryptOpts: + kmsProviders: + awsTemporary: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key()["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Insert with invalid temporary credentials" + clientOptions: + autoEncryptOpts: + kmsProviders: + awsTemporaryNoSessionToken: {} + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "security token" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/azureKMS.yml.template b/source/client-side-encryption/etc/test-templates/azureKMS.yml.template new file mode 100644 index 0000000000..5942b960ac --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/azureKMS.yml.template @@ -0,0 +1,46 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema("all")}} +key_vault_data: [{{key("azure")}}] + +tests: + - description: "Insert a document with auto encryption using Azure KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + azure: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_azure: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key("azure")["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_azure: {{ciphertext("string0", field="encrypted_string_azure", schema="all")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/badQueries.yml.template b/source/client-side-encryption/etc/test-templates/badQueries.yml.template new file mode 100644 index 0000000000..36619eeac3 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/badQueries.yml.template @@ -0,0 +1,536 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +# TODO: I could see an argument against having these tests of mongocryptd as part +# of driver tests. When mongocryptd introduces support for these operators, these +# tests will fail. But it's also easy enough to remove these tests when that happens. + +tests: + - description: "$text unconditionally fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { $text: { $search: "search text" } } + result: + errorContains: "Unsupported match expression operator for encryption" + - description: "$where unconditionally fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { $where: { $code: "function() { return true }" } } + result: + errorContains: "Unsupported match expression operator for encryption" + - description: "$bit operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $bitsAllClear: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllClear: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAllSet: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllSet: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnyClear: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnyClear: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnySet: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnySet: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "geo operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $near: [0,0] }} + result: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $near: [0,0] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $nearSphere: [0,0] }} + result: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $nearSphere: [0,0] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "inequality operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $gt: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $gt: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lt: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $lt: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $gte: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $gte: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lte: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $lte: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "other misc operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $mod: [3, 1] }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $mod: [3, 1] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $regex: "pattern", $options: "" }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $regex: "pattern", $options: "" }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $size: 2 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $size: 2 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $type: 2 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $type: 2 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $eq: null }} + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + - &doc1 { _id: 2, encrypted_string: "string1" } + - name: find + arguments: + filter: { encrypted_string: { $eq: null }} + result: + errorContains: "Illegal equality to null predicate for encrypted field" + - name: find + arguments: + filter: { unencrypted: { $in: [null] }} + result: + - *doc0 + - *doc1 + - name: find + arguments: + filter: { encrypted_string: { $in: [null] }} + result: + errorContains: "Illegal equality to null inside $in against an encrypted field" + - description: "$addToSet succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $addToSet: { "unencrypted": ["a"]}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $addToSet: { "encrypted_string": ["a"]}} + result: + errorContains: "$addToSet not allowed on encrypted values" + - description: "$inc succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $inc: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $inc: { "encrypted_string": 1}} + result: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$mul succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $mul: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $mul: { "encrypted_string": 1}} + result: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$max succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $max: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $max: { "encrypted_string": 1}} + result: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$min succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $min: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $min: { "encrypted_string": 1}} + result: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$currentDate succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $currentDate: { "unencrypted": true}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $currentDate: { "encrypted_string": true }} + result: + errorContains: "$currentDate not allowed on encrypted values" + - description: "$pop succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pop: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pop: { "encrypted_string": 1 }} + result: + errorContains: "$pop not allowed on encrypted values" + - description: "$pull succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pull: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pull: { "encrypted_string": 1 }} + result: + errorContains: "$pull not allowed on encrypted values" + - description: "$pullAll succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pullAll: { "unencrypted": [1] }} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pullAll: { "encrypted_string": [1] }} + result: + errorContains: "$pullAll not allowed on encrypted values" + - description: "$push succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $push: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $push: { "encrypted_string": 1 }} + result: + errorContains: "$push not allowed on encrypted values" + - description: "array filters on encrypted fields does not error in mongocryptd, but errors in mongod" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $set : { "encrypted_string.$[i].x": 1 }} + arrayFilters: [{ i.x: 1 }] + result: + errorContains: "Array update operations not allowed on encrypted values" + - description: "positional operator succeeds on unencrypted, errors on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { "unencrypted": 1 } + update: { $set : { "unencrypted.$": 1 }} + result: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { "encrypted_string": "abc" } + update: { $set : { "encrypted_string.$": "abc" }} + result: + errorContains: "Cannot encrypt fields below '$' positional update operator" + - description: "an update that would produce an array on an encrypted field errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $set : { "encrypted_string": [1,2] }} + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with encrypted field on _id errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + schemaMap: + "default.default": {{schema("encrypted_id")}} + operations: + - name: insertOne + arguments: + document: { _id: 1 } + result: + errorContains: "Invalid schema containing the 'encrypt' keyword." + - description: "an insert with an array value for an encrypted field fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { encrypted_string: [ "123", "456"] } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with a Timestamp(0,0) value in the top-level fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { random: {"$timestamp": {"t": 0, "i": 0 }} } + result: + errorContains: "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" + - description: "distinct with the key referring to a field where the keyID is a JSON Pointer errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: distinct + arguments: + filter: {} + fieldName: "encrypted_w_altname" + result: + errorContains: "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/badSchema.yml.template b/source/client-side-encryption/etc/test-templates/badSchema.yml.template new file mode 100644 index 0000000000..e760424374 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/badSchema.yml.template @@ -0,0 +1,73 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +key_vault_data: [{{key()}}] + +tests: + - description: "Schema with an encrypted field in an array" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema('invalid_array')}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] + - description: "Schema without specifying parent object types" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema('invalid_omitted_type')}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] + - description: "Schema with siblings of encrypt document" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema('invalid_siblings')}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "'encrypt' cannot be used in conjunction with 'bsonType'" + outcome: + collection: + data: [] + - description: "Schema with logical keywords" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema('logical_keywords')}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/basic.yml.template b/source/client-side-encryption/etc/test-templates/basic.yml.template new file mode 100644 index 0000000000..fd39db69ff --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/basic.yml.template @@ -0,0 +1,102 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert with deterministic encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Insert with randomized encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, random: "123" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc1] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { _id: 1, random: { $$type: "binData" } } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, random: { $$type: "binData" } } \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/bulk.yml.template b/source/client-side-encryption/etc/test-templates/bulk.yml.template new file mode 100644 index 0000000000..d3c681b2c6 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/bulk.yml.template @@ -0,0 +1,78 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Bulk write with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: bulkWrite + arguments: + requests: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + - name: insertOne + arguments: + document: &doc1 { _id: 2, encrypted_string: "string1" } + - name: updateOne + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + - name: deleteOne + arguments: + filter: { $and: [{ encrypted_string: "string1" }, { _id: 2 }]} + options: { ordered: true } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}}, random: { $$type: "binData" } } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } + ordered: true + command_name: insert + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} }} + u: {$set: { encrypted_string: {{ciphertext("string1", field="encrypted_string")}} }} + ordered: true + command_name: update + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { "$and": [ { "encrypted_string": { "$eq": {{ciphertext("string1", field="encrypted_string")}} }}, { "_id": { "$eq": 2 }} ] } + limit: 1 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string1", field="encrypted_string")}}, random: { $$type: "binData" } } diff --git a/source/client-side-encryption/etc/test-templates/bypassAutoEncryption.yml.template b/source/client-side-encryption/etc/test-templates/bypassAutoEncryption.yml.template new file mode 100644 index 0000000000..68a585e5f3 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/bypassAutoEncryption.yml.template @@ -0,0 +1,100 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [{_id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} }] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert with bypassAutoEncryption" + clientOptions: + autoEncryptOpts: + bypassAutoEncryption: true + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + - name: find + arguments: + filter: { } + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { } + command_name: find + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - { _id: 2, encrypted_string: "string0" } + - description: "Insert with bypassAutoEncryption for local schema" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{ schema() }} + bypassAutoEncryption: true + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + - name: find + arguments: + filter: { } + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { } + command_name: find + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - { _id: 2, encrypted_string: "string0" } \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/bypassedCommand.yml.template b/source/client-side-encryption/etc/test-templates/bypassedCommand.yml.template new file mode 100644 index 0000000000..da00e00551 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/bypassedCommand.yml.template @@ -0,0 +1,43 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {} +key_vault_data: [{{key()}}] + +tests: + - description: "ping is bypassed" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: ping + arguments: + command: + ping: 1 + expectations: + # No listCollections, no mongocryptd command, just the ping. + - command_started_event: + command: + ping: 1 + command_name: ping + - description: "kill op is not bypassed" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: killOp + arguments: + command: + killOp: 1 + op: 1234 + result: + errorContains: "command not supported for auto encryption: killOp" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/count.yml.template b/source/client-side-encryption/etc/test-templates/count.yml.template new file mode 100644 index 0000000000..b154c305c5 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/count.yml.template @@ -0,0 +1,54 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Count with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: count + arguments: + filter: { encrypted_string: "string0" } + result: 2 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + count: *collection_name + query: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + command_name: count + - description: "Count fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: count + arguments: + filter: { random: "abc" } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/countDocuments.yml.template b/source/client-side-encryption/etc/test-templates/countDocuments.yml.template new file mode 100644 index 0000000000..345cc39b34 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/countDocuments.yml.template @@ -0,0 +1,52 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "countDocuments with deterministic encryption" + skipReason: "waiting on SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: countDocuments + arguments: + filter: { encrypted_string: "string0" } + result: 1 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + aggregate: *collection_name + pipeline: + - { $match: { encrypted_string: {{ciphertext("string0", field="encrypted_string")}} }} + - { $group: { _id: 1, n: { $sum: 1 }}} + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/create-and-createIndexes.yml.template b/source/client-side-encryption/etc/test-templates/create-and-createIndexes.yml.template new file mode 100644 index 0000000000..ba334ccffe --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/create-and-createIndexes.yml.template @@ -0,0 +1,58 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +tests: + - description: "create is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "unencryptedCollection" + - name: createCollection + object: database + arguments: + collection: "unencryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: "unencryptedCollection" + - description: "createIndexes is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + collection: "unencryptedCollection" + index: name diff --git a/source/client-side-encryption/etc/test-templates/delete.yml.template b/source/client-side-encryption/etc/test-templates/delete.yml.template new file mode 100644 index 0000000000..beef392eeb --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/delete.yml.template @@ -0,0 +1,91 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "deleteOne with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: deleteOne + arguments: + filter: { encrypted_string: "string0" } + result: + deletedCount: 1 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + limit: 1 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted + - description: "deleteMany with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: deleteMany + arguments: + filter: { encrypted_string: { $in: [ "string0", "string1" ] } } + result: + deletedCount: 2 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { encrypted_string: { $in : [ {{ciphertext("string0", field="encrypted_string")}}, {{ciphertext("string1", field="encrypted_string")}} ] } } + limit: 0 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/distinct.yml.template b/source/client-side-encryption/etc/test-templates/distinct.yml.template new file mode 100644 index 0000000000..210973e64e --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/distinct.yml.template @@ -0,0 +1,66 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc2_encrypted { _id: 3, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "distinct with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: distinct + arguments: + filter: { encrypted_string: "string0" } + fieldName: "encrypted_string" + result: + - "string0" + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + distinct: *collection_name + key: encrypted_string + query: { encrypted_string: {$eq: {{ciphertext("string0", field="encrypted_string")}} } } + command_name: distinct + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted + - description: "Distinct fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: distinct + arguments: + filter: { random: "abc" } + fieldName: "encrypted_string" + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/explain.yml.template b/source/client-side-encryption/etc/test-templates/explain.yml.template new file mode 100644 index 0000000000..abcd671df3 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/explain.yml.template @@ -0,0 +1,57 @@ +runOn: + - minServerVersion: "7.0.0" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Explain a find with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: explain + arguments: + command: + explain: + find: *collection_name + filter: { encrypted_string : "string1" } + + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + explain: + find: *collection_name + filter: + { encrypted_string: { $eq: {{ciphertext("string1", field="encrypted_string")}} } } + verbosity: "allPlansExecution" + command_name: explain + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/find.yml.template b/source/client-side-encryption/etc/test-templates/find.yml.template new file mode 100644 index 0000000000..84174d9a6e --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/find.yml.template @@ -0,0 +1,105 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} , random: {{ciphertext("abc", field="random")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Find with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { encrypted_string: "string0" } + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + find: *collection_name + filter: + { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - description: "Find with $in with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { encrypted_string: { $in: [ "string0", "string1" ] } } + result: + - { _id: 1, encrypted_string: "string0" } + - &doc1 { _id: 2, encrypted_string: "string1", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + find: *collection_name + filter: + # Note, the values are re-ordered, but this is logically equivalent. + { encrypted_string: { $in: [ {{ciphertext("string0", field="encrypted_string")}}, {{ciphertext("string1", field="encrypted_string")}} ] } } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - description: "Find fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: find + arguments: + filter: { random: "abc" } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/findOneAndDelete.yml.template b/source/client-side-encryption/etc/test-templates/findOneAndDelete.yml.template new file mode 100644 index 0000000000..f6d43c6295 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/findOneAndDelete.yml.template @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "findOneAndDelete with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndDelete + arguments: + filter: + { encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: + { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + remove: true + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/findOneAndReplace.yml.template b/source/client-side-encryption/etc/test-templates/findOneAndReplace.yml.template new file mode 100644 index 0000000000..85117a5e09 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/findOneAndReplace.yml.template @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "findOneAndReplace with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndReplace + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1" } + returnDocument: Before + result: { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + update: { encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/findOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/findOneAndUpdate.yml.template new file mode 100644 index 0000000000..27c8f27355 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/findOneAndUpdate.yml.template @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "findOneAndUpdate with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndUpdate + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + returnDocument: Before + result: { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + update: { $set: { encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Compact.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Compact.yml.template new file mode 100644 index 0000000000..bec2710b9e --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Compact.yml.template @@ -0,0 +1,84 @@ +# Requires libmongocrypt 1.8.0. libmongocrypt 1.10.0 has a bug (MONGOCRYPT-699) that may cause this test to fail on server version 7. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }}, {{ yamlfile("keys/key2-document.json") }} ] +tests: + - description: "Compact works" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: runCommand + object: database + command_name: compactStructuredEncryptionData + arguments: + command: + compactStructuredEncryptionData: *collection_name + result: + ok: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }}, + {{ yamlfile("keys/key2-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + compactStructuredEncryptionData: *collection_name + 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: *collection_name + result: + errorContains: "'compactStructuredEncryptionData.compactionTokens' is missing" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-DecryptExistingData.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-DecryptExistingData.yml.template new file mode 100644 index 0000000000..df3f33ffb0 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-DecryptExistingData.yml.template @@ -0,0 +1,66 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [ + &doc0 { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ] +key_vault_data: [ {{ yamlfile("keys/key2-document.json") }}] +tests: + - description: "FLE2 decrypt of existing data succeeds" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: find + arguments: + filter: { _id: 1 } + result: + [{ "_id": 1, "encryptedUnindexed": "value123" }] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: *collection_name + filter: { "_id": 1 } + command_name: find + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key2-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Delete.yml.template new file mode 100644 index 0000000000..5054efb03c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Delete.yml.template @@ -0,0 +1,100 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile ("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile ("keys/key1-document.json") }} ] +tests: + - description: "Delete can query an FLE2 indexed field" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 {"_id": 1, "encryptedIndexed": "value123" } + - name: deleteOne + arguments: + filter: { "encryptedIndexed": "value123" } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: + - { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + data: [] diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFields-vs-jsonSchema.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFields-vs-jsonSchema.yml.template new file mode 100644 index 0000000000..842a4228e1 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFields-vs-jsonSchema.yml.template @@ -0,0 +1,102 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +json_schema: { + "properties": {}, + "bsonType": "object" + } +encrypted_fields: &encrypted_fields {{ yamlfile ("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile ("keys/key1-document.json") }} ] +tests: + - description: "encryptedFields is preferred over jsonSchema" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedIndexed: "123" } + - name: find + arguments: + filter: { encryptedIndexed: "123" } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType" : "00" } }] } diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFieldsMap-defaults.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFieldsMap-defaults.yml.template new file mode 100644 index 0000000000..3ee18ce3b8 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-EncryptedFieldsMap-defaults.yml.template @@ -0,0 +1,58 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +key_vault_data: [] +tests: + - description: "default state collections are applied to encryptionInformation" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + encryptedFieldsMap: &efm { + "default.default": { + "fields": [] + } + } + operations: + - name: insertOne + arguments: + document: &doc0 { + _id: 1, + # Include a FLE2FindEncryptedPayload for 'encryptionInformation' to be appended. + foo: { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + encryptionInformation: { + "type": { + "$numberInt": "1" + }, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [] + } + } + } + ordered: true + command_name: insert + outcome: + collection: + data: + - *doc0 diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..6f68387408 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-FindOneAndUpdate.yml.template @@ -0,0 +1,200 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile ("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile ("keys/key1-document.json") }} ] +tests: + - description: "findOneAndUpdate can query an FLE2 indexed field" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": { "foo": "bar"} } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + 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: {{ local_provider() }} + 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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": { "encryptedIndexed": { "$$type": "binData" }} } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: findAndModify + - command_started_event: + command: + find: *collection_name + 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/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Indexed.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Indexed.yml.template new file mode 100644 index 0000000000..eee08d01d6 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Indexed.yml.template @@ -0,0 +1,96 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Insert and find FLE2 indexed field" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedIndexed: "123" } + - name: find + arguments: + filter: { encryptedIndexed: "123" } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType" : "00" } }] } diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Unindexed.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Unindexed.yml.template new file mode 100644 index 0000000000..1cf9c1df6a --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-InsertFind-Unindexed.yml.template @@ -0,0 +1,89 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: {{ yamlfile("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile("keys/key2-document.json") }} ] +tests: + - description: "Insert and find FLE2 unindexed field" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedUnindexed: "value123" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key2-id.json" ) }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedUnindexed": { $$type: "binData" } } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedUnindexed: "value123" } + - name: find + arguments: + filter: { encryptedUnindexed: "value123" } + result: + # Expected error message changed in https://github.com/10gen/mongo-enterprise-modules/commit/212b584d4f7a44bed41c826a180a4aff00923d7a#diff-5f12b55e8d5c52c2f62853ec595dc2c1e2e5cb4fdbf7a32739a8e3acb3c6f818 + # Before the message was "cannot query non-indexed fields with the randomized encryption algorithm" + # After: "can only execute encrypted equality queries with an encrypted equality index" + # Use a small common substring. + errorContains: "encrypt" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-MissingKey.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-MissingKey.yml.template new file mode 100644 index 0000000000..744a1a0dda --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-MissingKey.yml.template @@ -0,0 +1,43 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [ + &doc0 { + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ] +encrypted_fields: {{ yamlfile("encryptedFields.json") }} +key_vault_data: [] +tests: + - description: "FLE2 encrypt fails with missing key" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedIndexed: "123" } + result: + errorContains: "not all keys requested were satisfied" + - description: "FLE2 decrypt fails with missing key" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: find + arguments: + filter: { } + result: + errorContains: "not all keys requested were satisfied" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-NoEncryption.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-NoEncryption.yml.template new file mode 100644 index 0000000000..212eb4162b --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-NoEncryption.yml.template @@ -0,0 +1,44 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +key_vault_data: [] +encrypted_fields: { + "fields": [] + } +tests: + - description: "insert with no encryption succeeds" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { + _id: 1, + foo: "bar" + } + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + command_name: insert + outcome: + collection: + data: + - *doc0 diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Compact.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Compact.yml.template new file mode 100644 index 0000000000..908d7ff33e --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Compact.yml.template @@ -0,0 +1,93 @@ +# Requires libmongocrypt 1.10.1. +runOn: + - minServerVersion: "8.0.0" # Require range v2 support on server. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Compact works with 'range' fields" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + # Insert before running compact to ensure the server errors if `encryptionInformation` is incorrectly omitted. + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: runCommand + object: database + command_name: compactStructuredEncryptionData + arguments: + command: + compactStructuredEncryptionData: *collection_name + result: + ok: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + compactStructuredEncryptionData: *collection_name + compactionTokens: + "encryptedInt": { + "ecoc": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "QxKJD2If48p0l8NAXf2Kr0aleMd/dATSjBK6hTpNMyc=", + "subType": "00" + } + } + } + encryptionInformation: *encryptionInformation + command_name: compactStructuredEncryptionData diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Aggregate.yml.template new file mode 100644 index 0000000000..86f0aafb3a --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Aggregate.yml.template @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Date. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Correctness.yml.template new file mode 100644 index 0000000000..2ede184dba --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Correctness.yml.template @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "0" } } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: {$numberLong: "0"}}, $lte: { $date: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDate: { $date: { $numberLong: "200" } }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "0" } } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "1" } } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: {$date: {$numberLong: "0"}}, $lte: {$date: {$numberLong: "200"}} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error mongocryptd. + errorContains: "value type is a date" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Delete.yml.template new file mode 100644 index 0000000000..72a8617571 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Delete.yml.template @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Date. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..f7a2a97e24 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-FindOneAndUpdate.yml.template @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Date. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: findOneAndUpdate + arguments: + filter: { encryptedDate: { $gt: {$date: {$numberLong: "0"}}} } + update: { "$set": { "encryptedDate": {$date: {$numberLong: "2"}}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDate": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-InsertFind.yml.template new file mode 100644 index 0000000000..a07a9986e9 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-InsertFind.yml.template @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Date. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" }} } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" }} } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Update.yml.template new file mode 100644 index 0000000000..32ab837b0b --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Date-Update.yml.template @@ -0,0 +1,240 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Date.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Date. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } }} + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Aggregate.yml.template new file mode 100644 index 0000000000..b9391fb4ff --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Aggregate.yml.template @@ -0,0 +1,1675 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Decimal. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimalNoPrecision": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Correctness.yml.template new file mode 100644 index 0000000000..fec83b77bc --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Correctness.yml.template @@ -0,0 +1,293 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Delete.yml.template new file mode 100644 index 0000000000..affdb107ab --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Delete.yml.template @@ -0,0 +1,899 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Decimal. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..4da7dc0428 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml.template @@ -0,0 +1,1672 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Decimal. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimalNoPrecision": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimalNoPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-InsertFind.yml.template new file mode 100644 index 0000000000..47de920376 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-InsertFind.yml.template @@ -0,0 +1,1668 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Decimal. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Update.yml.template new file mode 100644 index 0000000000..fd32a6945c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Decimal-Update.yml.template @@ -0,0 +1,1685 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Decimal. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml.template new file mode 100644 index 0000000000..f7df1f9335 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml.template @@ -0,0 +1,317 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DecimalPrecision. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Correctness.yml.template new file mode 100644 index 0000000000..51e6fa40e5 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Correctness.yml.template @@ -0,0 +1,424 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDecimalPrecision: { $numberDecimal: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Delete.yml.template new file mode 100644 index 0000000000..ed4b431cf0 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Delete.yml.template @@ -0,0 +1,220 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DecimalPrecision. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..f848910cfa --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml.template @@ -0,0 +1,316 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DecimalPrecision. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalPrecision: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimalPrecision": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimalPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml.template new file mode 100644 index 0000000000..1c92aa0693 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml.template @@ -0,0 +1,307 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DecimalPrecision. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Update.yml.template new file mode 100644 index 0000000000..7cd97b6eaa --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DecimalPrecision-Update.yml.template @@ -0,0 +1,324 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DecimalPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DecimalPrecision. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Defaults.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Defaults.yml.template new file mode 100644 index 0000000000..a4360e0016 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Defaults.yml.template @@ -0,0 +1,157 @@ +# Test "range" field with defaults for `trimFactor` and `sparsity`. +# Test requires libmongocrypt with changes in MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + topology: [ "replicaset", "sharded", "load-balanced" ] # Exclude "standalone". QE collections are not supported on standalone. +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + # Exclude `trimFactor` and `sparsity` + "contention": { "$numberLong": "0" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" } + } + } + ] + } +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range applies defaults for trimFactor and sparsity" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: *encryptionInformation + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: *encryptionInformation + command_name: find + outcome: + collection: + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + # Expected contents of `__safeContent__` require MONGOCRYPT-698 to apply expected `trimFactor`. + "__safeContent__": [ + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + } + ] + } diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Aggregate.yml.template new file mode 100644 index 0000000000..072c325426 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Aggregate.yml.template @@ -0,0 +1,901 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Double. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDoubleNoPrecision": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Correctness.yml.template new file mode 100644 index 0000000000..5461a72e0d --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Correctness.yml.template @@ -0,0 +1,292 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Delete.yml.template new file mode 100644 index 0000000000..1bec27dd0a --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Delete.yml.template @@ -0,0 +1,512 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Double. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..6dc0193b08 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-FindOneAndUpdate.yml.template @@ -0,0 +1,899 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Double. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDoubleNoPrecision": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDoubleNoPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-InsertFind.yml.template new file mode 100644 index 0000000000..2f2f03029a --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-InsertFind.yml.template @@ -0,0 +1,895 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Double. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Update.yml.template new file mode 100644 index 0000000000..f123b9b94e --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Double-Update.yml.template @@ -0,0 +1,912 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoubleNoPrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Double. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Aggregate.yml.template new file mode 100644 index 0000000000..2beea4ad88 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Aggregate.yml.template @@ -0,0 +1,313 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DoublePrecision. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "DQYKAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYAAQAAAAFtbgAAAAAAAAAAAAFteAAAAAAAAABpQAA=", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Correctness.yml.template new file mode 100644 index 0000000000..9d1b039d22 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Correctness.yml.template @@ -0,0 +1,424 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDoublePrecision: { $numberDouble: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Delete.yml.template new file mode 100644 index 0000000000..11e5c35a08 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Delete.yml.template @@ -0,0 +1,218 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DoublePrecision. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..55e400936f --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml.template @@ -0,0 +1,311 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DoublePrecision. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDoublePrecision: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDoublePrecision": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDoublePrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-InsertFind.yml.template new file mode 100644 index 0000000000..0619b944cf --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-InsertFind.yml.template @@ -0,0 +1,307 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DoublePrecision. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Update.yml.template new file mode 100644 index 0000000000..7c45ce371b --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-DoublePrecision-Update.yml.template @@ -0,0 +1,326 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-DoublePrecision.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range DoublePrecision. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Aggregate.yml.template new file mode 100644 index 0000000000..2f3e8ae479 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Aggregate.yml.template @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Int. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedInt": { $gt: {$numberInt: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Correctness.yml.template new file mode 100644 index 0000000000..72b225fb1f --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Correctness.yml.template @@ -0,0 +1,423 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "1" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lte: { $numberInt: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedInt: { $numberInt: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: { $numberInt: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "1" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lte: { $numberInt: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Delete.yml.template new file mode 100644 index 0000000000..5bd4fde474 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Delete.yml.template @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Int. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..4097c1555a --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-FindOneAndUpdate.yml.template @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Int. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedInt: { $gt: {$numberInt: "0"}} } + update: { "$set": { "encryptedInt": {$numberInt: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedInt": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-InsertFind.yml.template new file mode 100644 index 0000000000..d63760dc1d --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-InsertFind.yml.template @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Int. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Update.yml.template new file mode 100644 index 0000000000..ed10f48ca0 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Int-Update.yml.template @@ -0,0 +1,242 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Int.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Int. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Aggregate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Aggregate.yml.template new file mode 100644 index 0000000000..1ac133d902 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Aggregate.yml.template @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Long. Aggregate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedLong": { $gt: {$numberLong: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Correctness.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Correctness.yml.template new file mode 100644 index 0000000000..8da4e0187b --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Correctness.yml.template @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "Find with $gt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberLong: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "1" } }} + result: [] + + - description: "Find with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lte: { $numberLong: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } + result: [*doc0] + + - description: "Insert out of range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedLong: { $numberLong: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: { $numberLong: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "1" } }} } + result: [] + + - description: "Aggregate with $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lte: { $numberLong: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Delete.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Delete.yml.template new file mode 100644 index 0000000000..7391998c6b --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Delete.yml.template @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Long. Delete." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-FindOneAndUpdate.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-FindOneAndUpdate.yml.template new file mode 100644 index 0000000000..0d06fa6e3c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-FindOneAndUpdate.yml.template @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Long. FindOneAndUpdate." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedLong: { $gt: {$numberLong: "0"}} } + update: { "$set": { "encryptedLong": {$numberLong: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedLong": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-InsertFind.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-InsertFind.yml.template new file mode 100644 index 0000000000..3878f0b15c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-InsertFind.yml.template @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Long. Insert and Find." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Update.yml.template new file mode 100644 index 0000000000..ca47ffe791 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Rangev2-Long-Update.yml.template @@ -0,0 +1,242 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile("range-encryptedFields-Long.json") }} +key_vault_data: [ {{ yamlfile("keys/key1-document.json") }} ] +tests: + - description: "FLE2 Range Long. Update." + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/etc/test-templates/fle2v2-Update.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-Update.yml.template new file mode 100644 index 0000000000..a2c68b1918 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-Update.yml.template @@ -0,0 +1,210 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {{ yamlfile ("encryptedFields.json") }} +key_vault_data: [ {{ yamlfile ("keys/key1-document.json") }} ] +tests: + - description: "Update can query an FLE2 indexed field" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + 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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + update: *collection_name + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + 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: {{ local_provider() }} + 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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: datakeys + filter: { + "$or": [ + { + "_id": { + "$in": [ + {{ yamlfile ("keys/key1-id.json") }} + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + update: *collection_name + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: update + - command_started_event: + command: + find: *collection_name + 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/source/client-side-encryption/etc/test-templates/fle2v2-validatorAndPartialFieldExpression.yml.template b/source/client-side-encryption/etc/test-templates/fle2v2-validatorAndPartialFieldExpression.yml.template new file mode 100644 index 0000000000..63f0ec3b20 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/fle2v2-validatorAndPartialFieldExpression.yml.template @@ -0,0 +1,169 @@ +# Requires libmongocrypt 1.8.0. +runOn: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] + +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +tests: + - description: "create with a validator on an unencrypted field is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "encryptedCollection" + - name: createCollection + object: database + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: "encryptedCollection" + - description: "create with a validator on an encrypted field is an error" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + collection: "encryptedCollection" + index: name + - description: "createIndexes with a partialFilterExpression on an encrypted field is an error" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + encryptedFieldsMap: + "default.encryptedCollection": {{ yamlfile ("encryptedFields.json") }} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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/source/client-side-encryption/etc/test-templates/gcpKMS.yml.template b/source/client-side-encryption/etc/test-templates/gcpKMS.yml.template new file mode 100644 index 0000000000..40e7f5bcc7 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/gcpKMS.yml.template @@ -0,0 +1,46 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema("all")}} +key_vault_data: [{{key("gcp")}}] + +tests: + - description: "Insert a document with auto encryption using GCP KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + gcp: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_gcp: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key("gcp")["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_gcp: {{ciphertext("string0", field="encrypted_string_gcp", schema="all")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/getMore.yml.template b/source/client-side-encryption/etc/test-templates/getMore.yml.template new file mode 100644 index 0000000000..6ff0153b4c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/getMore.yml.template @@ -0,0 +1,61 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } + - &doc2_encrypted { _id: 3, encrypted_string: {{ciphertext("string2", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "getMore with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + batchSize: 2 + filter: {} + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string1" } + - { _id: 3, encrypted_string: "string2" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: *collection_name + batchSize: 2 + command_name: find + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 2 + command_name: getMore + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/insert.yml.template b/source/client-side-encryption/etc/test-templates/insert.yml.template new file mode 100644 index 0000000000..f1627e1164 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/insert.yml.template @@ -0,0 +1,88 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "insertOne with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}}, random: { $$type: "binData" } } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "insertMany with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertMany + arguments: + documents: + - *doc0 + - &doc1 { _id: 2, encrypted_string: "string1" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0_encrypted + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/keyAltName.yml.template b/source/client-side-encryption/etc/test-templates/keyAltName.yml.template new file mode 100644 index 0000000000..47d0a85d08 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/keyAltName.yml.template @@ -0,0 +1,64 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert with encryption using key alt name" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_w_altname: "string0", altname: "altname" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {$or: [ { _id: { $in: [] } }, { keyAltNames: { $in: [ "altname" ] } } ] } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_w_altname: { $$type: "binData" }, altname: "altname" } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Replace with key alt name fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: {} + update: { $set: { encrypted_w_altname: "string0" } } + upsert: true + result: + errorContains: "A non-static (JSONPointer) keyId is not supported" + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/keyCache.yml.template b/source/client-side-encryption/etc/test-templates/keyCache.yml.template new file mode 100644 index 0000000000..4842a0bab2 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/keyCache.yml.template @@ -0,0 +1,69 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert with deterministic encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + keyExpirationMS: 1 + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: wait + object: testRunner + arguments: + ms: 50 # Wait long enough to account for coarse time resolution on Windows (CDRIVER-4526). + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + # The cache has expired and the key must be fetched again + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted diff --git a/source/client-side-encryption/etc/test-templates/kmipKMS.yml.template b/source/client-side-encryption/etc/test-templates/kmipKMS.yml.template new file mode 100644 index 0000000000..35a259abd1 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/kmipKMS.yml.template @@ -0,0 +1,83 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema("all")}} +key_vault_data: [{{key("kmip")}},{{key("kmip_delegated")}}] + +tests: + - description: "Insert a document with auto encryption using KMIP KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_kmip: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key("kmip")["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_kmip: {{ciphertext("string0", field="encrypted_string_kmip", schema="all")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + + - description: "Insert a document with auto encryption using KMIP delegated KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_string_kmip_delegated: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key("kmip_delegated")["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { _id: 1, encrypted_string_kmip_delegated: {{ciphertext("string0", field="encrypted_string_kmip_delegated", schema="all")}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/localKMS.yml.template b/source/client-side-encryption/etc/test-templates/localKMS.yml.template new file mode 100644 index 0000000000..a0b22fee1f --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/localKMS.yml.template @@ -0,0 +1,47 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema("local")}} +key_vault_data: [{{key("local")}}] + +tests: + - description: "Insert a document with auto encryption using local KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + local: {{local_provider()}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {{key("local")["_id"]}} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string", schema="local")}}, random: { $$type: "binData" } } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/malformedCiphertext.yml.template b/source/client-side-encryption/etc/test-templates/malformedCiphertext.yml.template new file mode 100644 index 0000000000..e58a638273 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/malformedCiphertext.yml.template @@ -0,0 +1,69 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0 + _id: 1 + encrypted_string: + $binary: + base64: {{ ciphertext("string1", field="encrypted_string")["$binary"]["base64"] }} + subType: "00" + - _id: 2 + encrypted_string: + $binary: + base64: "AQ==" + subType: "06" + - _id: 3 + encrypted_string: + $binary: + base64: "AQAAa2V2aW4gYWxiZXJ0c29uCg==" + subType: "06" + +# Since test requires invalid data to be inserted, use a local schema. +key_vault_data: [{{key()}}] + +tests: + - description: "Wrong subtype" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{ schema() }} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 1 } + result: + # gets returned without decryption + - *doc0 + - description: "Empty data" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{ schema() }} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 2 } + result: + errorContains: "malformed ciphertext" + - description: "Malformed data" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{ schema() }} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 3 } + result: + # ciphertext can only validate subtype (which is correct) + # but takes the 16 byte UUID to look up key. Fails to find. + errorContains: "not all keys requested were satisfied" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/missingKey.yml.template b/source/client-side-encryption/etc/test-templates/missingKey.yml.template new file mode 100644 index 0000000000..100e97c2c9 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/missingKey.yml.template @@ -0,0 +1,42 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "Insert with encryption on a missing key" + clientOptions: + autoEncryptOpts: + keyVaultNamespace: "keyvault.different" + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + result: + errorContains: "not all keys requested were satisfied" + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: different + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/namedKMS.yml.template b/source/client-side-encryption/etc/test-templates/namedKMS.yml.template new file mode 100644 index 0000000000..0006e9c304 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/namedKMS.yml.template @@ -0,0 +1,56 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {{schema(name="local:name2")}} +key_vault_data: [{{key(name="local:name2")}}] + +tests: + - description: "Automatically encrypt and decrypt with a named KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + "local:name2": {{local_provider(name="name2")}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key(name="local:name2")["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string", schema="local:name2")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted diff --git a/source/client-side-encryption/etc/test-templates/noSchema.yml.template b/source/client-side-encryption/etc/test-templates/noSchema.yml.template new file mode 100644 index 0000000000..6bd1d13b40 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/noSchema.yml.template @@ -0,0 +1,37 @@ +# Test auto encryption on a collection with no jsonSchema configured. +# This is a regression test for MONGOCRYPT-378/PYTHON-3188. +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "unencrypted" + +tests: + - description: "Insert on an unencrypted collection" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1 } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0 diff --git a/source/client-side-encryption/etc/test-templates/replaceOne.yml.template b/source/client-side-encryption/etc/test-templates/replaceOne.yml.template new file mode 100644 index 0000000000..665968a635 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/replaceOne.yml.template @@ -0,0 +1,54 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "replaceOne with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: replaceOne + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1", random: "abc" } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + u: { encrypted_string: {{ciphertext("string1", field="encrypted_string")}}, random: { $$type: "binData" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string1", field="encrypted_string")}}, random: { $$type: "binData" } } \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/timeoutMS.yml.template b/source/client-side-encryption/etc/test-templates/timeoutMS.yml.template new file mode 100644 index 0000000000..c98fac5bae --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/timeoutMS.yml.template @@ -0,0 +1,67 @@ +runOn: + - minServerVersion: "4.4" +database_name: &database_name "cse-timeouts-db" +collection_name: &collection_name "cse-timeouts-coll" + +data: [] +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +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: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + result: + isTimeoutError: true + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + maxTimeMS: { $$type: ["int", "long"] } + command_name: listCollections + + # Test that timeoutMS applies to the sum of all operations done for client-side encryption. This is done by blocking + # listCollections and find for 30ms each and running an insertOne with timeoutMS=50. There should be one + # listCollections command and one "find" command, so the sum should take more than timeoutMS. A second listCollections + # event doesn't occur due to the internal MongoClient lacking configured auto encryption, plus libmongocrypt holds the + # collection schema in cache for a minute. + # + # This test does not include command monitoring expectations because the exact command sequence is dependent on the + # amount of time taken by mongocryptd communication. In slow runs, mongocryptd communication can breach the timeout + # and result in the final "find" not being sent. + - description: "remaining timeoutMS applied to find to get keyvault data" + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections", "find"] + blockConnection: true + blockTimeMS: 30 + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: *doc0 + result: + isTimeoutError: true diff --git a/source/client-side-encryption/etc/test-templates/types.yml.template b/source/client-side-encryption/etc/test-templates/types.yml.template new file mode 100644 index 0000000000..c216210e7c --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/types.yml.template @@ -0,0 +1,501 @@ +# Attempt to round trip some BSON types. +# Note: db pointer is excluded since it is deprecated and numberlong is excluded due to different driver interpretations of { $numberLong: '123' } in relaxed JSON parsing. + +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {} +key_vault_data: [{{key()}}] + +tests: + - description: "type=objectId" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("objectId")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_objectId: {"$oid": "AAAAAAAAAAAAAAAAAAAAAAAA"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc0 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_objectId: {{ciphertext("test", field="encrypted_objectId")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "type=symbol" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("symbol")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_symbol: {"$symbol": "test"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc1 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { _id: 1, encrypted_symbol: {{ciphertext("test", field="encrypted_symbol")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted + - description: "type=int" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("int")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc2 { _id: 1, encrypted_int: {"$numberInt": "123"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc2 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc2_encrypted { _id: 1, encrypted_int: {{ciphertext("test", field="encrypted_int32")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc2_encrypted + - description: "type=double" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("double")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc4 { _id: 1, encrypted_double: {"$numberDouble": "1.23"} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: double" + # After it is: + # "Cannot encrypt element of type: double" + # Only check for the common suffix. + errorContains: "element of type: double" + - description: "type=decimal" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("decimal")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc5 { _id: 1, encrypted_decimal: {"$numberDecimal": "1.23"} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: decimal" + # After it is: + # "Cannot encrypt element of type: decimal" + # Only check for the common suffix. + errorContains: "element of type: decimal" + - description: "type=binData" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("binData")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc6 { _id: 1, encrypted_binData: {"$binary": { base64: "AAAA", subType: "00" } } } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc6 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc6_encrypted { _id: 1, encrypted_binData: {{ciphertext("test", field="encrypted_binData")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc6_encrypted + - description: "type=javascript" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("javascript")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc7 { _id: 1, encrypted_javascript: {"$code": "var x = 1;" } } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc7 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc7_encrypted { _id: 1, encrypted_javascript: {{ciphertext("test", field="encrypted_javascript")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc7_encrypted + - description: "type=javascriptWithScope" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("javascriptWithScope")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc8 { _id: 1, encrypted_javascriptWithScope: {"$code": "var x = 1;", "$scope": {} } } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: javascriptWithScope" + # After it is: + # "Cannot encrypt element of type: javascriptWithScope" + # Only check for the common suffix. + errorContains: "element of type: javascriptWithScope" + - description: "type=object" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("object")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc9 { _id: 1, encrypted_object: {} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: object" + # After it is: + # "Cannot encrypt element of type: object" + # Only check for the common suffix. + errorContains: "element of type: object" + - description: "type=timestamp" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("timestamp")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc10 { _id: 1, encrypted_timestamp: {$timestamp: {t: 123, i: 456}} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc10 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc10_encrypted { _id: 1, encrypted_timestamp: {{ciphertext("test", field="encrypted_timestamp")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc10_encrypted + - description: "type=regex" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("regex")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc11 { _id: 1, encrypted_regex: {$regularExpression: { pattern: "test", options: ""}} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc11 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc11_encrypted { _id: 1, encrypted_regex: {{ciphertext("test", field="encrypted_regex")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc11_encrypted + - description: "type=date" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("date")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc13 { _id: 1, encrypted_date: {$date: { $numberLong: "123" }} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc13 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc13_encrypted { _id: 1, encrypted_date: {{ciphertext("test", field="encrypted_date")}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc13_encrypted + - description: "type=minKey" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("minKey")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc14 { _id: 1, encrypted_minKey: {$minKey: 1} } + result: + errorContains: "Cannot encrypt element of type: minKey" + - description: "type=maxKey" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("maxKey")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc15 { _id: 1, encrypted_maxKey: {$maxKey: 1} } + result: + errorContains: "Cannot encrypt element of type: maxKey" + - description: "type=undefined" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("undefined")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc16 { _id: 1, encrypted_undefined: {$undefined: true} } + result: + errorContains: "Cannot encrypt element of type: undefined" + - description: "type=array" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("array")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc17 { _id: 1, encrypted_array: [] } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: array" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common suffix. + errorContains: "element of type: array" + - description: "type=bool" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("bool")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc18 { _id: 1, encrypted_bool: true } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: bool" + # After it is: + # "Cannot encrypt element of type: bool" + # Only check for the common suffix. + errorContains: "element of type: bool" + - description: "type=null" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {{schema_w_type("null")}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc19 { _id: 1, encrypted_null: true } + result: + errorContains: "Cannot encrypt element of type: null" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/unsupportedCommand.yml.template b/source/client-side-encryption/etc/test-templates/unsupportedCommand.yml.template new file mode 100644 index 0000000000..ca20a0e4d7 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/unsupportedCommand.yml.template @@ -0,0 +1,25 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, x: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, x: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "mapReduce deterministic encryption (unsupported)" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: mapReduce + arguments: + map: { $code: "function inc() { return emit(0, this.x + 1) }" } + reduce: { $code: "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" } + out: { inline: 1 } + result: + errorContains: "command not supported for auto encryption: mapreduce" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/updateMany.yml.template b/source/client-side-encryption/etc/test-templates/updateMany.yml.template new file mode 100644 index 0000000000..cc43c7aa94 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/updateMany.yml.template @@ -0,0 +1,70 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } + - &doc1_encrypted { _id: 2, encrypted_string: {{ciphertext("string1", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "updateMany with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateMany + arguments: + filter: { encrypted_string: { $in: [ "string0", "string1" ] } } + update: { $set: { encrypted_string: "string2", random: "abc" } } + result: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $in: [ {{ciphertext("string0", field="encrypted_string")}}, {{ciphertext("string1", field="encrypted_string")}} ] } } + u: { $set: { encrypted_string: {{ciphertext("string2", field="encrypted_string")}}, random: { $$type: "binData" } } } + multi: true + upsert: false + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string2", field="encrypted_string")}}, random: { $$type: "binData" } } + - { _id: 2, encrypted_string: {{ciphertext("string2", field="encrypted_string")}}, random: { $$type: "binData" } } + - description: "updateMany fails when filtering on a random field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateMany + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/updateOne.yml.template b/source/client-side-encryption/etc/test-templates/updateOne.yml.template new file mode 100644 index 0000000000..4bdf8ade29 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/updateOne.yml.template @@ -0,0 +1,160 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {{ciphertext("string0", field="encrypted_string")}} } +json_schema: {{schema()}} +key_vault_data: [{{key()}}] + +tests: + - description: "updateOne with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1", random: "abc" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {{key()["_id"]}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {{ciphertext("string0", field="encrypted_string")}} } } + u: { $set: {encrypted_string: {{ciphertext("string1", field="encrypted_string")}}, random: { $$type: "binData" } } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {{ciphertext("string1", field="encrypted_string")}}, random: { $$type: "binData"} } + - description: "updateOne fails when filtering on a random field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "$unset works with an encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $unset: { encrypted_string: "" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + update: *collection_name + updates: + - q: { } + u: { $unset: {encrypted_string: "" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1 } + - description: "$rename works if target value has same encryption options" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $rename: { encrypted_string: "encrypted_string_equivalent" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + update: *collection_name + updates: + - q: { } + u: { $rename: {encrypted_string: "encrypted_string_equivalent" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string_equivalent: {{ciphertext("string0", field="encrypted_string")}} } + - description: "$rename fails if target value has different encryption options" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $rename: { encrypted_string: "random" } } + result: + errorContains: "$rename between two encrypted fields must have the same metadata or both be unencrypted" + - description: "an invalid update (no $ operators) is validated and errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { encrypted_string: "random" } + result: + errorContains: "" # Note, drivers differ in the error message. Just ensure an error is thrown. \ No newline at end of file diff --git a/source/client-side-encryption/etc/test-templates/validatorAndPartialFieldExpression.yml.template b/source/client-side-encryption/etc/test-templates/validatorAndPartialFieldExpression.yml.template new file mode 100644 index 0000000000..a876c1f783 --- /dev/null +++ b/source/client-side-encryption/etc/test-templates/validatorAndPartialFieldExpression.yml.template @@ -0,0 +1,166 @@ +# This test requires libmongocrypt 1.5.0-alpha2. +runOn: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: "6.0.0" + +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +tests: + - description: "create with a validator on an unencrypted field is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "encryptedCollection" + - name: createCollection + object: database + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: "encryptedCollection" + - description: "create with a validator on an encrypted field is an error" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + collection: "encryptedCollection" + index: name + - description: "createIndexes with a partialFilterExpression on an encrypted field is an error" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {{ local_provider() }} + schemaMap: + "default.encryptedCollection": {{schema('basic')}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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" \ No newline at end of file diff --git a/source/client-side-encryption/etc/update-corpus.py b/source/client-side-encryption/etc/update-corpus.py new file mode 100644 index 0000000000..904b5460fc --- /dev/null +++ b/source/client-side-encryption/etc/update-corpus.py @@ -0,0 +1,28 @@ +import json +import sys + +description = """Update the existing corpus data from a file with only deterministically encrypted fields. +""" + +if len(sys.argv) != 3: + print(description) + print("usage: python update-corpus.py ") + print("Updates the deterministic fields of in place.") + sys.exit(1) + +new_corpus_path = sys.argv[1] +old_corpus_path = sys.argv[2] +new_corpus_data = json.loads(open(new_corpus_path, "r").read()) +old_corpus_data = json.loads(open(old_corpus_path, "r").read()) + +for (key, val) in new_corpus_data.items(): + if key in ["_id", "altname_aws", "altname_local", "altname_azure", "altname_gcp", "altname_kmip"]: + continue + if new_corpus_data[key]["algo"] == "det": + old_corpus_data[key] = new_corpus_data[key] + # add random field if it did not exist + if new_corpus_data[key]["algo"] == "rand" and key not in old_corpus_data: + old_corpus_data[key] = new_corpus_data[key] + +open(old_corpus_path, "w").write(json.dumps(old_corpus_data, indent=2)) +print ("updated %s" % (old_corpus_path)) diff --git a/source/client-side-encryption/etc/validate-corpus.py b/source/client-side-encryption/etc/validate-corpus.py new file mode 100644 index 0000000000..e9af21ad7f --- /dev/null +++ b/source/client-side-encryption/etc/validate-corpus.py @@ -0,0 +1,35 @@ +import json +import sys + +description = """Validate that the same deterministically encrypted values in the corpus encrypt to the same ciphertext in the encrypted corpus. +""" + +if len(sys.argv) != 3: + print(description) + print("usage: python validate-corpus.py ") + sys.exit(1) + +corpus = json.loads(open(sys.argv[1], "r").read()) +corpus_encrypted = json.loads(open(sys.argv[2], "r").read()) + +# Get all deterministically encrypted fields that have the same value + kms + type +def get_matching_fields (item): + for (key, val) in corpus.items(): + if key == "_id" or key == "altname_aws" or key == "altname_local": + continue + if val["value"] == item["value"] and val["algo"] == "det" and item["algo"] == val["algo"] and val["kms"] == item["kms"] and val["type"] == item["type"]: + yield key + +count = 0 +for (key, val) in corpus.items(): + if key == "_id" or key == "altname_aws" or key == "altname_local": + continue + for matching_field in get_matching_fields (val): + count += 1 + if matching_field == key: + continue + if corpus_encrypted[matching_field]["value"] != corpus_encrypted[key]["value"]: + print ("error: %s does not match %s" % (matching_field, key)) + sys.exit(1) + +print("validated that %d ciphertexts are exact matches" % count) \ No newline at end of file diff --git a/source/client-side-encryption/external/external-key.json b/source/client-side-encryption/external/external-key.json new file mode 100644 index 0000000000..b3fe0723b0 --- /dev/null +++ b/source/client-side-encryption/external/external-key.json @@ -0,0 +1,31 @@ +{ + "status": { + "$numberInt": "1" + }, + "_id": { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + }, + "updateDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyAltNames": [ "local" ] +} \ No newline at end of file diff --git a/source/client-side-encryption/external/external-schema.json b/source/client-side-encryption/external/external-schema.json new file mode 100644 index 0000000000..7d8cad8c33 --- /dev/null +++ b/source/client-side-encryption/external/external-schema.json @@ -0,0 +1,19 @@ +{ + "properties": { + "encrypted": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" +} diff --git a/source/client-side-encryption/includes/components.png b/source/client-side-encryption/includes/components.png new file mode 100644 index 0000000000..585c9b9928 Binary files /dev/null and b/source/client-side-encryption/includes/components.png differ diff --git a/source/client-side-encryption/includes/wire_protocol_diagram.png b/source/client-side-encryption/includes/wire_protocol_diagram.png new file mode 100644 index 0000000000..4542548187 Binary files /dev/null and b/source/client-side-encryption/includes/wire_protocol_diagram.png differ diff --git a/source/client-side-encryption/limits/limits-doc.json b/source/client-side-encryption/limits/limits-doc.json new file mode 100644 index 0000000000..53de52326c --- /dev/null +++ b/source/client-side-encryption/limits/limits-doc.json @@ -0,0 +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", + "99": "a" +} \ No newline at end of file diff --git a/source/client-side-encryption/limits/limits-encryptedFields.json b/source/client-side-encryption/limits/limits-encryptedFields.json new file mode 100644 index 0000000000..c52a0271e1 --- /dev/null +++ b/source/client-side-encryption/limits/limits-encryptedFields.json @@ -0,0 +1,14 @@ +{ + "fields": [ + { + "keyId": { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "path": "foo", + "bsonType": "string" + } + ] +} \ No newline at end of file diff --git a/source/client-side-encryption/limits/limits-key.json b/source/client-side-encryption/limits/limits-key.json new file mode 100644 index 0000000000..b3fe0723b0 --- /dev/null +++ b/source/client-side-encryption/limits/limits-key.json @@ -0,0 +1,31 @@ +{ + "status": { + "$numberInt": "1" + }, + "_id": { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "masterKey": { + "provider": "local" + }, + "updateDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1557827033449" + } + }, + "keyAltNames": [ "local" ] +} \ No newline at end of file diff --git a/source/client-side-encryption/limits/limits-qe-doc.json b/source/client-side-encryption/limits/limits-qe-doc.json new file mode 100644 index 0000000000..71efbf4068 --- /dev/null +++ b/source/client-side-encryption/limits/limits-qe-doc.json @@ -0,0 +1,3 @@ +{ + "foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} \ No newline at end of file diff --git a/source/client-side-encryption/limits/limits-schema.json b/source/client-side-encryption/limits/limits-schema.json new file mode 100644 index 0000000000..c06908d9ce --- /dev/null +++ b/source/client-side-encryption/limits/limits-schema.json @@ -0,0 +1,1405 @@ +{ + "properties": { + "00": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "01": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "02": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "03": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "04": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "05": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "06": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "07": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "08": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "09": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "10": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "11": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "12": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "13": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "14": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "15": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "16": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "17": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "18": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "19": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "20": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "21": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "22": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "23": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "24": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "25": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "26": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "27": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "28": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "29": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "30": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "31": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "32": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "33": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "34": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "35": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "36": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "37": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "38": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "39": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "40": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "41": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "42": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "43": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "44": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "45": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "46": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "47": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "48": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "49": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "50": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "51": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "52": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "53": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "54": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "55": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "56": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "57": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "58": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "59": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "60": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "61": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "62": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "63": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "64": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "65": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "66": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "67": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "68": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "69": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "70": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "71": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "72": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "73": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "74": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "75": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "76": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "77": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "78": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "79": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "80": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "81": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "82": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "83": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "84": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "85": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "86": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "87": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "88": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "89": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "90": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "91": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "92": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "93": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "94": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "95": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "96": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "97": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "98": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "99": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "LOCALAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" +} \ No newline at end of file diff --git a/source/client-side-encryption/subtype6.rst b/source/client-side-encryption/subtype6.rst new file mode 100644 index 0000000000..9e7010e822 --- /dev/null +++ b/source/client-side-encryption/subtype6.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `subtype6.md <../bson-binary-encrypted/binary-encrypted.md>`_. diff --git a/source/client-side-encryption/tests/README.md b/source/client-side-encryption/tests/README.md new file mode 100644 index 0000000000..ecd478a14c --- /dev/null +++ b/source/client-side-encryption/tests/README.md @@ -0,0 +1,4482 @@ +# Client Side Encryption Tests + +______________________________________________________________________ + +## Introduction + +This document describes the format of the driver spec tests included in the JSON and YAML files included in the `legacy` +sub-directory. Tests in the `unified` directory are written using the +[Unified Test Format](../../unified-test-format/unified-test-format.md). + +The `timeoutMS.yml`/`timeoutMS.json` files in this directory contain tests for the `timeoutMS` option and its +application to the client-side encryption feature. Drivers MUST only run these tests after implementing the +[Client Side Operations Timeout](../../client-side-operations-timeout/client-side-operations-timeout.md) specification. + +Additional prose tests, that are not represented in the spec tests, are described and MUST be implemented by all +drivers. + +Running spec and prose tests require that the driver and server both support Client-Side Field Level Encryption. CSFLE +is supported when all of the following are true: + +- Server version is 4.2.0 or higher. Legacy spec test runners can rely on `runOn.minServerVersion` for this check. +- Driver has libmongocrypt enabled +- At least one of [crypt_shared](../client-side-encryption.md#crypt_shared) and/or + [mongocryptd](../client-side-encryption.md#mongocryptd) is available. + +## Spec Test Format + +The spec tests format is an extension of the +[transactions spec legacy test format](../../transactions/tests/legacy-test-format.md) with some additions: + +- A `json_schema` to set on the collection used for operations. +- An `encrypted_fields` to set on the collection used for operations. +- A `key_vault_data` of data that should be inserted in the key vault collection before each test. +- Introduction `autoEncryptOpts` to `clientOptions` +- Addition of `$db`to command in`command_started_event` +- Addition of `$$type` to `command_started_event` and outcome. + +The semantics of `$$type` is that any actual value matching one of the types indicated by either a BSON type string or +an array of BSON type strings is considered a match. + +For example, the following matches a command_started_event for an insert of a document where `random` must be of type +`binData`: + +```text +- command_started_event: + command: + insert: *collection_name + documents: + - { random: { $$type: "binData" } } + ordered: true + command_name: insert +``` + +The following matches a command_started_event for an insert of a document where `random` must be of type `binData` or +`string`: + +```text +- command_started_event: + command: + insert: *collection_name + documents: + - { random: { $$type: ["binData", "string"] } } + ordered: true + command_name: insert +``` + +The values of `$$type` correspond to +[these documented string representations of BSON types](https://www.mongodb.com/docs/manual/reference/bson-types/). + +Each YAML file has the following keys: + +- `runOn` Unchanged from Transactions spec tests. +- `database_name` Unchanged from Transactions spec tests. +- `collection_name` Unchanged from Transactions spec tests. +- `data` Unchanged from Transactions spec tests. +- `json_schema` A JSON Schema that should be set on the collection (using `createCollection`) before each test run. +- `encrypted_fields` An encryptedFields option that should be set on the collection (using `createCollection`) before + each test run. +- `key_vault_data` The data that should exist in the key vault collection under test before each test run. +- `tests`: An array of tests that are to be run independently of each other. Each test will have some or all of the + following fields: + - `description`: Unchanged from Transactions spec tests. + - `skipReason`: Unchanged from Transactions spec tests. + - `useMultipleMongoses`: Unchanged from Transactions spec tests. + - `failPoint`: Unchanged from Transactions spec tests. + - `clientOptions`: Optional, parameters to pass to MongoClient(). + - `autoEncryptOpts`: Optional + - `kmsProviders` A dictionary of KMS providers to set on the key vault ("aws" or "local") + - `aws` The AWS KMS provider. An empty object. Drivers MUST fill in AWS credentials (`accessKeyId`, + `secretAccessKey`) from the environment. + - `azure` The Azure KMS provider credentials. An empty object. Drivers MUST fill in Azure credentials + (`tenantId`, `clientId`, and `clientSecret`) from the environment. + - `gcp` The GCP KMS provider credentials. An empty object. Drivers MUST fill in GCP credentials (`email`, + `privateKey`) from the environment. + - `local` or `local:name2` The local KMS provider. + - `key` A 96 byte local key. + - `kmip` The KMIP KMS provider credentials. An empty object. Drivers MUST fill in KMIP credentials (`endpoint`, + and TLS options). + - `schemaMap`: Optional, a map from namespaces to local JSON schemas. + - `keyVaultNamespace`: Optional, a namespace to the key vault collection. Defaults to "keyvault.datakeys". + - `bypassAutoEncryption`: Optional, a boolean to indicate whether or not auto encryption should be bypassed. + Defaults to `false`. + - `encryptedFieldsMap` An optional document. The document maps collection namespace to `EncryptedFields` + documents. + - `operations`: Array of documents, each describing an operation to be executed. Each document has the following + fields: + - `name`: Unchanged from Transactions spec tests. + - `object`: Unchanged from Transactions spec tests.. Defaults to "collection" if omitted. + - `collectionOptions`: Unchanged from Transactions spec tests. + - `command_name`: Unchanged from Transactions spec tests. + - `arguments`: Unchanged from Transactions spec tests. + - `result`: Same as the Transactions spec test format with one addition: if the operation is expected to return an + error, the `result` document may contain an `isTimeoutError` boolean field. If `true`, the test runner MUST + assert that the error represents a timeout due to the use of the `timeoutMS` option. If `false`, the test runner + MUST assert that the error does not represent a timeout. + - `expectations`: Unchanged from Transactions spec tests. + - `outcome`: Unchanged from Transactions spec tests. + +## Credentials + +Test credentials are available in AWS Secrets Manager. See + for more background +on how the secrets are managed. + +Test credentials to KMS are located in "drivers/csfle". + +Test credentials to create environments are available in "drivers/gcpkms" and "drivers/azurekms". + +## Use as integration tests + +Do the following before running spec tests: + +- If available for the platform under test, obtain a [crypt_shared](../client-side-encryption.md#crypt_shared) binary + and place it in a location accessible to the tests. Refer to: + [Using crypt_shared](../client-side-encryption.md#enabling-crypt_shared) +- Start the mongocryptd process. +- Start a mongod process with **server version 4.2.0 or later**. +- Place credentials somewhere in the environment outside of tracked code. (If testing on evergreen, project variables + are a good place). +- Start a KMIP test server on port 5698 by running + [drivers-evergreen-tools/.evergreen/csfle/kms_kmip_server.py](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_kmip_server.py). + +Load each YAML (or JSON) file using a Canonical Extended JSON parser. + +If the test file name matches the regular expression `fle2-Range-.*-Correctness`, drivers MAY skip the test on macOS. +The `fle2-Range` tests are very slow on macOS and do not provide significant additional test coverage. + +Then for each element in `tests`: + +1. If the `skipReason` field is present, skip this test completely. + +2. If the `key_vault_data` field is present: + + 1. Drop the `keyvault.datakeys` collection using writeConcern "majority". + 2. Insert the data specified into the `keyvault.datakeys` with write concern "majority". + +3. Create a MongoClient. + +4. Create a collection object from the MongoClient, using the `database_name` and `collection_name` fields from the YAML + file. Drop the collection with writeConcern "majority". If a `json_schema` is defined in the test, use the + `createCollection` command to explicitly create the collection: + + ```typescript + {"create": , "validator": {"$jsonSchema": }} + ``` + + If `encrypted_fields` is defined in the test, the required collections and index described in + [Create and Drop Collection Helpers](../client-side-encryption.md#queryable-encryption-create-and-drop-collection-helpers) + must be created: + + - Use the `dropCollection` helper with `encrypted_fields` as an option and writeConcern "majority". + - Use the `createCollection` helper with `encrypted_fields` as an option. + +5. If the YAML file contains a `data` array, insert the documents in `data` into the test collection, using writeConcern + "majority". + +6. Create a **new** MongoClient using `clientOptions`. + + 1. If `autoEncryptOpts` includes `aws`, `awsTemporary`, `awsTemporaryNoSessionToken`, `azure`, `gcp`, and/or `kmip` + as a KMS provider, pass in credentials from the environment. + - `awsTemporary`, and `awsTemporaryNoSessionToken` require temporary AWS credentials. These can be retrieved using + the csfle + [set-temp-creds.sh](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/csfle) + script. + + - `aws`, `awsTemporary`, and `awsTemporaryNoSessionToken` are mutually exclusive. + + `aws` should be substituted with: + + ```javascript + "aws": { + "accessKeyId": , + "secretAccessKey": + } + ``` + + `awsTemporary` should be substituted with: + + ```javascript + "aws": { + "accessKeyId": , + "secretAccessKey": + "sessionToken": + } + ``` + + `awsTemporaryNoSessionToken` should be substituted with: + + ```javascript + "aws": { + "accessKeyId": , + "secretAccessKey": + } + ``` + + `gcp` should be substituted with: + + ```javascript + "gcp": { + "email": , + "privateKey": , + } + ``` + + `azure` should be substituted with: + + ```javascript + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + } + ``` + + `local` should be substituted with: + + ```javascript + "local": { "key": } + ``` + + `kmip` should be substituted with: + + ```javascript + "kmip": { "endpoint": "localhost:5698" } + ``` + + Configure KMIP TLS connections to use the following options: + + - `tlsCAFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). + This MAY be configured system-wide. + - `tlsCertificateKeyFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem). + + The method of passing TLS options for KMIP TLS connections is driver dependent. + 2. If `autoEncryptOpts` does not include `keyVaultNamespace`, default it to `keyvault.datakeys`. + +7. For each element in `operations`: + + - Enter a "try" block or your programming language's closest equivalent. + + - Create a Database object from the MongoClient, using the `database_name` field at the top level of the test file. + + - Create a Collection object from the Database, using the `collection_name` field at the top level of the test file. + If `collectionOptions` is present create the Collection object with the provided options. Otherwise create the + object with the default options. + + - Execute the named method on the provided `object`, passing the arguments listed. + + - If the driver throws an exception / returns an error while executing this series of operations, store the error + message and server error code. + + - If the result document has an "errorContains" field, verify that the method threw an exception or returned an + error, and that the value of the "errorContains" field matches the error string. "errorContains" is a substring + (case-insensitive) of the actual error message. + + If the result document has an "errorCodeName" field, verify that the method threw a command failed exception or + returned an error, and that the value of the "errorCodeName" field matches the "codeName" in the server error + response. + + If the result document has an "errorLabelsContain" field, verify that the method threw an exception or returned an + error. Verify that all of the error labels in "errorLabelsContain" are present in the error or exception using + the `hasErrorLabel` method. + + If the result document has an "errorLabelsOmit" field, verify that the method threw an exception or returned an + error. Verify that none of the error labels in "errorLabelsOmit" are present in the error or exception using the + `hasErrorLabel` method. + + - If the operation returns a raw command response, eg from `runCommand`, then compare only the fields present in the + expected result document. Otherwise, compare the method's return value to `result` using the same logic as the + CRUD Spec Tests runner. + +8. If the test includes a list of command-started events in `expectations`, compare them to the actual command-started + events using the same logic as the + [Command Monitoring spec legacy test runner](../../command-logging-and-monitoring/tests/README.md). + +9. For each element in `outcome`: + + - If `name` is "collection", create a new MongoClient *without encryption* and verify that the test collection + contains exactly the documents in the `data` array. Ensure this find reads the latest data by using **primary + read preference** with **local read concern** even when the MongoClient is configured with another read + preference or read concern. + +The spec test MUST be run with *and* without auth. + +## Using `crypt_shared` + +On platforms where [crypt_shared](../client-side-encryption.md#crypt_shared) is available, drivers should prefer to test +with the `crypt_shared` library instead of spawning mongocryptd. + +[crypt_shared](../client-side-encryption.md#crypt_shared) is released alongside the server. +[crypt_shared](../client-side-encryption.md#crypt_shared) is only available in versions 6.0 and above. + +mongocryptd is released alongside the server. mongocryptd is available in versions 4.2 and above. + +Drivers MUST run all tests with mongocryptd on at least one platform for all tested server versions. + +Drivers MUST run all tests with [crypt_shared](../client-side-encryption.md#crypt_shared) on at least one platform for +all tested server versions. For server versions < 6.0, drivers MUST test with the latest major release of +[crypt_shared](../client-side-encryption.md#crypt_shared). Using the latest major release of +[crypt_shared](../client-side-encryption.md#crypt_shared) is supported with older server versions. + +Note that some tests assert on mongocryptd-related behaviors (e.g. the `mongocryptdBypassSpawn` test). + +Drivers under test should load the [crypt_shared](../client-side-encryption.md#crypt_shared) library using either the +`cryptSharedLibPath` public API option (as part of the AutoEncryption `extraOptions`), or by setting a special search +path instead. + +Some tests will require *not* using [crypt_shared](../client-side-encryption.md#crypt_shared). For such tests, one +should ensure that `crypt_shared` will not be loaded. Refer to the client-side-encryption documentation for information +on "disabling" `crypt_shared` and setting library search paths. + +> [!NOTE] +> The [crypt_shared](../client-side-encryption.md#crypt_shared) dynamic library can be obtained using the +> [mongodl](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/mongodl.py) Python script +> from [drivers-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools/): +> +> ```shell +> $ python3 mongodl.py --component=crypt_shared --version= --out=./crypt_shared/ +> ``` +> +> Other versions of `crypt_shared` are also available. Please use the `--list` option to see versions. + +## Prose Tests + +Tests for the ClientEncryption type are not included as part of the YAML tests. + +In the prose tests LOCAL_MASTERKEY refers to the following base64: + +```text +Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk +``` + +Perform all applicable operations on key vault collections (e.g. inserting an example data key, or running a find +command) with readConcern/writeConcern "majority". + +### 1. Custom Key Material Test + +1. Create a `MongoClient` object (referred to as `client`). + +2. Using `client`, drop the collection `keyvault.datakeys`. + +3. Create a `ClientEncryption` object (referred to as `client_encryption`) with `client` set as the `keyVaultClient`. + +4. Using `client_encryption`, create a data key with a `local` KMS provider and the following custom key material (given + as base64): + + ```text + xPTAjBRG5JiPm+d3fj6XLi2q5DMXUS/f1f+SMAlhhwkhDRL0kr8r9GDLIGTAGlvC+HVjSIgdL+RKwZCvpXSyxTICWSXTUYsWYPyu3IoHbuBZdmw2faM3WhcRIgbMReU5 + ``` + +5. Find the resulting key document in `keyvault.datakeys`, save a copy of the key document, then remove the key document + from the collection. + +6. Replace the `_id` field in the copied key document with a UUID with base64 value `AAAAAAAAAAAAAAAAAAAAAA==` (16 bytes + all equal to `0x00`) and insert the modified key document into `keyvault.datakeys` with majority write concern. + +7. Using `client_encryption`, encrypt the string `"test"` with the modified data key using the + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic` algorithm and assert the resulting value is equal to the following + (given as base64): + + ```text + AQAAAAAAAAAAAAAAAAAAAAACz0ZOLuuhEYi807ZXTdhbqhLaS2/t9wLifJnnNYwiw79d75QYIZ6M/aYC1h9nCzCjZ7pGUpAuNnkUhnIXM3PjrA== + ``` + +### 2. Data Key and Double Encryption + +First, perform the setup. + +1. Create a MongoClient without encryption enabled (referred to as `client`). Enable command monitoring to listen for + command_started events. + +2. Using `client`, drop the collections `keyvault.datakeys` and `db.coll`. + +3. Create the following: + + - A MongoClient configured with auto encryption (referred to as `client_encrypted`) + - A `ClientEncryption` object (referred to as `client_encryption`) + + Configure both objects with the following KMS providers: + + ```javascript + { + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + }, + "gcp": { + "email": , + "privateKey": , + } + "local": { "key": }, + "kmip": { "endpoint": "localhost:5698" } + } + ``` + + Configure KMIP TLS connections to use the following options: + + - `tlsCAFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). + This MAY be configured system-wide. + - `tlsCertificateKeyFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem). + + The method of passing TLS options for KMIP TLS connections is driver dependent. + + Configure both objects with `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure the `MongoClient` with the following `schema_map`: + + ```javascript + { + "db.coll": { + "bsonType": "object", + "properties": { + "encrypted_placeholder": { + "encrypt": { + "keyId": "/placeholder", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + } + ``` + + Configure `client_encryption` with the `keyVaultClient` of the previously created `client`. + +For each KMS provider (`aws`, `azure`, `gcp`, `local`, and `kmip`), referred to as `provider_name`, run the following +test. + +1. Call `client_encryption.createDataKey()`. + - Set keyAltNames to `["_altname"]`. + + - Set the masterKey document based on `provider_name`. + + For "aws": + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + } + ``` + + For "azure": + + ```javascript + { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + ``` + + For "gcp": + + ```javascript + { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + ``` + + For "kmip": + + ```javascript + {} + ``` + + For "local", do not set a masterKey document. + + - Expect a BSON binary with subtype 4 to be returned, referred to as `datakey_id`. + + - Use `client` to run a `find` on `keyvault.datakeys` by querying with the `_id` set to the `datakey_id`. + + - Expect that exactly one document is returned with the "masterKey.provider" equal to `provider_name`. + + - Check that `client` captured a command_started event for the `insert` command containing a majority writeConcern. +2. Call `client_encryption.encrypt()` with the value `"hello "`, the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`, and the `key_id` of `datakey_id`. + - Expect the return value to be a BSON binary subtype 6, referred to as `encrypted`. + - Use `client_encrypted` to insert `{ _id: "", "value": }` into `db.coll`. + - Use `client_encrypted` to run a find querying with `_id` of `""` and expect `value` to be + `"hello "`. +3. Call `client_encryption.encrypt()` with the value `"hello "`, the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`, and the `key_alt_name` of `_altname`. + - Expect the return value to be a BSON binary subtype 6. Expect the value to exactly match the value of `encrypted`. +4. Test explicit encrypting an auto encrypted field. + - Use `client_encrypted` to attempt to insert `{ "encrypted_placeholder": }` + - Expect an exception to be thrown, since this is an attempt to auto encrypt an already encrypted value. + +### 3. External Key Vault Test + +Run the following tests twice, parameterized by a boolean `withExternalKeyVault`. + +1. Create a MongoClient without encryption enabled (referred to as `client`). + +2. Using `client`, drop the collections `keyvault.datakeys` and `db.coll`. Insert the document + [external/external-key.json](../external/external-key.json) into `keyvault.datakeys`. + +3. Create the following: + + - A MongoClient configured with auto encryption (referred to as `client_encrypted`) + - A `ClientEncryption` object (referred to as `client_encryption`) + + Configure both objects with the `local` KMS providers as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure both objects with `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure `client_encrypted` to use the schema [external/external-schema.json](../external/external-schema.json) for + `db.coll` by setting a schema map like: `{ "db.coll": }` + + If `withExternalKeyVault == true`, configure both objects with an external key vault client. The external client MUST + connect to the same MongoDB cluster that is being tested against, except it MUST use the username `fake-user` and + password `fake-pwd`. + +4. Use `client_encrypted` to insert the document `{"encrypted": "test"}` into `db.coll`. If + `withExternalKeyVault == true`, expect an authentication exception to be thrown. Otherwise, expect the insert to + succeed. + +5. Use `client_encryption` to explicitly encrypt the string `"test"` with key ID `LOCALAAAAAAAAAAAAAAAAA==` and + deterministic algorithm. If `withExternalKeyVault == true`, expect an authentication exception to be thrown. + Otherwise, expect the insert to succeed. + +### 4. BSON Size Limits and Batch Splitting + +First, perform the setup. + +1. Create a MongoClient without encryption enabled (referred to as `client`). + +2. Using `client`, drop and create the collection `db.coll` configured with the included JSON schema + [limits/limits-schema.json](../limits/limits-schema.json). + +3. If using MongoDB 8.0+, use `client` to drop and create the collection `db.coll2` configured with the included + encryptedFields [limits/limits-encryptedFields.json](../limits/limits-encryptedFields.json). + +4. Using `client`, drop the collection `keyvault.datakeys`. Insert the document + [limits/limits-key.json](../limits/limits-key.json) + +5. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure with the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + +Using `client_encrypted` perform the following operations: + +1. Insert `{ "_id": "over_2mib_under_16mib", "unencrypted": }` into `coll`. + + Expect this to succeed since this is still under the `maxBsonObjectSize` limit. + +2. Insert the document [limits/limits-doc.json](../limits/limits-doc.json) concatenated with + `{ "_id": "encryption_exceeds_2mib", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }` into + `coll`. Note: limits-doc.json is a 1005 byte BSON document that encrypts to a ~10,000 byte document. + + Expect this to succeed since after encryption this still is below the normal maximum BSON document size. Note, before + auto encryption this document is under the 2 MiB limit. After encryption it exceeds the 2 MiB limit, but does NOT + exceed the 16 MiB limit. + +3. Use MongoCollection.bulkWrite to insert the following into `coll`: + + - `{ "_id": "over_2mib_1", "unencrypted": }` + - `{ "_id": "over_2mib_2", "unencrypted": }` + + Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using + [command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md). + +4. Use MongoCollection.bulkWrite insert the following into `coll`: + + - The document [limits/limits-doc.json](../limits/limits-doc.json) concatenated with + `{ "_id": "encryption_exceeds_2mib_1", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }` + - The document [limits/limits-doc.json](../limits/limits-doc.json) concatenated with + `{ "_id": "encryption_exceeds_2mib_2", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }` + + Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using + [command logging and monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md). + +5. Insert `{ "_id": "under_16mib", "unencrypted": ` into `coll`. + + Expect this to succeed since this is still (just) under the `maxBsonObjectSize` limit. + +6. Insert the document [limits/limits-doc.json](../limits/limits-doc.json) concatenated with + `{ "_id": "encryption_exceeds_16mib", "unencrypted": < the string "a" repeated (16777216 - 2000) times > }` into + `coll`. + + Expect this to fail indicating the document exceeded the `maxBsonObjectSize` limit. If the write is sent to the + server (i.e. does not fail due to a driver-side check), expect a server error with code 2 or 10334. + +7. If using MongoDB 8.0+, use MongoClient.bulkWrite to insert the following into `coll2`: + + - `{ "_id": "over_2mib_3", "unencrypted": }` + - `{ "_id": "over_2mib_4", "unencrypted": }` + + Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using + [command logging and monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md). + +8. If using MongoDB 8.0+, use MongoClient.bulkWrite to insert the following into `coll2`: + + - The document [limits/limits-qe-doc.json](../limits/limits-qe-doc.json) concatenated with + `{ "_id": "encryption_exceeds_2mib_3", "foo": < the string "a" repeated (2097152 - 2000 - 1500) times > }` + - The document [limits/limits-qe-doc.json](../limits/limits-qe-doc.json) concatenated with + `{ "_id": "encryption_exceeds_2mib_4", "foo": < the string "a" repeated (2097152 - 2000 - 1500) times > }` + + Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using + [command logging and monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md). + +Optionally, if it is possible to mock the maxWriteBatchSize (i.e. the maximum number of documents in a batch) test that +setting maxWriteBatchSize=1 and inserting the two documents `{ "_id": "a" }, { "_id": "b" }` with `client_encrypted` +splits the operation into two inserts. + +### 5. Views Are Prohibited + +1. Create a MongoClient without encryption enabled (referred to as `client`). + +2. Using `client`, drop and create a view named `db.view` with an empty pipeline. E.g. using the command + `{ "create": "view", "viewOn": "coll" }`. + +3. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure with the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + +4. Using `client_encrypted`, attempt to insert a document into `db.view`. Expect an exception to be thrown containing + the message: "cannot auto encrypt a view". + +### 6. Corpus Test + +The corpus test exhaustively enumerates all ways to encrypt all BSON value types. Note, the test data includes BSON +binary subtype 4 (or standard UUID), which MUST be decoded and encoded as subtype 4. Run the test as follows. + +1. Create a MongoClient without encryption enabled (referred to as `client`). + +2. Using `client`, drop and create the collection `db.coll` configured with the included JSON schema + [corpus/corpus-schema.json](../corpus/corpus-schema.json). + +3. Using `client`, drop the collection `keyvault.datakeys`. Insert the documents + [corpus/corpus-key-local.json](../corpus/corpus-key-local.json), + [corpus/corpus-key-aws.json](../corpus/corpus-key-aws.json), + [corpus/corpus-key-azure.json](../corpus/corpus-key-azure.json), + [corpus/corpus-key-gcp.json](../corpus/corpus-key-gcp.json), and + [corpus/corpus-key-kmip.json](../corpus/corpus-key-kmip.json). + +4. Create the following: + + - A MongoClient configured with auto encryption (referred to as `client_encrypted`) + - A `ClientEncryption` object (referred to as `client_encryption`) + + Configure both objects with `aws`, `azure`, `gcp`, `local`, and `kmip` KMS providers as follows: + + ```javascript + { + "aws": { }, + "azure": { }, + "gcp": { }, + "local": { "key": }, + "kmip": { "endpoint": "localhost:5698" } + } + ``` + + Configure KMIP TLS connections to use the following options: + + - `tlsCAFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). + This MAY be configured system-wide. + - `tlsCertificateKeyFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem). + + The method of passing TLS options for KMIP TLS connections is driver dependent. + + Where LOCAL_MASTERKEY is the following base64: + + ```text + Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + ``` + + Configure both objects with `keyVaultNamespace` set to `keyvault.datakeys`. + +5. Load [corpus/corpus.json](../corpus/corpus.json) to a variable named `corpus`. The corpus contains subdocuments with + the following fields: + + - `kms` is `aws`, `azure`, `gcp`, `local`, or `kmip` + - `type` is a BSON type string + [names coming from here](https://www.mongodb.com/docs/manual/reference/operator/query/type/)) + - `algo` is either `rand` or `det` for random or deterministic encryption + - `method` is either `auto`, for automatic encryption or `explicit` for explicit encryption + - `identifier` is either `id` or `altname` for the key identifier + - `allowed` is a boolean indicating whether the encryption for the given parameters is permitted. + - `value` is the value to be tested. + + Create a new BSON document, named `corpus_copied`. Iterate over each field of `corpus`. + + - If the field name is `_id`, `altname_aws`, `altname_local`, `altname_azure`, `altname_gcp`, or `altname_kmip` copy + the field to `corpus_copied`. + + - If `method` is `auto`, copy the field to `corpus_copied`. + + - If `method` is `explicit`, use `client_encryption` to explicitly encrypt the value. + + - Encrypt with the algorithm described by `algo`. + - If `identifier` is `id` + - If `kms` is `local` set the key_id to the UUID with base64 value `LOCALAAAAAAAAAAAAAAAAA==`. + - If `kms` is `aws` set the key_id to the UUID with base64 value `AWSAAAAAAAAAAAAAAAAAAA==`. + - If `kms` is `azure` set the key_id to the UUID with base64 value `AZUREAAAAAAAAAAAAAAAAA==`. + - If `kms` is `gcp` set the key_id to the UUID with base64 value `GCPAAAAAAAAAAAAAAAAAAA==`. + - If `kms` is `kmip` set the key_id to the UUID with base64 value `KMIPAAAAAAAAAAAAAAAAAA==`. + - If `identifier` is `altname` + - If `kms` is `local` set the key_alt_name to "local". + - If `kms` is `aws` set the key_alt_name to "aws". + - If `kms` is `azure` set the key_alt_name to "azure". + - If `kms` is `gcp` set the key_alt_name to "gcp". + - If `kms` is `kmip` set the key_alt_name to "kmip". + + If `allowed` is true, copy the field and encrypted value to `corpus_copied`. If `allowed` is false. verify that an + exception is thrown. Copy the unencrypted value to to `corpus_copied`. + +6. Using `client_encrypted`, insert `corpus_copied` into `db.coll`. + +7. Using `client_encrypted`, find the inserted document from `db.coll` to a variable named `corpus_decrypted`. Since it + should have been automatically decrypted, assert the document exactly matches `corpus`. + +8. Load [corpus/corpus_encrypted.json](../corpus/corpus-encrypted.json) to a variable named `corpus_encrypted_expected`. + Using `client` find the inserted document from `db.coll` to a variable named `corpus_encrypted_actual`. + + Iterate over each field of `corpus_encrypted_expected` and check the following: + + - If the `algo` is `det`, that the value equals the value of the corresponding field in `corpus_encrypted_actual`. + - If the `algo` is `rand` and `allowed` is true, that the value does not equal the value of the corresponding field + in `corpus_encrypted_actual`. + - If `allowed` is true, decrypt the value with `client_encryption`. Decrypt the value of the corresponding field of + `corpus_encrypted` and validate that they are both equal. + - If `allowed` is false, validate the value exactly equals the value of the corresponding field of `corpus` (neither + was encrypted). + +9. Repeat steps 1-8 with a local JSON schema. I.e. amend step 4 to configure the schema on `client_encrypted` with the + `schema_map` option. + +### 7. Custom Endpoint Test + +#### Setup + +For each test cases, start by creating two `ClientEncryption` objects. Recreate the `ClientEncryption` objects for each +test case. + +Create a `ClientEncryption` object (referred to as `client_encryption`) + +Configure with `keyVaultNamespace` set to `keyvault.datakeys`, and a default MongoClient as the `keyVaultClient`. + +Configure with KMS providers as follows: + +```javascript +{ + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "login.microsoftonline.com:443" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "oauth2.googleapis.com:443" + }, + "kmip" { + "endpoint": "localhost:5698" + } +} +``` + +Create a `ClientEncryption` object (referred to as `client_encryption_invalid`) + +Configure with `keyVaultNamespace` set to `keyvault.datakeys`, and a default MongoClient as the `keyVaultClient`. + +Configure with KMS providers as follows: + +```javascript +{ + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "doesnotexist.invalid:443" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "doesnotexist.invalid:443" + }, + "kmip": { + "endpoint": "doesnotexist.invalid:5698" + } +} +``` + +Configure KMIP TLS connections to use the following options: + +- `tlsCAFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). + This MAY be configured system-wide. +- `tlsCertificateKeyFile` (or equivalent) set to + [drivers-evergreen-tools/.evergreen/x509gen/client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem). + +The method of passing TLS options for KMIP TLS connections is driver dependent. + +#### Test cases + +1. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + +2. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + endpoint: "kms.us-east-1.amazonaws.com" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + +3. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + endpoint: "kms.us-east-1.amazonaws.com:443" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + +4. Call `client_encryption.createDataKey()` with "kmip" as the provider and the following masterKey: + + ```javascript + { + "keyId": "1", + "endpoint": "localhost:12345" + } + ``` + + Expect this to fail with a socket connection error. + +5. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + endpoint: "kms.us-east-2.amazonaws.com" + } + ``` + + Expect this to fail with an exception. + +6. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + endpoint: "doesnotexist.invalid" + } + ``` + + Expect this to fail with a network exception indicating failure to resolve "doesnotexist.invalid". + +7. Call `client_encryption.createDataKey()` with "azure" as the provider and the following masterKey: + + ```javascript + { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + + Call `client_encryption_invalid.createDataKey()` with the same masterKey. Expect this to fail with a network + exception indicating failure to resolve "doesnotexist.invalid". + +8. Call `client_encryption.createDataKey()` with "gcp" as the provider and the following masterKey: + + ```javascript + { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle", + "endpoint": "cloudkms.googleapis.com:443" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + + Call `client_encryption_invalid.createDataKey()` with the same masterKey. Expect this to fail with a network + exception indicating failure to resolve "doesnotexist.invalid". + +9. Call `client_encryption.createDataKey()` with "gcp" as the provider and the following masterKey: + + ```javascript + { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle", + "endpoint": "doesnotexist.invalid:443" + } + ``` + + Expect this to fail with an exception with a message containing the string: "Invalid KMS response". + +10. Call `client_encryption.createDataKey()` with "kmip" as the provider and the following masterKey: + + ```javascript + { + "keyId": "1" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + + Call `client_encryption_invalid.createDataKey()` with the same masterKey. Expect this to fail with a network + exception indicating failure to resolve "doesnotexist.invalid". + +11. Call `client_encryption.createDataKey()` with "kmip" as the provider and the following masterKey: + + ```javascript + { + "keyId": "1", + "endpoint": "localhost:5698" + } + ``` + + Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to + validate it works. + +12. Call `client_encryption.createDataKey()` with "kmip" as the provider and the following masterKey: + + ```javascript + { + "keyId": "1", + "endpoint": "doesnotexist.invalid:5698" + } + ``` + + Expect this to fail with a network exception indicating failure to resolve "doesnotexist.invalid". + +### 8. Bypass Spawning mongocryptd + +> [!NOTE] +> CONSIDER: To reduce the chances of tests interfering with each other, drivers MAY use a different port for each test +> in this group, and include it in `--pidfilepath`. The interference may come from the fact that once spawned by a test, +> `mongocryptd` stays up and running for some time. + +#### Via loading shared library + +The following tests that loading [crypt_shared](../client-side-encryption.md#crypt_shared) bypasses spawning +mongocryptd. + +> [!NOTE] +> IMPORTANT: This test requires the [crypt_shared](../client-side-encryption.md#crypt_shared) library be loaded. If the +> [crypt_shared](../client-side-encryption.md#crypt_shared) library is not available, skip the test. + +1. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure the required options. Use the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure `client_encrypted` to use the schema [external/external-schema.json](../external/external-schema.json) for + `db.coll` by setting a schema map like: `{ "db.coll": }` + + Configure the following `extraOptions`: + + ```javascript + { + "mongocryptdURI": "mongodb://localhost:27021/?serverSelectionTimeoutMS=1000", + "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"], + "cryptSharedLibPath": "", + "cryptSharedLibRequired": true + } + ``` + + Drivers MAY pass a different port if they expect their testing infrastructure to be using port 27021. Pass a port + that should be free. + +2. Use `client_encrypted` to insert the document `{"unencrypted": "test"}` into `db.coll`. Expect this to succeed. + +3. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via + `--port`) with serverSelectionTimeoutMS=1000. Run a handshake command and ensure it fails with a server selection + timeout. + +> [!NOTE] +> IMPORTANT: If [crypt_shared](../client-side-encryption.md#crypt_shared) is visible to the operating system's library +> search mechanism, the expected server error generated by the `Via mongocryptdBypassSpawn`, `Via bypassAutoEncryption`, +> `Via bypassQueryAnalysis` tests will not appear because libmongocrypt will load the `crypt_shared` library instead of +> consulting mongocryptd. For the following tests, it is required that libmongocrypt *not* load `crypt_shared`. Refer to +> the client-side-encryption document for more information on "disabling" `crypt_shared`. Take into account that once +> loaded, for example, by another test, `crypt_shared` cannot be unloaded and may be used by `MongoClient`, thus making +> the tests misbehave in unexpected ways. + +#### Via mongocryptdBypassSpawn + +The following tests that setting `mongocryptdBypassSpawn=true` really does bypass spawning mongocryptd. + +1. Insert the document [external/external-key.json](../external/external-key.json) into `keyvault.datakeys` with + majority write concern. This step is not required to run this test, and drivers MAY skip it. But if the driver + misbehaves, then not having the encryption fully set up may complicate the process of figuring out what is wrong. + +2. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure the required options. Use the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure `client_encrypted` to use the schema [external/external-schema.json](../external/external-schema.json) for + `db.coll` by setting a schema map like: `{ "db.coll": }` + + Configure the following `extraOptions`: + + ```javascript + { + "mongocryptdBypassSpawn": true + "mongocryptdURI": "mongodb://localhost:27021/?serverSelectionTimeoutMS=1000", + "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"] + } + ``` + + Drivers MAY pass a different port if they expect their testing infrastructure to be using port 27021. Pass a port + that should be free. + +3. Use `client_encrypted` to insert the document `{"encrypted": "test"}` into `db.coll`. Expect a server selection error + propagated from the internal MongoClient failing to connect to mongocryptd on port 27021. + +#### Via bypassAutoEncryption + +The following tests that setting `bypassAutoEncryption=true` really does bypass spawning mongocryptd. + +1. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure the required options. Use the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure with `bypassAutoEncryption=true`. + + Configure the following `extraOptions`: + + ```javascript + { + "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"] + } + ``` + + Drivers MAY pass a different value to `--port` if they expect their testing infrastructure to be using port 27021. + Pass a port that should be free. + +2. Use `client_encrypted` to insert the document `{"unencrypted": "test"}` into `db.coll`. Expect this to succeed. + +3. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via + `--port`) with serverSelectionTimeoutMS=1000. Run a handshake command and ensure it fails with a server selection + timeout. + +#### Via bypassQueryAnalysis + +Repeat the steps from the "Via bypassAutoEncryption" test, replacing "bypassAutoEncryption=true" with +"bypassQueryAnalysis=true". + +### 9. Deadlock Tests + +The following tests only apply to drivers that have implemented a connection pool (see the +[Connection Monitoring and Pooling](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) +specification). + +There are multiple parameterized test cases. Before each test case, perform the setup. + +#### Setup + +Create a `MongoClient` for setup operations named `client_test`. + +Create a `MongoClient` for key vault operations with `maxPoolSize=1` named `client_keyvault`. Capture command started +events. + +Using `client_test`, drop the collections `keyvault.datakeys` and `db.coll`. + +Insert the document [external/external-key.json](../external/external-key.json) into `keyvault.datakeys` with majority +write concern. + +Create a collection `db.coll` configured with a JSON schema +[external/external-schema.json](../external/external-schema.json) as the validator, like so: + +```typescript +{"create": "coll", "validator": {"$jsonSchema": }} +``` + +Create a `ClientEncryption` object, named `client_encryption` configured with: - `keyVaultClient`=`client_test` - +`keyVaultNamespace`="keyvault.datakeys" - `kmsProviders`=`{ "local": { "key": } }` + +Use `client_encryption` to encrypt the value "string0" with `algorithm`="AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" +and `keyAltName`="local". Store the result in a variable named `ciphertext`. + +Proceed to run the test case. + +Each test case configures a `MongoClient` with automatic encryption (named `client_encrypted`). + +Each test must assert the number of unique `MongoClient` objects created. This can be accomplished by capturing +`TopologyOpeningEvent`, or by checking command started events for a client identifier (not possible in all drivers). + +#### Running a test case + +- Create a `MongoClient` named `client_encrypted` configured as follows: + + - Set `AutoEncryptionOpts`: + + - `keyVaultNamespace="keyvault.datakeys"` + - `kmsProviders`=`{ "local": { "key": } }` + - Append `TestCase.AutoEncryptionOpts` (defined below) + + - Capture command started events. + + - Set `maxPoolSize=TestCase.MaxPoolSize` + +- If the testcase sets `AutoEncryptionOpts.bypassAutoEncryption=true`: + + - Use `client_test` to insert `{ "_id": 0, "encrypted": }` into `db.coll`. + +- Otherwise: + + - Use `client_encrypted` to insert `{ "_id": 0, "encrypted": "string0" }`. + +- Use `client_encrypted` to run a `findOne` operation on `db.coll`, with the filter `{ "_id": 0 }`. + +- Expect the result to be `{ "_id": 0, "encrypted": "string0" }`. + +- Check captured events against `TestCase.Expectations`. + +- Check the number of unique `MongoClient` objects created is equal to `TestCase.ExpectedNumberOfClients`. + +#### Case 1 + +- MaxPoolSize: 1 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=false + - keyVaultClient=unset + +- Expectations: + + - Expect `client_encrypted` to have captured four `CommandStartedEvent`: + - a listCollections to "db". + - a find on "keyvault". + - an insert on "db". + - a find on "db" + +- ExpectedNumberOfClients: 2 + +#### Case 2 + +- MaxPoolSize: 1 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=false + - keyVaultClient=client_keyvault + +- Expectations: + + - Expect `client_encrypted` to have captured three `CommandStartedEvent`: + + - a listCollections to "db". + - an insert on "db". + - a find on "db" + + - Expect `client_keyvault` to have captured one `CommandStartedEvent`: + + - a find on "keyvault". + +- ExpectedNumberOfClients: 2 + +#### Case 3 + +- MaxPoolSize: 1 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=true + - keyVaultClient=unset + +- Expectations: + + - Expect `client_encrypted` to have captured three `CommandStartedEvent`: + - a find on "db" + - a find on "keyvault". + +- ExpectedNumberOfClients: 2 + +#### Case 4 + +- MaxPoolSize: 1 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=true + - keyVaultClient=client_keyvault + +- Expectations: + + - Expect `client_encrypted` to have captured two `CommandStartedEvent`: + + - a find on "db" + + - Expect `client_keyvault` to have captured one `CommandStartedEvent`: + + - a find on "keyvault". + +- ExpectedNumberOfClients: 1 + +#### Case 5 + +Drivers that do not support an unlimited maximum pool size MUST skip this test. + +- MaxPoolSize: 0 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=false + - keyVaultClient=unset + +- Expectations: + + - Expect `client_encrypted` to have captured five `CommandStartedEvent`: + - a listCollections to "db". + - a listCollections to "keyvault". + - a find on "keyvault". + - an insert on "db". + - a find on "db" + +- ExpectedNumberOfClients: 1 + +#### Case 6 + +Drivers that do not support an unlimited maximum pool size MUST skip this test. + +- MaxPoolSize: 0 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=false + - keyVaultClient=client_keyvault + +- Expectations: + + - Expect `client_encrypted` to have captured three `CommandStartedEvent`: + + - a listCollections to "db". + - an insert on "db". + - a find on "db" + + - Expect `client_keyvault` to have captured one `CommandStartedEvent`: + + - a find on "keyvault". + +- ExpectedNumberOfClients: 1 + +#### Case 7 + +Drivers that do not support an unlimited maximum pool size MUST skip this test. + +- MaxPoolSize: 0 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=true + - keyVaultClient=unset + +- Expectations: + + - Expect `client_encrypted` to have captured three `CommandStartedEvent`: + - a find on "db" + - a find on "keyvault". + +- ExpectedNumberOfClients: 1 + +#### Case 8 + +Drivers that do not support an unlimited maximum pool size MUST skip this test. + +- MaxPoolSize: 0 + +- AutoEncryptionOpts: + + - bypassAutoEncryption=true + - keyVaultClient=client_keyvault + +- Expectations: + + - Expect `client_encrypted` to have captured two `CommandStartedEvent`: + + - a find on "db" + + - Expect `client_keyvault` to have captured one `CommandStartedEvent`: + + - a find on "keyvault". + +- ExpectedNumberOfClients: 1 + +### 10. KMS TLS Tests + +The following tests that connections to KMS servers with TLS verify peer certificates. + +The two tests below make use of mock KMS servers which can be run on Evergreen using +[the mock KMS server script](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_http_server.py). +Drivers can set up their local Python environment for the mock KMS server by running +[the virtualenv activation script](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/activate-kmstlsvenv.sh). + +To start two mock KMS servers, one on port 9000 with +[ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA file and +[expired.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/expired.pem) as a +cert file, and one on port 9001 with +[ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA file and +[wrong-host.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/wrong-host.pem) +as a cert file, run the following commands from the `.evergreen/csfle` directory: + +```shell +. ./activate_venv.sh +python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 9000 & +python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 9001 & +``` + +#### Setup + +For both tests, do the following: + +1. Start a `mongod` process with **server version 4.2.0 or later**. +2. Create a `MongoClient` for key vault operations. +3. Create a `ClientEncryption` object (referred to as `client_encryption`) with `keyVaultNamespace` set to + `keyvault.datakeys`. + +#### Invalid KMS Certificate + +1. Start a mock KMS server on port 9000 with + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA + file and + [expired.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/expired.pem) + as a cert file. + +2. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + "region": "us-east-1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "endpoint": "127.0.0.1:9000", + } + ``` + + Expect this to fail with an exception with a message referencing an expired certificate. This message will be + language dependent. In Python, this message is "certificate verify failed: certificate has expired". In Go, this + message is "certificate has expired or is not yet valid". If the language of implementation has a single, generic + error message for all certificate validation errors, drivers may inspect other fields of the error to verify its + meaning. + +#### Invalid Hostname in KMS Certificate + +1. Start a mock KMS server on port 9001 with + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA + file and + [wrong-host.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/wrong-host.pem) + as a cert file. + +2. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey: + + ```javascript + { + "region": "us-east-1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "endpoint": "127.0.0.1:9001", + } + ``` + + Expect this to fail with an exception with a message referencing an incorrect or unexpected host. This message will + be language dependent. In Python, this message is "certificate verify failed: IP address mismatch, certificate is + not valid for '127.0.0.1'". In Go, this message is "cannot validate certificate for 127.0.0.1 because it doesn't + contain any IP SANs". If the language of implementation has a single, generic error message for all certificate + validation errors, drivers may inspect other fields of the error to verify its meaning. + +### 11. KMS TLS Options Tests + +#### Setup + +Start a `mongod` process with **server version 4.2.0 or later**. + +Four mock KMS server processes must be running: + +1. The mock + [KMS HTTP server](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_http_server.py). + + Run on port 9000 with + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA + file and + [expired.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/expired.pem) + as a cert file. + + Example: + + ```shell + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 9000 + ``` + +2. The mock + [KMS HTTP server](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_http_server.py). + + Run on port 9001 with + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA + file and + [wrong-host.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/wrong-host.pem) + as a cert file. + + Example: + + ```shell + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 9001 + ``` + +3. The mock + [KMS HTTP server](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_http_server.py). + + Run on port 9002 with + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem) as a CA + file and + [server.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/server.pem) as + a cert file. + + Run with the `--require_client_cert` option. + + Example: + + ```shell + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 9002 --require_client_cert + ``` + +4. The mock + [KMS KMIP server](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_kmip_server.py). + +Create the following `ClientEncryption` objects. + +Configure each with `keyVaultNamespace` set to `keyvault.datakeys`, and a default MongoClient as the `keyVaultClient`. + +1. Create a `ClientEncryption` object named `client_encryption_no_client_cert` with the following KMS providers: + + ```javascript + { + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9002" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9002" + }, + "kmip" { + "endpoint": "127.0.0.1:5698" + } + } + ``` + + Add TLS options for the `aws`, `azure`, `gcp`, and `kmip` providers to use the following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + +2. Create a `ClientEncryption` object named `client_encryption_with_tls` with the following KMS providers: + + ```javascript + { + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9002" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9002" + }, + "kmip" { + "endpoint": "127.0.0.1:5698" + } + } + ``` + + Add TLS options for the `aws`, `azure`, `gcp`, and `kmip` providers to use the following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + - `tlsCertificateKeyFile` (or equivalent) set to + [client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem) + +3. Create a `ClientEncryption` object named `client_encryption_expired` with the following KMS providers: + + ```javascript + { + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9000" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9000" + }, + "kmip" { + "endpoint": "127.0.0.1:9000" + } + } + ``` + + Add TLS options for the `aws`, `azure`, `gcp`, and `kmip` providers to use the following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + +4. Create a `ClientEncryption` object named `client_encryption_invalid_hostname` with the following KMS providers: + + ```javascript + { + "aws": { + "accessKeyId": , + "secretAccessKey": + }, + "azure": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9001" + }, + "gcp": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9001" + }, + "kmip" { + "endpoint": "127.0.0.1:9001" + } + } + ``` + + Add TLS options for the `aws`, `azure`, `gcp`, and `kmip` providers to use the following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + +5. Create a `ClientEncryption` object named `client_encryption_with_names` with the following KMS providers: + + ```javascript + { + "aws:no_client_cert": { + "accessKeyId": , + "secretAccessKey": + }, + "azure:no_client_cert": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9002" + }, + "gcp:no_client_cert": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9002" + }, + "kmip:no_client_cert": { + "endpoint": "127.0.0.1:5698" + }, + "aws:with_tls": { + "accessKeyId": , + "secretAccessKey": + }, + "azure:with_tls": { + "tenantId": , + "clientId": , + "clientSecret": , + "identityPlatformEndpoint": "127.0.0.1:9002" + }, + "gcp:with_tls": { + "email": , + "privateKey": , + "endpoint": "127.0.0.1:9002" + }, + "kmip:with_tls": { + "endpoint": "127.0.0.1:5698" + } + } + ``` + + Support for named KMS providers requires libmongocrypt 1.9.0. + + Add TLS options for the `aws:no_client_cert`, `azure:no_client_cert`, `gcp:no_client_cert`, and `kmip:no_client_cert` + providers to use the following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + + Add TLS options for the `aws:with_tls`, `azure:with_tls`, `gcp:with_tls`, and `kmip:with_tls` providers to use the + following options: + + - `tlsCAFile` (or equivalent) set to + [ca.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem). This MAY + be configured system-wide. + - `tlsCertificateKeyFile` (or equivalent) set to + [client.pem](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/client.pem) + +#### Case 1: AWS + +Call `client_encryption_no_client_cert.createDataKey()` with "aws" as the provider and the following masterKey: + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + endpoint: "127.0.0.1:9002" +} +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_tls.createDataKey()` with "aws" as the provider and the following masterKey: + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + endpoint: "127.0.0.1:9002" +} +``` + +Expect an error from libmongocrypt with a message containing the string: "parse error". This implies TLS handshake +succeeded. + +Call `client_encryption_expired.createDataKey()` with "aws" as the provider and the following masterKey: + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + endpoint: "127.0.0.1:9000" +} +``` + +Expect an error indicating TLS handshake failed due to an expired certificate. + +Call `client_encryption_invalid_hostname.createDataKey()` with "aws" as the provider and the following masterKey: + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + endpoint: "127.0.0.1:9001" +} +``` + +Expect an error indicating TLS handshake failed due to an invalid hostname. + +#### Case 2: Azure + +Call `client_encryption_no_client_cert.createDataKey()` with "azure" as the provider and the following masterKey: + +```javascript +{ 'keyVaultEndpoint': 'doesnotexist.invalid', 'keyName': 'foo' } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_tls.createDataKey()` with "azure" as the provider and the same masterKey. + +Expect an error from libmongocrypt with a message containing the string: "HTTP status=404". This implies TLS handshake +succeeded. + +Call `client_encryption_expired.createDataKey()` with "azure" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an expired certificate. + +Call `client_encryption_invalid_hostname.createDataKey()` with "azure" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an invalid hostname. + +#### Case 3: GCP + +Call `client_encryption_no_client_cert.createDataKey()` with "gcp" as the provider and the following masterKey: + +```javascript +{ 'projectId': 'foo', 'location': 'bar', 'keyRing': 'baz', 'keyName': 'foo' } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_tls.createDataKey()` with "gcp" as the provider and the same masterKey. + +Expect an error from libmongocrypt with a message containing the string: "HTTP status=404". This implies TLS handshake +succeeded. + +Call `client_encryption_expired.createDataKey()` with "gcp" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an expired certificate. + +Call `client_encryption_invalid_hostname.createDataKey()` with "gcp" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an invalid hostname. + +#### Case 4: KMIP + +Call `client_encryption_no_client_cert.createDataKey()` with "kmip" as the provider and the following masterKey: + +```javascript +{ } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_tls.createDataKey()` with "kmip" as the provider and the same masterKey. + +Expect success. + +Call `client_encryption_expired.createDataKey()` with "kmip" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an expired certificate. + +Call `client_encryption_invalid_hostname.createDataKey()` with "kmip" as the provider and the same masterKey. + +Expect an error indicating TLS handshake failed due to an invalid hostname. + +#### Case 5: `tlsDisableOCSPEndpointCheck` is permitted + +This test does not apply if the driver does not support the the option `tlsDisableOCSPEndpointCheck`. + +Create a `ClientEncryption` object with the following KMS providers: + +> ```javascript +> { +> "aws": { +> "accessKeyId": "foo", +> "secretAccessKey": "bar" +> } +> } +> ``` +> +> Add TLS options for the `aws` with the following options: +> +> - `tlsDisableOCSPEndpointCheck` (or equivalent) set to `true`. + +Expect no error on construction. + +#### Case 6: named KMS providers apply TLS options + +##### Named AWS + +Call `client_encryption_with_names.createDataKey()` with "aws:no_client_cert" as the provider and the following +masterKey. + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + endpoint: "127.0.0.1:9002" +} +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_names.createDataKey()` with "aws:with_tls" as the provider and the same masterKey. + +Expect an error from libmongocrypt with a message containing the string: "parse error". This implies TLS handshake +succeeded. + +##### Named Azure + +Call `client_encryption_with_names.createDataKey()` with "azure:no_client_cert" as the provider and the following +masterKey: + +```javascript +{ 'keyVaultEndpoint': 'doesnotexist.invalid', 'keyName': 'foo' } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_names.createDataKey()` with "azure:with_tls" as the provider and the same masterKey. + +Expect an error from libmongocrypt with a message containing the string: "HTTP status=404". This implies TLS handshake +succeeded. + +##### Named GCP + +Call `client_encryption_with_names.createDataKey()` with "gcp:no_client_cert" as the provider and the following +masterKey: + +```javascript +{ 'projectId': 'foo', 'location': 'bar', 'keyRing': 'baz', 'keyName': 'foo' } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_names.createDataKey()` with "gcp:with_tls" as the provider and the same masterKey. + +Expect an error from libmongocrypt with a message containing the string: "HTTP status=404". This implies TLS handshake +succeeded. + +##### Named KMIP + +Call `client_encryption_with_names.createDataKey()` with "kmip:no_client_cert" as the provider and the following +masterKey: + +```javascript +{ } +``` + +Expect an error indicating TLS handshake failed. + +Call `client_encryption_with_names.createDataKey()` with "kmip:with_tls" as the provider and the same masterKey. + +Expect success. + +### 12. Explicit Encryption + +The Explicit Encryption tests require MongoDB server 7.0+. The tests must not run against a standalone. + +> [!NOTE] +> MongoDB Server 7.0 introduced a backwards breaking change to the Queryable Encryption (QE) protocol: QEv2. +> libmongocrypt 1.8.0 is configured to use the QEv2 protocol. + +Before running each of the following test cases, perform the following Test Setup. + +#### Test Setup + +Load the file +[encryptedFields.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields.json) +as `encryptedFields`. + +Load the file +[key1-document.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/keys/key1-document.json) +as `key1Document`. + +Read the `"_id"` field of `key1Document` as `key1ID`. + +Drop and create the collection `db.explicit_encryption` using `encryptedFields` as an option. See +[FLE 2 CreateCollection() and Collection.Drop()](../client-side-encryption.md#create-collection-helper). + +Load the file +[encryptedFields-c10.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-c10.json) +as `encryptedFields_c10`. + +Drop and create the collection `db.explicit_encryption_c10` using `encryptedFields_c10` as an option. See +[FLE 2 CreateCollection() and Collection.Drop()](../client-side-encryption.md#create-collection-helper). + +Drop and create the collection `keyvault.datakeys`. + +Insert `key1Document` in `keyvault.datakeys` with majority write concern. + +Create a MongoClient named `keyVaultClient`. + +Create a ClientEncryption object named `clientEncryption` with these options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Create a MongoClient named `encryptedClient` with these `AutoEncryptionOpts`: + +```typescript +class AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, + bypassQueryAnalysis: true, +} +``` + +#### Case 1: can insert encrypted indexed and find + +Use `clientEncryption` to encrypt the value "encrypted indexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Indexed", + contentionFactor: 0, +} +``` + +Store the result in `insertPayload`. + +Use `encryptedClient` to insert the document `{ "encryptedIndexed": }` into `db.explicit_encryption`. + +Use `clientEncryption` to encrypt the value "encrypted indexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Indexed", + queryType: "equality", + contentionFactor: 0, +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter +`{ "encryptedIndexed": }`. + +Assert one document is returned containing the field `{ "encryptedIndexed": "encrypted indexed value" }`. + +#### Case 2: can insert encrypted indexed and find with non-zero contention + +Use `clientEncryption` to encrypt the value "encrypted indexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Indexed", + contentionFactor: 10, +} +``` + +Store the result in `insertPayload`. + +Use `encryptedClient` to insert the document `{ "encryptedIndexed": }` into +`db.explicit_encryption_c10`. + +Repeat the above steps 10 times to insert 10 total documents. The `insertPayload` must be regenerated each iteration. + +Use `clientEncryption` to encrypt the value "encrypted indexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Indexed", + queryType: "equality", + contentionFactor: 10, +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption_c10` collection with the filter +`{ "encryptedIndexed": }`. + +Assert 10 documents are returned. Assert each returned document contains the field +`{ "encryptedIndexed": "encrypted indexed value" }`. + +#### Case 3: can insert encrypted unindexed + +Use `clientEncryption` to encrypt the value "encrypted unindexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Unindexed", +} +``` + +Store the result in `insertPayload`. + +Use `encryptedClient` to insert the document `{ "_id": 1, "encryptedUnindexed": }` into +`db.explicit_encryption`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter +`{ "_id": 1 }`. + +Assert one document is returned containing the field `{ "encryptedUnindexed": "encrypted unindexed value" }`. + +#### Case 4: can roundtrip encrypted indexed + +Use `clientEncryption` to encrypt the value "encrypted indexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Indexed", + contentionFactor: 0, +} +``` + +Store the result in `payload`. + +Use `clientEncryption` to decrypt `payload`. Assert the returned value equals "encrypted indexed value". + +#### Case 5: can roundtrip encrypted unindexed + +Use `clientEncryption` to encrypt the value "encrypted unindexed value" with these `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Unindexed", +} +``` + +Store the result in `payload`. + +Use `clientEncryption` to decrypt `payload`. Assert the returned value equals "encrypted unindexed value". + +### 13. Unique Index on keyAltNames + +The following setup must occur before running each of the following test cases. + +#### Setup + +1. Create a `MongoClient` object (referred to as `client`). + +2. Using `client`, drop the collection `keyvault.datakeys`. + +3. Using `client`, create a unique index on `keyAltNames` with a partial index filter for only documents where + `keyAltNames` exists using writeConcern "majority". + + The command should be equivalent to: + + ```typescript + db.runCommand( + { + createIndexes: "datakeys", + indexes: [ + { + name: "keyAltNames_1", + key: { "keyAltNames": 1 }, + unique: true, + partialFilterExpression: { keyAltNames: { $exists: true } } + } + ], + writeConcern: { w: "majority" } + } + ) + ``` + +4. Create a `ClientEncryption` object (referred to as `client_encryption`) with `client` set as the `keyVaultClient`. + +5. Using `client_encryption`, create a data key with a `local` KMS provider and the keyAltName "def". + +#### Case 1: createDataKey() + +1. Use `client_encryption` to create a new local data key with a keyAltName "abc" and assert the operation does not + fail. +2. Repeat Step 1 and assert the operation fails due to a duplicate key server error (error code 11000). +3. Use `client_encryption` to create a new local data key with a keyAltName "def" and assert the operation fails due to + a duplicate key server error (error code 11000). + +#### Case 2: addKeyAltName() + +1. Use `client_encryption` to create a new local data key and assert the operation does not fail. +2. Use `client_encryption` to add a keyAltName "abc" to the key created in Step 1 and assert the operation does not + fail. +3. Repeat Step 2, assert the operation does not fail, and assert the returned key document contains the keyAltName "abc" + added in Step 2. +4. Use `client_encryption` to add a keyAltName "def" to the key created in Step 1 and assert the operation fails due to + a duplicate key server error (error code 11000). +5. Use `client_encryption` to add a keyAltName "def" to the existing key, assert the operation does not fail, and assert + the returned key document contains the keyAltName "def" added during Setup. + +### 14. Decryption Events + +Before running each of the following test cases, perform the following Test Setup. + +#### Test Setup + +Create a MongoClient named `setupClient`. + +Drop and create the collection `db.decryption_events`. + +Create a ClientEncryption object named `clientEncryption` with these options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Create a data key with the "local" KMS provider. Storing the result in a variable named `keyID`. + +Use `clientEncryption` to encrypt the string "hello" with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId: , + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", +} +``` + +Store the result in a variable named `ciphertext`. + +Copy `ciphertext` into a variable named `malformedCiphertext`. Change the last byte to a different value. This will +produce an invalid HMAC tag. + +Create a MongoClient named `encryptedClient` with these `AutoEncryptionOpts`: + +```typescript +class AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Configure `encryptedClient` with "retryReads=false". Register a listener for CommandSucceeded events on +`encryptedClient`. The listener must store the most recent `CommandSucceededEvent` reply for the "aggregate" command. +The listener must store the most recent `CommandFailedEvent` error for the "aggregate" command. + +#### Case 1: Command Error + +Use `setupClient` to configure the following failpoint: + +```typescript +{ + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 123, + "failCommands": [ + "aggregate" + ] + } +} +``` + +Use `encryptedClient` to run an aggregate on `db.decryption_events`. + +Expect an exception to be thrown from the command error. Expect a `CommandFailedEvent`. + +#### Case 2: Network Error + +Use `setupClient` to configure the following failpoint: + +```typescript +{ + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "errorCode": 123, + "closeConnection": true, + "failCommands": [ + "aggregate" + ] + } +} +``` + +Use `encryptedClient` to run an aggregate on `db.decryption_events`. + +Expect an exception to be thrown from the network error. Expect a `CommandFailedEvent`. + +#### Case 3: Decrypt Error + +Use `encryptedClient` to insert the document `{ "encrypted": }` into `db.decryption_events`. + +Use `encryptedClient` to run an aggregate on `db.decryption_events` with an empty pipeline. + +Expect an exception to be thrown from the decryption error. Expect a `CommandSucceededEvent`. Expect the +`CommandSucceededEvent.reply` to contain BSON binary for the field `cursor.firstBatch.encrypted`. + +#### Case 4: Decrypt Success + +Use `encryptedClient` to insert the document `{ "encrypted": }` into `db.decryption_events`. + +Use `encryptedClient` to run an aggregate on `db.decryption_events` with an empty pipeline. + +Expect no exception. Expect a `CommandSucceededEvent`. Expect the `CommandSucceededEvent.reply` to contain BSON binary +for the field `cursor.firstBatch.encrypted`. + +### 15. On-demand AWS Credentials + +These tests require valid AWS credentials. Refer: +[Automatic AWS Credentials](../client-side-encryption.md#automatic-credentials). + +For these cases, create a [ClientEncryption](../client-side-encryption.md#clientencryption) object $C$ with the +following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": {} }, +} +``` + +#### Case 1: Failure + +Do not run this test case in an environment where AWS credentials are available (e.g. via environment variables or a +metadata URL). (Refer: [Obtaining credentials for AWS](../../auth/auth.md#obtaining-credentials)) + +Attempt to create a datakey with $C$ using the `"aws"` KMS provider. Expect this to fail due to a lack of KMS provider +credentials. + +#### Case 2: Success + +For this test case, the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` must be defined and set to +a valid set of AWS credentials. + +Use the client encryption to create a datakey using the `"aws"` KMS provider. This should successfully load and use the +AWS credentials that were defined in the environment. + +### 16. Rewrap + +#### Case 1: Rewrap with separate ClientEncryption + +When the following test case requests setting `masterKey`, use the following values based on the KMS provider: + +For "aws": + +```javascript +{ + "region": "us-east-1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" +} +``` + +For "azure": + +```javascript +{ + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" +} +``` + +For "gcp": + +```javascript +{ + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" +} +``` + +For "kmip": + +```javascript +{} +``` + +For "local", do not set a masterKey document. + +Run the following test case for each pair of KMS providers (referred to as `srcProvider` and `dstProvider`). Include +pairs where `srcProvider` equals `dstProvider`. + +1. Drop the collection `keyvault.datakeys`. + +2. Create a `ClientEncryption` object named `clientEncryption1` with these options: + + ```typescript + class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: , + } + ``` + +3. Call `clientEncryption1.createDataKey` with `srcProvider` and these options: + + ```typescript + class DataKeyOpts { + masterKey: , + } + ``` + + Store the return value in `keyID`. + +4. Call `clientEncryption1.encrypt` with the value "test" and these options: + + ```typescript + class EncryptOpts { + keyId : keyID, + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + } + ``` + + Store the return value in `ciphertext`. + +5. Create a `ClientEncryption` object named `clientEncryption2` with these options: + + ```typescript + class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: , + } + ``` + +6. Call `clientEncryption2.rewrapManyDataKey` with an empty `filter` and these options: + + ```typescript + class RewrapManyDataKeyOpts { + provider: dstProvider, + masterKey: , + } + ``` + + Assert that the returned `RewrapManyDataKeyResult.bulkWriteResult.modifiedCount` is 1. + +7. Call `clientEncryption1.decrypt` with the `ciphertext`. Assert the return value is "test". + +8. Call `clientEncryption2.decrypt` with the `ciphertext`. Assert the return value is "test". + +#### Case 2: RewrapManyDataKeyOpts.provider is not optional + +Drivers MAY chose not to implement this prose test if their implementation of `RewrapManyDataKeyOpts` makes it +impossible by design to omit `RewrapManyDataKeyOpts.provider` when `RewrapManyDataKeyOpts.masterKey` is set. + +1. Create a `ClientEncryption` object named `clientEncryption` with these options: + + ```typescript + class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: , + } + ``` + +2. Call `clientEncryption.rewrapManyDataKey` with an empty `filter` and these options: + + ```typescript + class RewrapManyDataKeyOpts { + masterKey: {} + } + ``` + + Assert that `clientEncryption.rewrapManyDataKey` raises a client error indicating that the required + `RewrapManyDataKeyOpts.provider` field is missing. + +### 17. On-demand GCP Credentials + +Refer: [Automatic GCP Credentials](../client-side-encryption.md#obtaining-gcp-credentials). + +For these cases, create a [ClientEncryption](../client-side-encryption.md#clientencryption) object $C$ with the +following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "gcp": {} }, +} +``` + +#### Case 1: Failure + +Do not run this test case in an environment with a GCP service account is attached (e.g. any +[GCE equivalent runtime](https://google.aip.dev/auth/4115)). This may be run in an AWS EC2 instance. + +Attempt to create a datakey with $C$ using the `"gcp"` KMS provider and following `DataKeyOpts`: + +```typescript +class DataKeyOpts { + masterKey: { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle", + } +} +``` + +Expect the attempt to obtain `"gcp"` credentials from the environment to fail. + +#### Case 2: Success + +This test case must run in a Google Compute Engine (GCE) Virtual Machine with a service account attached. See +[drivers-evergreen-tools/.evergreen/csfle/gcpkms](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/gcpkms) +for scripts to create a GCE instance for testing. The Evergreen task SHOULD set a `batchtime` of 14 days to reduce how +often this test case runs. + +Attempt to create a datakey with $C$ using the `"gcp"` KMS provider and following `DataKeyOpts`: + +```typescript +class DataKeyOpts { + masterKey: { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle", + } +} +``` + +This should successfully load and use the GCP credentials of the service account attached to the virtual machine. + +Expect the key to be successfully created. + +### 18. Azure IMDS Credentials + +Refer: [Automatic Azure Credentials](../client-side-encryption.md#obtaining-an-access-token-for-azure-key-vault) + +The test cases for IMDS communication are specially designed to not require an Azure environment, while still exercising +the core of the functionality. The design of these test cases encourages an implementation to separate the concerns of +IMDS communication from the logic of KMS key manipulation. The purpose of these test cases is to ensure drivers will +behave appropriately regardless of the behavior of the IMDS server. + +For these IMDS credentials tests, a simple stand-in IMDS-imitating HTTP server is available in drivers-evergreen-tools, +at `.evergreen/csfle/fake_azure.py`. `fake_azure.py` is a very simple `bottle.py` application. For the easiest use, it +is recommended to execute it through `bottle.py` (which is a sibling file in the same directory): + +```shell +python .evergreen/csfle/bottle.py fake_azure:imds +``` + +This will run the `imds` Bottle application defined in the `fake_azure` Python module. `bottle.py` accepts additional +command line arguments to control the bind host and TCP port (use `--help` for more information). + +For each test case, follow the process for obtaining the token as outlined in the +[automatic Azure credentials section](../client-side-encryption.md#obtaining-an-access-token-for-azure-key-vault) with +the following changes: + +1. Instead of the standard IMDS TCP endpoint of `169.254.169.254:80`, communicate with the running `fake_azure` HTTP + server. +2. For each test case, the behavior of the server may be controlled by attaching an additional HTTP header to the sent + request: `X-MongoDB-HTTP-TestParams`. + +#### Case 1: Success + +Do not set an `X-MongoDB-HTTP-TestParams` header. + +Upon receiving a response from `fake_azure`, the driver must decode the following information: + +1. HTTP status will be `200 Okay`. +2. The HTTP body will be a valid JSON string. +3. The access token will be the string `"magic-cookie"`. +4. The expiry duration of the token will be seventy seconds. +5. The token will have a resource of `"https://vault.azure.net"` + +#### Case 2: Empty JSON + +This case addresses a server returning valid JSON with invalid content. + +Set `X-MongoDB-HTTP-TestParams` to `case=empty-json`. + +Upon receiving a response: + +1. HTTP status will be `200 Okay` +2. The HTTP body will be a valid JSON string. +3. There will be no access token, expiry duration, or resource. + +The test case should ensure that this error condition is handled gracefully. + +#### Case 3: Bad JSON + +This case addresses a server returning malformed JSON. + +Set `X-MongoDB-HTTP-TestParams` to `case=bad-json`. + +Upon receiving a response: + +1. HTTP status will be `200 Okay` +2. The response body will contain a malformed JSON string. + +The test case should ensure that this error condition is handled gracefully. + +#### Case 4: HTTP 404 + +This case addresses a server returning a "Not Found" response. This is documented to occur spuriously within an Azure +environment. + +Set `X-MongoDB-HTTP-TestParams` to `case=404`. + +Upon receiving a response: + +1. HTTP status will be `404 Not Found`. +2. The response body is unspecified. + +The test case should ensure that this error condition is handled gracefully. + +#### Case 5: HTTP 500 + +This case addresses an IMDS server reporting an internal error. This is documented to occur spuriously within an Azure +environment. + +Set `X-MongoDB-HTTP-TestParams` to `case=500`. + +Upon receiving a response: + +1. HTTP status code will be `500`. +2. The response body is unspecified. + +The test case should ensure that this error condition is handled gracefully. + +#### Case 6: Slow Response + +This case addresses an IMDS server responding very slowly. Drivers should not halt the application waiting on a peer to +communicate. + +Set `X-MongoDB-HTTP-TestParams` to `case=slow`. + +The HTTP response from the `fake_azure` server will take at least 1000 seconds to complete. The request should fail with +a timeout. + +### 19. Azure IMDS Credentials Integration Test + +Refer: [Automatic Azure Credentials](../client-side-encryption.md#obtaining-an-access-token-for-azure-key-vault) + +For these cases, create a [ClientEncryption](../client-side-encryption.md#clientencryption) object $C$ with the +following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "azure": {} }, +} +``` + +#### Case 1: Failure + +Do not run this test case in an Azure environment with an attached identity. This may be run in an AWS EC2 instance. + +Attempt to create a datakey with $C$ using the `"azure"` KMS provider and following `DataKeyOpts`: + +```typescript +class DataKeyOpts { + masterKey: { + "keyVaultEndpoint": "https://keyvault-drivers-2411.vault.azure.net/keys/", + "keyName": "KEY-NAME", + } +} +``` + +Expect the attempt to obtain `"azure"` credentials from the environment to fail. + +#### Case 2: Success + +This test case must run in an Azure environment with an attached identity. See +[drivers-evergreen-tools/.evergreen/csfle/azurekms](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/azurekms) +for scripts to create a Azure instance for testing. The Evergreen task SHOULD set a `batchtime` of 14 days to reduce how +often this test case runs. + +Attempt to create a datakey with $C$ using the `"azure"` KMS provider and following `DataKeyOpts`: + +```typescript +class DataKeyOpts { + masterKey: { + "keyVaultEndpoint": "https://keyvault-drivers-2411.vault.azure.net/keys/", + "keyName": "KEY-NAME", + } +} +``` + +This should successfully load and use the Azure credentials of the service account attached to the virtual machine. + +Expect the key to be successfully created. + +### 20. Bypass creating mongocryptd client when shared library is loaded + +> [!NOTE] +> IMPORTANT: If [crypt_shared](../client-side-encryption.md#crypt_shared) is not visible to the operating system's +> library search mechanism, this test should be skipped. + +The following tests that a mongocryptd client is not created when shared library is in-use. + +1. Start a new thread (referred to as `listenerThread`) + +2. On `listenerThread`, create a TcpListener on 127.0.0.1 endpoint and port 27021. Start the listener and wait for + establishing connections. If any connection is established, then signal about this to the main thread. + + Drivers MAY pass a different port if they expect their testing infrastructure to be using port 27021. Pass a port + that should be free. + +3. Create a MongoClient configured with auto encryption (referred to as `client_encrypted`) + + Configure the required options. Use the `local` KMS provider as follows: + + ```javascript + { "local": { "key": } } + ``` + + Configure with the `keyVaultNamespace` set to `keyvault.datakeys`. + + Configure the following `extraOptions`: + + ```javascript + { + "mongocryptdURI": "mongodb://localhost:27021/?serverSelectionTimeoutMS=1000" + } + ``` + +4. Use `client_encrypted` to insert the document `{"unencrypted": "test"}` into `db.coll`. + +5. Expect no signal from `listenerThread`. + +### 21. Automatic Data Encryption Keys + +The Automatic Data Encryption Keys tests require MongoDB server 7.0+. The tests must not run against a standalone. + +> [!NOTE] +> MongoDB Server 7.0 introduced a backwards breaking change to the Queryable Encryption (QE) protocol: QEv2. +> libmongocrypt 1.8.0 is configured to use the QEv2 protocol. + +For each of the following test cases, assume `DB` is a valid open database handle, and assume a +[ClientEncryption](../client-side-encryption.md#clientencryption) object `CE` created using the following options: + +```javascript +clientEncryptionOptions: { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { + local: { key: base64Decode(LOCAL_MASTERKEY) }, + aws: { + accessKeyId: , + secretAccessKey: + }, + }, +} +``` + +Run each test case with each of these KMS providers: `aws`, `local`. The KMS provider name is referred to as +`kmsProvider`. When testing `aws`, use the following as the `masterKey` option: + +```javascript +{ + region: "us-east-1", + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" +} +``` + +When testing `local`, set `masterKey` to `null`. + +#### Case 1: Simple Creation and Validation + +This test is the most basic to verify that +[CreateEncryptedCollection](../client-side-encryption.md#create-encrypted-collection-helper) created a collection with +queryable encryption enabled. It verifies that the server rejects an attempt to insert plaintext in an encrypted fields. + +1. Create a new create-collection options $Opts$ including the following: + + ```typescript + { + encryptedFields: { + fields: [{ + path: "ssn", + bsonType: "string", + keyId: null + }] + } + } + ``` + +2. Invoke $CreateEncryptedCollection(CE, DB, "testing1", Opts, kmsProvider, masterKey)$ to obtain a new collection + $Coll$. Expect success. + +3. Attempt to insert the following document into `Coll`: + + ```typescript + { + ssn: "123-45-6789" + } + ``` + +4. Expect an error from the insert operation that indicates that the document failed validation. This error indicates + that the server expects to receive an encrypted field for `ssn`, but we tried to insert a plaintext field via a + client that is unaware of the encryption requirements. + +#### Case 2: Missing `encryptedFields` + +The [CreateEncryptedCollection](../client-side-encryption.md#create-encrypted-collection-helper) helper should not +create a regular collection if there are no `encryptedFields` for the collection being created. Instead, it should +generate an error indicated that the `encryptedFields` option is missing. + +1. Create a new empty create-collection options $Opts$. (i.e. it must not contain any `encryptedFields` options.) +2. Invoke $CreateEncryptedCollection(CE, DB, "testing1", Opts, kmsProvider, masterKey)$. +3. Expect the invocation to fail with an error indicating that `encryptedFields` is not defined for the collection, and + expect that no collection was created within the database. It would be *incorrect* for + [CreateEncryptedCollection](../client-side-encryption.md#create-encrypted-collection-helper) to create a regular + collection without queryable encryption enabled. + +#### Case 3: Invalid `keyId` + +The [CreateEncryptedCollection](../client-side-encryption.md#create-encrypted-collection-helper) helper only inspects +`encryptedFields.fields` for `keyId` of `null`. +[CreateEncryptedCollection](../client-side-encryption.md#create-encrypted-collection-helper) should forward all other +data as-is, even if it would be malformed. The server should generate an error when attempting to create a collection +with such invalid settings. + +> [!NOTE] +> This test is not required if the type system of the driver has a compile-time check that fields' `keyId`s are of the +> correct type. + +1. Create a new create-collection options $Opts$ including the following: + + ```typescript + { + encryptedFields: { + fields: [{ + path: "ssn", + bsonType: "string", + keyId: false, + }] + } + } + ``` + +2. Invoke $CreateEncryptedCollection(CE, DB, "testing1", Opts, kmsProvider, masterKey)$. + +3. Expect an error from the server indicating a validation error at `create.encryptedFields.fields.keyId`, which must be + a UUID and not a boolean value. + +#### Case 4: Insert encrypted value + +This test is continuation of the case 1 and provides a way to complete inserting with encrypted value. + +1. Create a new create-collection options $Opts$ including the following: + + ```typescript + { + encryptedFields: { + fields: [{ + path: "ssn", + bsonType: "string", + keyId: null + }] + } + } + ``` + +2. Invoke $CreateEncryptedCollection(CE, DB, "testing1", Opts, kmsProvider, masterKey)$ to obtain a new collection + $Coll$ and data key $key1$. Expect success. + +3. Use $CE$ to explicitly encrypt the string "123-45-6789" using algorithm $Unindexed$ and data key $key1$. Refer result + as $encryptedPayload$. + +4. Attempt to insert the following document into `Coll`: + + ```typescript + { + ssn: + } + ``` + + Expect success. + +### 22. Range Explicit Encryption + +The Range Explicit Encryption tests utilize Queryable Encryption (QE) range protocol V2 and require MongoDB server +8.0.0-rc14+ for [SERVER-91889](https://jira.mongodb.org/browse/SERVER-91889) and libmongocrypt 1.11.0+ for +[MONGOCRYPT-705](https://jira.mongodb.org/browse/MONGOCRYPT-705). The tests must not run against a standalone. + +Each of the following test cases must pass for each of the supported types (`DecimalNoPrecision`, `DecimalPrecision`, +`DoublePrecision`, `DoubleNoPrecision`, `Date`, `Int`, and `Long`), unless it is stated the type should be skipped. + +Tests for `DecimalNoPrecision` must only run against a replica set. `DecimalNoPrecision` queries are expected to take a +long time and may exceed the default mongos timeout. + +Before running each of the following test cases, perform the following Test Setup. + +#### Test Setup + +Load the file for the specific data type being tested `range-encryptedFields-.json`. For example, for `Int` load +[range-encryptedFields-Int.json](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/etc/data/range-encryptedFields-Int.json) +as `encryptedFields`. + +Load the file +[key1-document.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/keys/key1-document.json) +as `key1Document`. + +Read the `"_id"` field of `key1Document` as `key1ID`. + +Drop and create the collection `db.explicit_encryption` using `encryptedFields` as an option. See +[FLE 2 CreateCollection() and Collection.Drop()](../client-side-encryption.md#create-collection-helper). + +Drop and create the collection `keyvault.datakeys`. + +Insert `key1Document` in `keyvault.datakeys` with majority write concern. + +Create a MongoClient named `keyVaultClient`. + +Create a ClientEncryption object named `clientEncryption` with these options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Create a MongoClient named `encryptedClient` with these `AutoEncryptionOpts`: + +```typescript +class AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, + bypassQueryAnalysis: true, +} +``` + +The remaining tasks require setting `RangeOpts`. [Test Setup: RangeOpts](#test-setup-rangeopts) lists the values to use +for `RangeOpts` for each of the supported data types. + +Use `clientEncryption` to encrypt these values: 0, 6, 30, and 200. Ensure the type matches that of the encrypted field. +For example, if the encrypted field is `encryptedDoubleNoPrecision` encrypt the value 6.0. + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Use `encryptedClient` to insert the following documents into `db.explicit_encryption`: + +```javascript +{ "_id": 0, "encrypted": } +{ "_id": 1, "encrypted": } +{ "_id": 2, "encrypted": } +{ "_id": 3, "encrypted": } +``` + +#### Test Setup: RangeOpts + +This section lists the values to use for `RangeOpts` for each of the supported data types, since each data type requires +a different `RangeOpts`. + +Each test listed in the cases below must pass for all supported data types unless it is stated the type should be +skipped. + +1. DecimalNoPrecision + + ```typescript + class RangeOpts { + trimFactor: 1, + sparsity: 1, + } + ``` + +2. DecimalPrecision + + ```typescript + class RangeOpts { + min: { "$numberDecimal": "0" }, + max: { "$numberDecimal": "200" }, + trimFactor: 1, + sparsity: 1, + precision: 2, + } + ``` + +3. DoubleNoPrecision + + ```typescript + class RangeOpts { + trimFactor: 1 + sparsity: 1, + } + ``` + +4. DoublePrecision + + ```typescript + class RangeOpts { + min: { "$numberDouble": "0" }, + max: { "$numberDouble": "200" }, + trimFactor: 1, + sparsity: 1, + precision: 2, + } + ``` + +5. Date + + ```typescript + class RangeOpts { + min: {"$date": { "$numberLong": "0" } } , + max: {"$date": { "$numberLong": "200" } }, + trimFactor: 1, + sparsity: 1, + } + ``` + +6. Int + + ```typescript + class RangeOpts { + min: {"$numberInt": "0" } , + max: {"$numberInt": "200" }, + trimFactor: 1, + sparsity: 1, + } + ``` + +7. Long + + ```typescript + class RangeOpts { + min: {"$numberLong": "0" } , + max: {"$numberLong": "200" }, + trimFactor: 1, + sparsity: 1, + } + ``` + +#### Case 1: can decrypt a payload + +Use `clientEncryption.encrypt()` to encrypt the value 6. Ensure the type matches that of the encrypted field. For +example, if the encrypted field is `encryptedLong` encrypt a BSON int64 type, not a BSON int32 type. + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Store the result in `insertPayload`. + +Use `clientEncryption` to decrypt `insertPayload`. Assert the returned value equals 6 and has the expected type. + +> [!NOTE] +> The type returned by `clientEncryption.decrypt()` may differ from the input type to `clientEncryption.encrypt()` +> depending on how the driver unmarshals BSON numerics to language native types. Example: a driver may unmarshal a BSON +> int64 to a numeric type that does not distinguish between int64 and int32. + +#### Case 2: can find encrypted range and return the maximum + +Use `clientEncryption.encryptExpression()` to encrypt this query: + +```javascript +// Convert 6 and 200 to the encrypted field type +{ "$and": [ { "encrypted": { "$gte": 6 } }, { "encrypted": { "$lte": 200 } } ] } +``` + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + queryType: "range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter `findPayload` +and sort the results by `_id`. + +Assert the following three documents are returned: + +```javascript +// Convert 6, 30, and 200 to the encrypted field type +{ "_id": 1, "encrypted": 6 } +{ "_id": 2, "encrypted": 30 } +{ "_id": 3, "encrypted": 200 } +``` + +#### Case 3: can find encrypted range and return the minimum + +Use `clientEncryption.encryptExpression()` to encrypt this query: + +```javascript +// Convert 0 and 6 to the encrypted field type +{ "$and": [ { "encrypted": { "$gte": 0 } }, { "encrypted": { "$lte": 6 } } ] } +``` + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + queryType: "range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter `findPayload` +and sort the results by `_id`. + +Assert the following two documents are returned: + +```javascript +// Convert 0 and 6 to the encrypted field type +{ "_id": 0, "encrypted": 0 } +{ "_id": 1, "encrypted": 6 } +``` + +#### Case 4: can find encrypted range with an open range query + +Use `clientEncryption.encryptExpression()` to encrypt this query: + +```javascript +// Convert 30 to the encrypted field type +{ "$and": [ { "encrypted": { "$gt": 30 } } ] } +``` + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + queryType: "range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter `findPayload` +and sort the results by `_id`. + +Assert the following document is returned: + +```javascript +// Convert 200 to the encrypted field type +{ "_id": 3, "encrypted": 200 } +``` + +#### Case 5: can run an aggregation expression inside $expr + +Use `clientEncryption.encryptExpression()` to encrypt this query: + +```javascript +// Convert 30 to the encrypted field type +{ "$and": [ { "$lt": [ "$encrypted", 30 ] } ] } } +``` + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + queryType: "range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Store the result in `findPayload`. + +Use `encryptedClient` to run a "find" operation on the `db.explicit_encryption` collection with the filter +`{ "$expr": }` and sort the results by `_id`. + +Assert the following two documents are returned: + +```javascript +// Convert 0 and 6 to the encrypted field type +{ "_id": 0, "encrypted": 0 } +{ "_id": 1, "encrypted": 6 } +``` + +#### Case 6: encrypting a document greater than the maximum errors + +This test case should be skipped if the encrypted field is `encryptedDoubleNoPrecision` or +`encryptedDecimalNoPrecision`. + +Use `clientEncryption.encrypt()` to encrypt the value 201. Ensure the type matches that of the encrypted field. + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Assert that an error was raised because 201 is greater than the maximum value in `RangeOpts`. + +#### Case 7: encrypting a value of a different type errors + +This test case should be skipped if the encrypted field is `encryptedDoubleNoPrecision` or +`encryptedDecimalNoPrecision`. + +Use `clientEncryption.encrypt()` to encrypt the value 6 with a type that does not match that of the encrypted field. + +If the encrypted field is `encryptedInt` use a BSON double type. Otherwise, use a BSON int32 type. + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Ensure that `RangeOpts` corresponds to the type of the encrypted field (i.e. expected type) and not that of the value +being passed to `clientEncryption.encrypt()`. + +Assert that an error was raised. + +#### Case 8: setting precision errors if the type is not double or Decimal128 + +This test case should be skipped if the encrypted field is `encryptedDoublePrecision`, `encryptedDoubleNoPrecision`, +`encryptedDecimalPrecision`, or `encryptedDecimalNoPrecision`. + +Use `clientEncryption.encrypt()` to encrypt the value 6. Ensure the type matches that of the encrypted field. + +Add `{ precision: 2 }` to the encrypted field's `RangeOpts` (see: [Test Setup: RangeOpts](#test-setup-rangeopts)). + +Encrypt using the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "Range", + contentionFactor: 0, + rangeOpts: , +} +``` + +Assert that an error was raised. + +### 23. Range Explicit Encryption applies defaults + +This test requires libmongocrypt with changes in +[14ccd9ce](https://github.com/mongodb/libmongocrypt/commit/14ccd9ce8a030158aec07f63e8139d34b95d88e6) +([MONGOCRYPT-698](https://jira.mongodb.org/browse/MONGOCRYPT-698)). + +#### Test Setup + +Create a MongoClient named `keyVaultClient`. + +Create a ClientEncryption object named `clientEncryption` with these options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: keyVaultClient, + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": "" } }, +} +``` + +Create a key with `clientEncryption.createDataKey`. Store the returned key ID in a variable named `keyId`. + +Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options: + +```typescript +class EncryptOpts { + keyId : keyId, + algorithm: "Range", + contentionFactor: 0, + rangeOpts: RangeOpts { + min: 0, + max: 1000 + } +} +``` + +Store the result in a variable named `payload_defaults`. + +#### Case 1: Uses libmongocrypt defaults + +Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options: + +```typescript +class EncryptOpts { + keyId : keyId, + algorithm: "Range", + contentionFactor: 0, + rangeOpts: RangeOpts { + min: 0, + max: 1000, + sparsity: 2, + trimFactor: 6 + } +} +``` + +Assert the returned payload size equals the size of `payload_defaults`. + +> [!NOTE] +> Do not compare the payload contents. The payloads include random data. The `trimFactor` and `sparsity` directly affect +> the payload size. + +#### Case 2: Accepts `trimFactor` 0 + +Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options: + +```typescript +class EncryptOpts { + keyId : keyId, + algorithm: "Range", + contentionFactor: 0, + rangeOpts: RangeOpts { + min: 0, + max: 1000, + trimFactor: 0 + } +} +``` + +Assert the returned payload size is greater than the size of `payload_defaults`. + +> [!NOTE] +> Do not compare the payload contents. The payloads include random data. The `trimFactor` and `sparsity` directly affect +> the payload size. + +### 24. KMS Retry Tests + +The following tests that certain AWS, Azure, and GCP KMS operations are retried on transient errors. + +This test uses a mock server with configurable failpoints to simulate network failures. To start the server: + +```shell +python -u kms_failpoint_server.py --port 9003 +``` + +See the [TLS tests](#10-kms-tls-tests) for running the mock server on Evergreen. See +[the mock server implementation](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/4ba50d373652b6fb39239745664637e33e2b01e6/.evergreen/csfle/kms_failpoint_server.py) +and the +[C driver tests](https://github.com/mongodb/mongo-c-driver/blob/d934cd5de55af65220816e4fd01ce3f9c0ef1cd4/src/libmongoc/tests/test-mongoc-client-side-encryption.c#L6295) +for how to configure failpoints. + +#### Setup + +1. Start a `mongod` process with **server version 4.2.0 or later**. +2. Start the failpoint KMS server with: `python -u kms_failpoint_server.py --port 9003`. +3. Create a `MongoClient` for key vault operations. +4. Create a `ClientEncryption` object (referred to as `client_encryption`) with `keyVaultNamespace` set to + `keyvault.datakeys`. + +The failpoint server is configured using HTTP requests. Example request to simulate a network failure: + +`curl -X POST https://localhost:9003/set_failpoint/network -d '{"count": 1}' --cacert drivers-evergreen-tools/.evergreen/x509gen/ca.pem` + +To simulate an HTTP failure, replace `network` with `http`. + +When the following test cases request setting `masterKey`, use the following values based on the KMS provider: + +For "aws": + +```javascript +{ + "region": "foo", + "key": "bar", + "endpoint": "127.0.0.1:9003", +} +``` + +For "azure": + +```javascript +{ + "keyVaultEndpoint": "127.0.0.1:9003", + "keyName": "foo", +} +``` + +For "gcp": + +```javascript +{ + "projectId": "foo", + "location": "bar", + "keyRing": "baz", + "keyName": "qux", + "endpoint": "127.0.0.1:9003" +} +``` + +#### Case 1: createDataKey and encrypt with TCP retry + +1. Configure the mock server to simulate one network failure. +2. Call `client_encryption.createDataKey()` with "aws" as the provider. Expect this to succeed. Store the returned key + ID in a variable named `keyId`. +3. Configure the mock server to simulate another network failure. +4. Call `clientEncryption.encrypt` with the following `EncryptOpts` to encrypt the int32 value `123` with the newly + created key: + ```typescript + class EncryptOpts { + keyId : , + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + } + ``` + Expect this to succeed. + +Repeat this test with the `azure` and `gcp` masterKeys. + +#### Case 2: createDataKey and encrypt with HTTP retry + +1. Configure the mock server to simulate one HTTP failure. +2. Call `client_encryption.createDataKey()` with "aws" as the provider. Expect this to succeed. Store the returned key + ID in a variable named `keyId`. +3. Configure the mock server to simulate another HTTP failure. +4. Call `clientEncryption.encrypt` with the following `EncryptOpts` to encrypt the int32 value `123` with the newly + created key: + ```typescript + class EncryptOpts { + keyId : , + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + } + ``` + Expect this to succeed. + +Repeat this test with the `azure` and `gcp` masterKeys. + +#### Case 3: createDataKey fails after too many retries + +1. Configure the mock server to simulate four network failures. +2. Call `client_encryption.createDataKey()` with "aws" as the provider. Expect this to fail. + +Repeat this test with the `azure` and `gcp` masterKeys. + +### 25. Test $lookup + +All tests require libmongocrypt 1.13.0, server 7.0+, and must be skipped on standalone. Tests define more constraints. + +The syntax `` is used to refer to the content of the corresponding file in `../etc/data/lookup`. + +#### Setup + +Create an encrypted MongoClient named `encryptedClient` configured with: + +```python +AutoEncryptionOpts( + keyVaultNamespace="db.keyvault", + kmsProviders={"local": { "key": "" }} +) +``` + +Use `encryptedClient` to drop `db.keyvault`. Insert `` into `db.keyvault` with majority write concern. + +Use `encryptedClient` to drop and create the following collections: + +- `db.csfle` with options: `{ "validator": { "$jsonSchema": ""}}`. +- `db.csfle2` with options: `{ "validator": { "$jsonSchema": ""}}`. +- `db.qe` with options: `{ "encryptedFields": ""}`. +- `db.qe2` with options: `{ "encryptedFields": ""}`. +- `db.no_schema` with no options. +- `db.no_schema2` with no options. +- `db.non_csfle_schema` with options: `{ "validator": { "$jsonSchema": ""}}` + +Create an unencrypted MongoClient named `unencryptedClient`. + +Insert documents with `encryptedClient`: + +- `{"csfle": "csfle"}` into `db.csfle` + - Use `unencryptedClient` to retrieve it. Assert the `csfle` field is BSON binary. +- `{"csfle2": "csfle2"}` into `db.csfle2` + - Use `unencryptedClient` to retrieve it. Assert the `csfle2` field is BSON binary. +- `{"qe": "qe"}` into `db.qe` + - Use `unencryptedClient` to retrieve it. Assert the `qe` field is BSON binary. +- `{"qe2": "qe2"}` into `db.qe2` + - Use `unencryptedClient` to retrieve it. Assert the `qe2` field is BSON binary. +- `{"no_schema": "no_schema"}` into `db.no_schema` +- `{"no_schema2": "no_schema2"}` into `db.no_schema2` +- `{"non_csfle_schema": "non_csfle_schema"}` into `db.non_csfle_schema` + +#### Case 1: `db.csfle` joins `db.no_schema` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.csfle` with the following pipeline: + +```json +[ + {"$match" : {"csfle" : "csfle"}}, + { + "$lookup" : { + "from" : "no_schema", + "as" : "matched", + "pipeline" : [ {"$match" : {"no_schema" : "no_schema"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect one document to be returned matching: `{"csfle" : "csfle", "matched" : [ {"no_schema" : "no_schema"} ]}`. + +#### Case 2: `db.qe` joins `db.no_schema` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.qe` with the following pipeline: + +```json +[ + {"$match" : {"qe" : "qe"}}, + { + "$lookup" : { + "from" : "no_schema", + "as" : "matched", + "pipeline" : + [ {"$match" : {"no_schema" : "no_schema"}}, {"$project" : {"_id" : 0, "__safeContent__" : 0}} ] + } + }, + {"$project" : {"_id" : 0, "__safeContent__" : 0}} +] +``` + +Expect one document to be returned matching: `{"qe" : "qe", "matched" : [ {"no_schema" : "no_schema"} ]}`. + +#### Case 3: `db.no_schema` joins `db.csfle` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.no_schema` with the following pipeline: + +```json +[ + {"$match" : {"no_schema" : "no_schema"}}, + { + "$lookup" : { + "from" : "csfle", + "as" : "matched", + "pipeline" : [ {"$match" : {"csfle" : "csfle"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect one document to be returned matching: `{"no_schema" : "no_schema", "matched" : [ {"csfle" : "csfle"} ]}`. + +#### Case 4: `db.no_schema` joins `db.qe` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.no_schema` with the following pipeline: + +```json +[ + {"$match" : {"no_schema" : "no_schema"}}, + { + "$lookup" : { + "from" : "qe", + "as" : "matched", + "pipeline" : [ {"$match" : {"qe" : "qe"}}, {"$project" : {"_id" : 0, "__safeContent__" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect one document to be returned matching: `{"no_schema" : "no_schema", "matched" : [ {"qe" : "qe"} ]}`. + +#### Case 5: `db.csfle` joins `db.csfle2` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.csfle` with the following pipeline: + +```json +[ + {"$match" : {"csfle" : "csfle"}}, + { + "$lookup" : { + "from" : "csfle2", + "as" : "matched", + "pipeline" : [ {"$match" : {"csfle2" : "csfle2"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect one document to be returned matching: `{"csfle" : "csfle", "matched" : [ {"csfle2" : "csfle2"} ]}`. + +#### Case 6: `db.qe` joins `db.qe2` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.qe` with the following pipeline: + +```json +[ + {"$match" : {"qe" : "qe"}}, + { + "$lookup" : { + "from" : "qe2", + "as" : "matched", + "pipeline" : [ {"$match" : {"qe2" : "qe2"}}, {"$project" : {"_id" : 0, "__safeContent__" : 0}} ] + } + }, + {"$project" : {"_id" : 0, "__safeContent__" : 0}} +] +``` + +Expect one document to be returned matching: `{"qe" : "qe", "matched" : [ {"qe2" : "qe2"} ]}`. + +#### Case 7: `db.no_schema` joins `db.no_schema2` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.no_schema` with the following pipeline: + +```json +[ + {"$match" : {"no_schema" : "no_schema"}}, + { + "$lookup" : { + "from" : "no_schema2", + "as" : "matched", + "pipeline" : [ {"$match" : {"no_schema2" : "no_schema2"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect one document to be returned matching: +`{"no_schema" : "no_schema", "matched" : [ {"no_schema2" : "no_schema2"} ]}`. + +#### Case 8: `db.csfle` joins `db.qe` + +Test requires server 8.1+ and mongocryptd/crypt_shared 8.1+ + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.csfle` with the following pipeline: + +```json +[ + {"$match" : {"csfle" : "qe"}}, + { + "$lookup" : { + "from" : "qe", + "as" : "matched", + "pipeline" : [ {"$match" : {"qe" : "qe"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect an exception to be thrown with a message containing one of the following substrings depending on the +mongocryptd/crypt_shared and libmongocrypt versions: + +- mongocryptd/crypt_shared \<8.2 **or** libmongocrypt \<1.17.0: `not supported`. +- mongocryptd/crypt_shared 8.2+ **and** libmongocrypt 1.17.0+: + `Cannot specify both encryptionInformation and csfleEncryptionSchemas unless csfleEncryptionSchemas only contains non-encryption JSON schema validators`. + +#### Case 9: test error with \<8.1 + +This case requires mongocryptd/crypt_shared \<8.1. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.csfle` with the following pipeline: + +```json +[ + {"$match" : {"csfle" : "csfle"}}, + { + "$lookup" : { + "from" : "no_schema", + "as" : "matched", + "pipeline" : [ {"$match" : {"no_schema" : "no_schema"}}, {"$project" : {"_id" : 0}} ] + } + }, + {"$project" : {"_id" : 0}} +] +``` + +Expect an exception to be thrown with a message containing the substring `Upgrade`. + +#### Case 10: `db.qe` joins `db.non_csfle_schema` + +Test requires server 8.2+, mongocryptd/crypt_shared 8.2+, and libmongocrypt 1.17.0+. + +Recreate `encryptedClient` with the same `AutoEncryptionOpts` as the setup. (Recreating prevents schema caching from +impacting the test). + +Run an aggregate operation on `db.qe` with the following pipeline: + +```json +[ + {"$match" : {"qe" : "qe"}}, + { + "$lookup" : { + "from" : "non_csfle_schema", + "as" : "matched", + "pipeline" : [ {"$match" : {"non_csfle_schema" : "non_csfle_schema"}}, {"$project" : {"_id" : 0, "__safeContent__" : 0}} ] + } + }, + {"$project" : {"_id" : 0, "__safeContent__" : 0}} +] +``` + +Expect one document to be returned matching: `{"qe" : "qe", "matched" : [ {"non_csfle_schema" : "non_csfle_schema"} ]}`. + +### 26. Custom AWS Credentials + +These tests require valid AWS credentials for the remote KMS provider via the secrets manager (FLE_AWS_KEY and +FLE_AWS_SECRET). These tests MUST NOT run inside an AWS environment that has the same credentials set in order to +properly ensure the tests would fail using on-demand credentials. + +#### Case 1: ClientEncryption with `credentialProviders` and incorrect `kmsProviders` + +Create a MongoClient named `setupClient`. + +Create a [ClientEncryption](../client-side-encryption.md#clientencryption) object with the following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": { "accessKeyId": , "secretAccessKey": } }, + credentialProviders: { "aws": } +} +``` + +Assert that an error is thrown. + +#### Case 2: ClientEncryption with `credentialProviders` works + +Create a MongoClient named `setupClient`. + +Create a [ClientEncryption](../client-side-encryption.md#clientencryption) object with the following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": {} }, + credentialProviders: { "aws": } +} +``` + +Use the client encryption to create a datakey using the "aws" KMS provider. This should successfully load and use the +AWS credentials that were provided by the secrets manager for the remote provider. Assert the datakey was created and +that the custom credential provider was called at least once. + +An example of this in Node.js: + +```typescript +import { ClientEncryption, MongoClient } from 'mongodb'; + +let calledCount = 0; +const masterKey = { + region: '', + key: '' +}; +const keyVaultClient = new MongoClient(process.env.MONGODB_URI); +const options = { + keyVaultNamespace: 'keyvault.datakeys', + kmsProviders: { aws: {} }, + credentialProviders: { + aws: async () => { + calledCount++; + return { + accessKeyId: process.env.FLE_AWS_KEY, + secretAccessKey: process.env.FLE_AWS_SECRET + }; + } + } +}; +const clientEncryption = new ClientEncryption(keyVaultClient, options); +const dk = await clientEncryption.createDataKey('aws', { masterKey }); +expect(dk).to.be.a(Binary); +expect(calledCount).to.be.greaterThan(0); +``` + +#### Case 3: `AutoEncryptionOpts` with `credentialProviders` and incorrect `kmsProviders` + +Create a `MongoClient` object with the following options: + +```typescript +class AutoEncryptionOpts { + autoEncryption: { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": { "accessKeyId": , "secretAccessKey": } }, + credentialProviders: { "aws": } + } +} +``` + +Assert that an error is thrown. + +#### Case 4: ClientEncryption with `credentialProviders` and valid environment variables. + +Ensure a valid `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are present in the environment. + +Create a MongoClient named `setupClient`. + +Create a [ClientEncryption](../client-side-encryption.md#clientencryption) object with the following options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "aws": {} }, + credentialProviders: { "aws": } +} +``` + +Use the client encryption to create a datakey using the "aws" KMS provider. This should successfully load and use the +AWS credentials that were provided by the secrets manager for the remote provider. Assert the datakey was created and +that the custom credential provider was called at least once. + +An example of this in Node.js: + +```typescript +import { ClientEncryption, MongoClient } from 'mongodb'; + +let calledCount = 0; +const masterKey = { + region: '', + key: '' +}; +const keyVaultClient = new MongoClient(process.env.MONGODB_URI); +const options = { + keyVaultNamespace: 'keyvault.datakeys', + kmsProviders: { aws: {} }, + credentialProviders: { + aws: async () => { + calledCount++; + return { + accessKeyId: process.env.FLE_AWS_KEY, + secretAccessKey: process.env.FLE_AWS_SECRET + }; + } + } +}; +const clientEncryption = new ClientEncryption(keyVaultClient, options); +const dk = await clientEncryption.createDataKey('aws', { masterKey }); +expect(dk).to.be.a(Binary); +expect(calledCount).to.be.greaterThan(0); +``` + +### 27. String Explicit Encryption + +The String Explicit Encryption tests utilize Queryable Encryption (QE) and require MongoDB server 8.2.0+ and +libmongocrypt 1.18.1+. The tests must not run against a standalone. + +Before running each of the following test cases, perform the following Test Setup. + +#### Test Setup + +Using [QE CreateCollection() and Collection.Drop()](../client-side-encryption.md#create-collection-helper), drop and +create the following collections with majority write concern: + +- `db.prefix-suffix` using the `encryptedFields` option set to the contents of + [encryptedFields-prefix-suffix.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix.json). + This step requires server 9.0.0+. +- `db.prefix-suffix-ci-di` using the `encryptedFields` option set to the contents of + [encryptedFields-prefix-suffix-ci-di.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-ci-di.json). + This step requires server 9.0.0+. +- `db.prefix-suffix-preview` using the `encryptedFields` option set to the contents of + [encryptedFields-prefix-suffix-preview.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-prefix-suffix-preview.json). + This step requires server pre-9.0.0. +- `db.substring` using the `encryptedFields` option set to the contents of + [encryptedFields-substring.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-substring.json) + This step requires server 9.0.0+. +- `db.substring-ci-di` using the `encryptedFields` option set to the contents of + [encryptedFields-substring-ci-di.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-substring-ci-di.json) + This step requires server 9.0.0+. +- `db.substring-preview` using the `encryptedFields` option set to the contents of + [encryptedFields-substring-preview.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/encryptedFields-substring-preview.json) + This step requires server pre-9.0.0. + +Load the file +[key1-document.json](https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/etc/data/keys/key1-document.json) +as `key1Document`. + +Read the `"_id"` field of `key1Document` as `key1ID`. + +Drop and create the collection `keyvault.datakeys`. + +Insert `key1Document` in `keyvault.datakeys` with majority write concern. + +Create a MongoClient named `keyVaultClient`. + +Create a ClientEncryption object named `clientEncryption` with these options: + +```typescript +class ClientEncryptionOpts { + keyVaultClient: , + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Create a MongoClient named `explicitEncryptedClient` with these `AutoEncryptionOpts`: + +```typescript +class AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, + bypassQueryAnalysis: true, +} +``` + +Create a MongoClient with auto-encryption enabled (without `bypassQueryAnalysis`) named `autoEncryptedClient` using +these `AutoEncryptionOpts`: + +```typescript +class AutoEncryptionOpts { + keyVaultNamespace: "keyvault.datakeys", + kmsProviders: { "local": { "key": } }, +} +``` + +Use `clientEncryption` to encrypt the string `"foobarbaz"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + }, + suffix: SuffixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + }, + }, +} +``` + +Use `explicitEncryptedClient` to insert the following document into `db.prefix-suffix` (if created) and +`db.prefix-suffix-preview` (if created) with majority write concern: + +```javascript +{ "_id": 0, "encryptedText": } +``` + +Use `clientEncryption` to encrypt the string `"foobarbaz"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + substring: SubstringOpts { + strMaxLength: 10, + strMaxQueryLength: 6, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to insert the following document into `db.substring` (if created) and +`db.substring-preview` (if created) with majority write concern: + +```javascript +{ "_id": 0, "encryptedText": } +``` + +#### Case 1: can find a document by prefix + +Run this case multiple times with the following sets of parameters: + +- `queryType=prefix` and `collection=prefix-suffix` + - Require server 9.0.0+ and libmongocrypt 1.19.0+. +- `queryType=prefixPreview` and `collection=prefix-suffix-preview` + - Require server pre-9.0.0 and libmongocrypt 1.19.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"foo"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrStartsWith: {input: '$encryptedText', prefix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "_id": 0, "encryptedText": "foobarbaz" } +``` + +#### Case 2: can find a document by suffix + +Run this case multiple times with the following sets of parameters: + +- `queryType=suffix` and `collection=prefix-suffix` + - Require server 9.0.0+ and libmongocrypt 1.19.0+. +- `queryType=suffixPreview` and `collection=prefix-suffix-preview` + - Require server pre-9.0.0 and libmongocrypt 1.19.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"baz"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + suffix: SuffixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrEndsWith: {input: '$encryptedText', suffix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "_id": 0, "encryptedText": "foobarbaz" } +``` + +#### Case 3: assert no document found by prefix + +Run this case multiple times with the following sets of parameters: + +- `queryType=prefix` and `collection=prefix-suffix` + - Require server 9.0.0+ and libmongocrypt 1.19.0+. +- `queryType=prefixPreview` and `collection=prefix-suffix-preview` + - Require server pre-9.0.0 and libmongocrypt 1.19.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"baz"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrStartsWith: {input: '$encryptedText', prefix: } } } +``` + +Assert that no documents are returned. + +#### Case 4: assert no document found by suffix + +Run this case multiple times with the following sets of parameters: + +- `queryType=suffix` and `collection=prefix-suffix` + - Require server 9.0.0+ and libmongocrypt 1.19.0+. +- `queryType=suffixPreview` and `collection=prefix-suffix-preview` + - Require server pre-9.0.0 and libmongocrypt 1.19.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"foo"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + suffix: SuffixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrEndsWith: {input: '$encryptedText', suffix: } } } +``` + +Assert that no documents are returned. + +#### Case 5: can find a document by substring + +Run this case multiple times with the following sets of parameters: + +- `queryType=substring` and `collection=substring` + - Require server 9.0.0+ and libmongocrypt 1.20.0+. +- `queryType=substringPreview` and `collection=substring-preview` + - Require server pre-9.0.0 and libmongocrypt 1.18.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"bar"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + substring: SubstringOpts { + strMaxLength: 10, + strMaxQueryLength: 6, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrContains: {input: '$encryptedText', substring: } } } +``` + +Assert the following document is returned: + +```javascript +{ "_id": 0, "encryptedText": "foobarbaz" } +``` + +#### Case 6: assert no document found by substring + +Run this case multiple times with the following sets of parameters: + +- `queryType=substring` and `collection=substring` + - Require server 9.0.0+ and libmongocrypt 1.20.0+. +- `queryType=substringPreview` and `collection=substring-preview` + - Require server pre-9.0.0 and libmongocrypt 1.18.1+. + +Use `clientEncryption.encrypt()` to encrypt the string `"qux"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + substring: SubstringOpts { + strMaxLength: 10, + strMaxQueryLength: 6, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.` collection with the following filter: + +```javascript +{ $expr: { $encStrContains: {input: '$encryptedText', substring: } } } +``` + +Assert that no documents are returned. + +#### Case 7: assert `contentionFactor` is required + +This test case requires MongoDB server 9.0.0+ and libmongocrypt 1.19.0+. + +Use `clientEncryption.encrypt()` to encrypt the string `"foo"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "prefix", + stringOpts: StringOpts { + caseSensitive: true, + diacriticSensitive: true, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Expect an error from libmongocrypt with a message containing the string: "contention factor is required for string +algorithm". + +#### Case 8: can find an auto-encrypted case-insensitively indexed document by prefix and suffix + +This is a regression test for [DRIVERS-3470](https://jira.mongodb.org/browse/DRIVERS-3470). This test case requires +MongoDB server 9.0.0+ and libmongocrypt 1.19.0+. + +Use `autoEncryptedClient` to insert the following document into `db.prefix-suffix-ci-di` with majority write concern. + +```javascript +{ "encryptedText": "BingQiLin" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"bing"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "prefix", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.prefix-suffix-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrStartsWith: {input: '$encryptedText', prefix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "BingQiLin" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"lin"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "suffix", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + suffix: SuffixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.prefix-suffix-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrEndsWith: {input: '$encryptedText', suffix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "BingQiLin" } +``` + +#### Case 9: can find an auto-encrypted diacritic-insensitively indexed document by prefix and suffix + +This is a regression test for [DRIVERS-3470](https://jira.mongodb.org/browse/DRIVERS-3470). This test case requires +MongoDB server 9.0.0+ and libmongocrypt 1.19.0+. + +Use `autoEncryptedClient` to insert the following document into `db.prefix-suffix-ci-di` with majority write concern: + +```javascript +{ "encryptedText": "cafébarbäz" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"cafe"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "prefix", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + prefix: PrefixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.prefix-suffix-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrStartsWith: {input: '$encryptedText', prefix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "cafébarbäz" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"baz"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "suffix", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + suffix: SuffixOpts { + strMaxQueryLength: 10, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.prefix-suffix-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrEndsWith: {input: '$encryptedText', suffix: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "cafébarbäz" } +``` + +#### Case 10: can find an auto-encrypted case-insensitively indexed document by substring + +This is a regression test for [DRIVERS-3470](https://jira.mongodb.org/browse/DRIVERS-3470). This test case requires +MongoDB server 9.0.0+ and libmongocrypt 1.20.0+. + +Use `autoEncryptedClient` to insert the following document into `db.substring-ci-di` with majority write concern: + +```javascript +{ "encryptedText": "FooBarBaz" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"bar"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "substring", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + substring: SubstringOpts { + strMaxLength: 10, + strMaxQueryLength: 6, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.substring-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrContains: {input: '$encryptedText', substring: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "FooBarBaz" } +``` + +#### Case 11: can find an auto-encrypted diacritic-insensitively indexed document by substring + +This is a regression test for [DRIVERS-3470](https://jira.mongodb.org/browse/DRIVERS-3470). This test case requires +MongoDB server 9.0.0+ and libmongocrypt 1.20.0+. + +Use `autoEncryptedClient` to insert the following document into `db.substring-ci-di` with majority write concern: + +```javascript +{ "encryptedText": "foocafébaz" } +``` + +Use `clientEncryption.encrypt()` to encrypt the string `"cafe"` with the following `EncryptOpts`: + +```typescript +class EncryptOpts { + keyId : , + algorithm: "String", + queryType: "substring", + contentionFactor: 0, + stringOpts: StringOpts { + caseSensitive: false, + diacriticSensitive: false, + substring: SubstringOpts { + strMaxLength: 10, + strMaxQueryLength: 6, + strMinQueryLength: 2, + } + }, +} +``` + +Use `explicitEncryptedClient` to run a "find" operation on the `db.substring-ci-di` collection with the following +filter: + +```javascript +{ $expr: { $encStrContains: {input: '$encryptedText', substring: } } } +``` + +Assert the following document is returned: + +```javascript +{ "encryptedText": "foocafébaz" } +``` diff --git a/source/client-side-encryption/tests/benchmarks.md b/source/client-side-encryption/tests/benchmarks.md new file mode 100644 index 0000000000..64e1694a84 --- /dev/null +++ b/source/client-side-encryption/tests/benchmarks.md @@ -0,0 +1,51 @@ +# In-Use Encryption: Benchmarks + +______________________________________________________________________ + +## Benchmarking Bindings + +Drivers are encouraged to benchmark the bindings to libmongocrypt. Benchmarking may help to identify performance issues +due to the cost of calling between the native language and the C library. + +A handle to libmongocrypt (`mongocrypt_t`) is needed for the benchmark. In the public driver API, `mongocrypt_t` is an +implementation detail contained in a `MongoClient`. The bindings API may more directly interface `mongocrypt_t`. +Example: the Java bindings API contains a +[MongoCrypt class](https://github.com/mongodb/mongo-java-driver/blob/main/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoCrypt.java) +closely wrapping the `mongocrypt_t`. + +If possible, drivers are encouraged to use the bindings API and mock responses from the MongoDB server. This may help to +narrow the scope of the benchmarked code. See +[BenchmarkRunner.java](https://github.com/mongodb/libmongocrypt/blob/b81e66e0208d13e07c2e5e60b3170f0cfc61e1e2/bindings/java/mongocrypt/benchmarks/src/main/java/com/mongodb/crypt/benchmark/BenchmarkRunner.java) +for an example using the Java bindings API. If that is not possible, the benchmark can be implemented using the +`MongoClient` API, and the benchmark will include the time spent communicating with the MongoDB server. + +### Benchmarking Bulk Decryption + +Set up the benchmark data: + +- Create a data key with the "local" KMS provider. +- Encrypt 1500 string values of the form `value 0001`, `value 0002`, `value 0003`, ... with the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`. +- Create a document of the form: + `{ "key0001": , "key0002": , "key0003": }`. +- Create a handle to `mongocrypt_t`. This may be through the bindings API (preferred) or through a `MongoClient` + configured with `AutoEncryptionOpts`. + +Warm up the benchmark: + +- Use the handle to decrypt the document repeatedly for one second. + +Run the benchmark. Repeat benchmark for thread counts: (1, 2, 8, 64): + +- Start threads. Use the same handle between all threads (`mongocrypt_t` is thread-safe). +- In each thread: decrypt the document repeatedly for one second. +- Count the number of decrypt operations performed (ops/sec). +- Repeat 10 times. + +Produce results: + +- Report the median result of the ops/sec for each thread count. + +**Note:** The `mongocrypt_t` handle caches the decrypted Data Encryption Key (DEK) for a fixed time period of one +minute. If the benchmark exceeds one minute, the DEK will be requested again from the key vault collection. Reporting +the median of trials is expected to prevent this impacting results. diff --git a/source/client-side-encryption/tests/legacy/aggregate.json b/source/client-side-encryption/tests/legacy/aggregate.json new file mode 100644 index 0000000000..7de725b71d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/aggregate.json @@ -0,0 +1,390 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "Aggregate with deterministic encryption", + "skipReason": "SERVER-39395", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encrypted_string": "457-55-5642" + } + } + ] + }, + "result": [ + { + "_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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encrypted_string": "457-55-5642" + } + } + ] + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Aggregate with empty pipeline", + "skipReason": "SERVER-40829 hides agg support behind enableTestCommands flag.", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [] + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [], + "cursor": {} + }, + "command_name": "aggregate" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Aggregate should fail with random encryption", + "skipReason": "SERVER-39395", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "random": "abc" + } + } + ] + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + }, + { + "description": "Database aggregate should fail", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$currentOp": { + "allUsers": false, + "idleConnections": false, + "localOps": true + } + }, + { + "$match": { + "command.aggregate": { + "$eq": 1 + } + } + }, + { + "$project": { + "command": 1 + } + }, + { + "$project": { + "command.lsid": 0 + } + } + ] + }, + "result": { + "errorContains": "non-collection command not supported for auto encryption: aggregate" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/aggregate.yml b/source/client-side-encryption/tests/legacy/aggregate.yml new file mode 100644 index 0000000000..64ad5efa60 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/aggregate.yml @@ -0,0 +1,120 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "Aggregate with deterministic encryption" + skipReason: "SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + aggregate: *collection_name + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Aggregate with empty pipeline" + skipReason: "SERVER-40829 hides agg support behind enableTestCommands flag." + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: [] + result: + - { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + aggregate: *collection_name + pipeline: [] + cursor: {} + command_name: aggregate + # Needs to fetch key when decrypting results + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Aggregate should fail with random encryption" + skipReason: "SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { random: "abc" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "Database aggregate should fail" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: aggregate + object: database + arguments: + pipeline: + - $currentOp: { allUsers: false, idleConnections: false, localOps: true } + - $match: { command.aggregate: { $eq: 1 } } + - $project: { command: 1 } + - $project: { command.lsid: 0 } + result: + errorContains: "non-collection command not supported for auto encryption: aggregate" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/awsTemporary.json b/source/client-side-encryption/tests/legacy/awsTemporary.json new file mode 100644 index 0000000000..10eb85feee --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/awsTemporary.yml b/source/client-side-encryption/tests/legacy/awsTemporary.yml new file mode 100644 index 0000000000..cc787530a0 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/awsTemporary.yml @@ -0,0 +1,57 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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: &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - 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: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Insert with invalid temporary credentials" + clientOptions: + autoEncryptOpts: + kmsProviders: + awsTemporaryNoSessionToken: {} + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "security token" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/azureKMS.json b/source/client-side-encryption/tests/legacy/azureKMS.json new file mode 100644 index 0000000000..b0f5111370 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/azureKMS.json @@ -0,0 +1,235 @@ +{ + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "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/source/client-side-encryption/tests/legacy/azureKMS.yml b/source/client-side-encryption/tests/legacy/azureKMS.yml new file mode 100644 index 0000000000..a402973ad0 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/azureKMS.yml @@ -0,0 +1,46 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], '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: &doc0 { _id: 1, encrypted_string_azure: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - 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: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_azure: {'$binary': {'base64': 'AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/badQueries.json b/source/client-side-encryption/tests/legacy/badQueries.json new file mode 100644 index 0000000000..4968307ba3 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/badQueries.json @@ -0,0 +1,1446 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "$text unconditionally fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "$text": { + "$search": "search text" + } + } + }, + "result": { + "errorContains": "Unsupported match expression operator for encryption" + } + } + ] + }, + { + "description": "$where unconditionally fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "$where": { + "$code": "function() { return true }" + } + } + }, + "result": { + "errorContains": "Unsupported match expression operator for encryption" + } + } + ] + }, + { + "description": "$bit operators succeed on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAllClear": 35 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAllClear": 35 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAllSet": 35 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAllSet": 35 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAnyClear": 35 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAnyClear": 35 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAnySet": 35 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAnySet": 35 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "geo operators succeed on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$near": [ + 0, + 0 + ] + } + } + }, + "result": { + "errorContains": "unable to find index" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$near": [ + 0, + 0 + ] + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$nearSphere": [ + 0, + 0 + ] + } + } + }, + "result": { + "errorContains": "unable to find index" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$nearSphere": [ + 0, + 0 + ] + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$geoIntersects": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$geoIntersects": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$geoWithin": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$geoWithin": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "inequality operators succeed on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$gt": 1 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$gt": 1 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$lt": 1 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$lt": 1 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$gte": 1 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$gte": 1 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$lte": 1 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$lte": 1 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "other misc operators succeed on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$mod": [ + 3, + 1 + ] + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$mod": [ + 3, + 1 + ] + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$regex": "pattern", + "$options": "" + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$regex": "pattern", + "$options": "" + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$size": 2 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$size": 2 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$type": 2 + } + } + }, + "result": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$type": 2 + } + } + }, + "result": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$eq": null + } + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$eq": null + } + } + }, + "result": { + "errorContains": "Illegal equality to null predicate for encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$in": [ + null + ] + } + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + null + ] + } + } + }, + "result": { + "errorContains": "Illegal equality to null inside $in against an encrypted field" + } + } + ] + }, + { + "description": "$addToSet succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$addToSet": { + "unencrypted": [ + "a" + ] + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$addToSet": { + "encrypted_string": [ + "a" + ] + } + } + }, + "result": { + "errorContains": "$addToSet not allowed on encrypted values" + } + } + ] + }, + { + "description": "$inc succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$inc and $mul not allowed on encrypted values" + } + } + ] + }, + { + "description": "$mul succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$mul": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$mul": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$inc and $mul not allowed on encrypted values" + } + } + ] + }, + { + "description": "$max succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$max": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$max": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$max and $min not allowed on encrypted values" + } + } + ] + }, + { + "description": "$min succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$min": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$min": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$max and $min not allowed on encrypted values" + } + } + ] + }, + { + "description": "$currentDate succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$currentDate": { + "unencrypted": true + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$currentDate": { + "encrypted_string": true + } + } + }, + "result": { + "errorContains": "$currentDate not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pop succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pop": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pop": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$pop not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pull succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pull": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pull": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$pull not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pullAll succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pullAll": { + "unencrypted": [ + 1 + ] + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pullAll": { + "encrypted_string": [ + 1 + ] + } + } + }, + "result": { + "errorContains": "$pullAll not allowed on encrypted values" + } + } + ] + }, + { + "description": "$push succeeds on unencrypted, error on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$push": { + "unencrypted": 1 + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$push": { + "encrypted_string": 1 + } + } + }, + "result": { + "errorContains": "$push not allowed on encrypted values" + } + } + ] + }, + { + "description": "array filters on encrypted fields does not error in mongocryptd, but errors in mongod", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_string.$[i].x": 1 + } + }, + "arrayFilters": [ + { + "i.x": 1 + } + ] + }, + "result": { + "errorContains": "Array update operations not allowed on encrypted values" + } + } + ] + }, + { + "description": "positional operator succeeds on unencrypted, errors on encrypted", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "unencrypted": 1 + }, + "update": { + "$set": { + "unencrypted.$": 1 + } + } + }, + "result": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encrypted_string": "abc" + }, + "update": { + "$set": { + "encrypted_string.$": "abc" + } + } + }, + "result": { + "errorContains": "Cannot encrypt fields below '$' positional update operator" + } + } + ] + }, + { + "description": "an update that would produce an array on an encrypted field errors", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_string": [ + 1, + 2 + ] + } + } + }, + "result": { + "errorContains": "Cannot encrypt element of type" + } + } + ] + }, + { + "description": "an insert with encrypted field on _id errors", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "schemaMap": { + "default.default": { + "properties": { + "_id": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + }, + "result": { + "errorContains": "Invalid schema containing the 'encrypt' keyword." + } + } + ] + }, + { + "description": "an insert with an array value for an encrypted field fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "encrypted_string": [ + "123", + "456" + ] + } + }, + "result": { + "errorContains": "Cannot encrypt element of type" + } + } + ] + }, + { + "description": "an insert with a Timestamp(0,0) value in the top-level fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "random": { + "$timestamp": { + "t": 0, + "i": 0 + } + } + } + }, + "result": { + "errorContains": "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" + } + } + ] + }, + { + "description": "distinct with the key referring to a field where the keyID is a JSON Pointer errors", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": {}, + "fieldName": "encrypted_w_altname" + }, + "result": { + "errorContains": "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/badQueries.yml b/source/client-side-encryption/tests/legacy/badQueries.yml new file mode 100644 index 0000000000..893622c4ce --- /dev/null +++ b/source/client-side-encryption/tests/legacy/badQueries.yml @@ -0,0 +1,536 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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']}] + +# TODO: I could see an argument against having these tests of mongocryptd as part +# of driver tests. When mongocryptd introduces support for these operators, these +# tests will fail. But it's also easy enough to remove these tests when that happens. + +tests: + - description: "$text unconditionally fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { $text: { $search: "search text" } } + result: + errorContains: "Unsupported match expression operator for encryption" + - description: "$where unconditionally fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { $where: { $code: "function() { return true }" } } + result: + errorContains: "Unsupported match expression operator for encryption" + - description: "$bit operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $bitsAllClear: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllClear: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAllSet: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllSet: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnyClear: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnyClear: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnySet: 35 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnySet: 35 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "geo operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $near: [0,0] }} + result: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $near: [0,0] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $nearSphere: [0,0] }} + result: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $nearSphere: [0,0] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[ [0,0], [1,0], [1,1], [0,0] ]] }} }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "inequality operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $gt: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $gt: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lt: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $lt: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $gte: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $gte: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lte: 1 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $lte: 1 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - description: "other misc operators succeed on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { unencrypted: { $mod: [3, 1] }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $mod: [3, 1] }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $regex: "pattern", $options: "" }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $regex: "pattern", $options: "" }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $size: 2 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $size: 2 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $type: 2 }} + result: [] + - name: find + arguments: + filter: { encrypted_string: { $type: 2 }} + result: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $eq: null }} + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + - &doc1 { _id: 2, encrypted_string: "string1" } + - name: find + arguments: + filter: { encrypted_string: { $eq: null }} + result: + errorContains: "Illegal equality to null predicate for encrypted field" + - name: find + arguments: + filter: { unencrypted: { $in: [null] }} + result: + - *doc0 + - *doc1 + - name: find + arguments: + filter: { encrypted_string: { $in: [null] }} + result: + errorContains: "Illegal equality to null inside $in against an encrypted field" + - description: "$addToSet succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $addToSet: { "unencrypted": ["a"]}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $addToSet: { "encrypted_string": ["a"]}} + result: + errorContains: "$addToSet not allowed on encrypted values" + - description: "$inc succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $inc: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $inc: { "encrypted_string": 1}} + result: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$mul succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $mul: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $mul: { "encrypted_string": 1}} + result: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$max succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $max: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $max: { "encrypted_string": 1}} + result: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$min succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $min: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $min: { "encrypted_string": 1}} + result: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$currentDate succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $currentDate: { "unencrypted": true}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $currentDate: { "encrypted_string": true }} + result: + errorContains: "$currentDate not allowed on encrypted values" + - description: "$pop succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pop: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pop: { "encrypted_string": 1 }} + result: + errorContains: "$pop not allowed on encrypted values" + - description: "$pull succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pull: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pull: { "encrypted_string": 1 }} + result: + errorContains: "$pull not allowed on encrypted values" + - description: "$pullAll succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $pullAll: { "unencrypted": [1] }} + result: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $pullAll: { "encrypted_string": [1] }} + result: + errorContains: "$pullAll not allowed on encrypted values" + - description: "$push succeeds on unencrypted, error on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $push: { "unencrypted": 1}} + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { } + update: { $push: { "encrypted_string": 1 }} + result: + errorContains: "$push not allowed on encrypted values" + - description: "array filters on encrypted fields does not error in mongocryptd, but errors in mongod" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $set : { "encrypted_string.$[i].x": 1 }} + arrayFilters: [{ i.x: 1 }] + result: + errorContains: "Array update operations not allowed on encrypted values" + - description: "positional operator succeeds on unencrypted, errors on encrypted" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { "unencrypted": 1 } + update: { $set : { "unencrypted.$": 1 }} + result: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { "encrypted_string": "abc" } + update: { $set : { "encrypted_string.$": "abc" }} + result: + errorContains: "Cannot encrypt fields below '$' positional update operator" + - description: "an update that would produce an array on an encrypted field errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $set : { "encrypted_string": [1,2] }} + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with encrypted field on _id errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + schemaMap: + "default.default": {'properties': {'_id': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}} + operations: + - name: insertOne + arguments: + document: { _id: 1 } + result: + errorContains: "Invalid schema containing the 'encrypt' keyword." + - description: "an insert with an array value for an encrypted field fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { encrypted_string: [ "123", "456"] } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with a Timestamp(0,0) value in the top-level fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { random: {"$timestamp": {"t": 0, "i": 0 }} } + result: + errorContains: "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" + - description: "distinct with the key referring to a field where the keyID is a JSON Pointer errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: distinct + arguments: + filter: {} + fieldName: "encrypted_w_altname" + result: + errorContains: "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/badSchema.json b/source/client-side-encryption/tests/legacy/badSchema.json new file mode 100644 index 0000000000..1fd0f8ed3f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/badSchema.json @@ -0,0 +1,254 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "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": "Schema with an encrypted field in an array", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + }, + "bsonType": "array" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + }, + { + "description": "Schema without specifying parent object types", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "foo": { + "properties": { + "bar": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + } + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + }, + { + "description": "Schema with siblings of encrypt document", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + }, + "bsonType": "object" + } + } + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "'encrypt' cannot be used in conjunction with 'bsonType'" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + }, + { + "description": "Schema with logical keywords", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "anyOf": [ + { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + ] + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/badSchema.yml b/source/client-side-encryption/tests/legacy/badSchema.yml new file mode 100644 index 0000000000..035774de59 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/badSchema.yml @@ -0,0 +1,73 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +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: "Schema with an encrypted field in an array" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}}, 'bsonType': 'array'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] + - description: "Schema without specifying parent object types" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'foo': {'properties': {'bar': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}}}}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] + - description: "Schema with siblings of encrypt document" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}, 'bsonType': 'object'}}} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "'encrypt' cannot be used in conjunction with 'bsonType'" + outcome: + collection: + data: [] + - description: "Schema with logical keywords" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'anyOf': [{'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}}}]} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: *doc0 + result: + errorContains: "Invalid schema" + outcome: + collection: + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/basic.json b/source/client-side-encryption/tests/legacy/basic.json new file mode 100644 index 0000000000..3ed066f530 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/basic.json @@ -0,0 +1,350 @@ +{ + "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 with deterministic encryption, then find it", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_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", + "readConcern": { + "level": "majority" + } + }, + "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" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Insert with randomized encryption, then find it", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "random": "123" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "random": "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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "random": { + "$$type": "binData" + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/basic.yml b/source/client-side-encryption/tests/legacy/basic.yml new file mode 100644 index 0000000000..dfbf5270cf --- /dev/null +++ b/source/client-side-encryption/tests/legacy/basic.yml @@ -0,0 +1,102 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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 with deterministic encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Insert with randomized encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, random: "123" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc1] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - { _id: 1, random: { $$type: "binData" } } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, random: { $$type: "binData" } } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/bulk.json b/source/client-side-encryption/tests/legacy/bulk.json new file mode 100644 index 0000000000..1b62e5e8ab --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bulk.json @@ -0,0 +1,333 @@ +{ + "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": "Bulk write with encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "encrypted_string": "string1" + } + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + } + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "$and": [ + { + "encrypted_string": "string1" + }, + { + "_id": 2 + } + ] + } + } + } + ], + "options": { + "ordered": true + } + } + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + } + } + ], + "ordered": true + }, + "command_name": "update" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "$and": [ + { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + }, + { + "_id": { + "$eq": 2 + } + } + ] + }, + "limit": 1 + } + ], + "ordered": true + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/bulk.yml b/source/client-side-encryption/tests/legacy/bulk.yml new file mode 100644 index 0000000000..824ccdaa10 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bulk.yml @@ -0,0 +1,78 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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: "Bulk write with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: bulkWrite + arguments: + requests: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + - name: insertOne + arguments: + document: &doc1 { _id: 2, encrypted_string: "string1" } + - name: updateOne + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + - name: deleteOne + arguments: + filter: { $and: [{ encrypted_string: "string1" }, { _id: 2 }]} + options: { ordered: true } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}}, random: { $$type: "binData" } } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} }} + u: {$set: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} }} + ordered: true + command_name: update + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { "$and": [ { "encrypted_string": { "$eq": {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} }}, { "_id": { "$eq": 2 }} ] } + limit: 1 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}}, random: { $$type: "binData" } } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/bypassAutoEncryption.json b/source/client-side-encryption/tests/legacy/bypassAutoEncryption.json new file mode 100644 index 0000000000..9d09cb3fa9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bypassAutoEncryption.json @@ -0,0 +1,402 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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 with bypassAutoEncryption", + "clientOptions": { + "autoEncryptOpts": { + "bypassAutoEncryption": true, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "encrypted_string": "string0" + }, + "bypassDocumentValidation": true + } + }, + { + "name": "find", + "arguments": { + "filter": {} + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": {} + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + } + }, + { + "description": "Insert with bypassAutoEncryption for local schema", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "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" + } + }, + "bypassAutoEncryption": true, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "encrypted_string": "string0" + }, + "bypassDocumentValidation": true + } + }, + { + "name": "find", + "arguments": { + "filter": {} + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": {} + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/bypassAutoEncryption.yml b/source/client-side-encryption/tests/legacy/bypassAutoEncryption.yml new file mode 100644 index 0000000000..7a7bf74e1d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bypassAutoEncryption.yml @@ -0,0 +1,100 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [{_id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} }] +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 with bypassAutoEncryption" + clientOptions: + autoEncryptOpts: + bypassAutoEncryption: true + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + - name: find + arguments: + filter: { } + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { } + command_name: find + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - { _id: 2, encrypted_string: "string0" } + - description: "Insert with bypassAutoEncryption for local schema" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'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'} + bypassAutoEncryption: true + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + - name: find + arguments: + filter: { } + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { } + command_name: find + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - { _id: 2, encrypted_string: "string0" } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/bypassedCommand.json b/source/client-side-encryption/tests/legacy/bypassedCommand.json new file mode 100644 index 0000000000..18054a70cb --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bypassedCommand.json @@ -0,0 +1,107 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": {}, + "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": "ping is bypassed", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "runCommand", + "object": "database", + "command_name": "ping", + "arguments": { + "command": { + "ping": 1 + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "ping": 1 + }, + "command_name": "ping" + } + } + ] + }, + { + "description": "kill op is not bypassed", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "runCommand", + "object": "database", + "command_name": "killOp", + "arguments": { + "command": { + "killOp": 1, + "op": 1234 + } + }, + "result": { + "errorContains": "command not supported for auto encryption: killOp" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/bypassedCommand.yml b/source/client-side-encryption/tests/legacy/bypassedCommand.yml new file mode 100644 index 0000000000..98e360b6e3 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/bypassedCommand.yml @@ -0,0 +1,43 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {} +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: "ping is bypassed" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: ping + arguments: + command: + ping: 1 + expectations: + # No listCollections, no mongocryptd command, just the ping. + - command_started_event: + command: + ping: 1 + command_name: ping + - description: "kill op is not bypassed" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: killOp + arguments: + command: + killOp: 1 + op: 1234 + result: + errorContains: "command not supported for auto encryption: killOp" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/count.json b/source/client-side-encryption/tests/legacy/count.json new file mode 100644 index 0000000000..9df8cd639e --- /dev/null +++ b/source/client-side-encryption/tests/legacy/count.json @@ -0,0 +1,229 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "Count with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "count", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "result": 2 + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "count": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + "command_name": "count" + } + } + ] + }, + { + "description": "Count fails when filtering on a random encrypted field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "count", + "arguments": { + "filter": { + "random": "abc" + } + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/count.yml b/source/client-side-encryption/tests/legacy/count.yml new file mode 100644 index 0000000000..b8c436a4c0 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/count.yml @@ -0,0 +1,54 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "Count with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: count + arguments: + filter: { encrypted_string: "string0" } + result: 2 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + count: *collection_name + query: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + command_name: count + - description: "Count fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: count + arguments: + filter: { random: "abc" } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/countDocuments.json b/source/client-side-encryption/tests/legacy/countDocuments.json new file mode 100644 index 0000000000..07ff97f264 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/countDocuments.json @@ -0,0 +1,241 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "countDocuments with deterministic encryption", + "skipReason": "waiting on SERVER-39395", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "countDocuments", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "result": 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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/countDocuments.yml b/source/client-side-encryption/tests/legacy/countDocuments.yml new file mode 100644 index 0000000000..e28b478c05 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/countDocuments.yml @@ -0,0 +1,52 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "countDocuments with deterministic encryption" + skipReason: "waiting on SERVER-39395" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: countDocuments + arguments: + filter: { encrypted_string: "string0" } + result: 1 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + aggregate: *collection_name + pipeline: + - { $match: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} }} + - { $group: { _id: 1, n: { $sum: 1 }}} + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/create-and-createIndexes.json b/source/client-side-encryption/tests/legacy/create-and-createIndexes.json new file mode 100644 index 0000000000..48638a97c8 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/create-and-createIndexes.yml b/source/client-side-encryption/tests/legacy/create-and-createIndexes.yml new file mode 100644 index 0000000000..2d9f5ef711 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/create-and-createIndexes.yml @@ -0,0 +1,58 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +tests: + - description: "create is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "unencryptedCollection" + - name: createCollection + object: database + arguments: + collection: "unencryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: "unencryptedCollection" + - description: "createIndexes is OK" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + collection: "unencryptedCollection" + index: name \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/delete.json b/source/client-side-encryption/tests/legacy/delete.json new file mode 100644 index 0000000000..a6f4ffde91 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/delete.json @@ -0,0 +1,340 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "deleteOne with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "deleteOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "deleteMany with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "deleteMany", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + } + }, + "result": { + "deletedCount": 2 + } + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + }, + "limit": 0 + } + ], + "ordered": true + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/delete.yml b/source/client-side-encryption/tests/legacy/delete.yml new file mode 100644 index 0000000000..60810d063d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/delete.yml @@ -0,0 +1,91 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "deleteOne with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: deleteOne + arguments: + filter: { encrypted_string: "string0" } + result: + deletedCount: 1 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + limit: 1 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted + - description: "deleteMany with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: deleteMany + arguments: + filter: { encrypted_string: { $in: [ "string0", "string1" ] } } + result: + deletedCount: 2 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + delete: *collection_name + deletes: + - q: { encrypted_string: { $in : [ {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}}, {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} ] } } + limit: 0 + ordered: true + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/distinct.json b/source/client-side-encryption/tests/legacy/distinct.json new file mode 100644 index 0000000000..9786b07814 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/distinct.json @@ -0,0 +1,276 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "distinct with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "fieldName": "encrypted_string" + }, + "result": [ + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "distinct": "default", + "key": "encrypted_string", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + "command_name": "distinct" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Distinct fails when filtering on a random encrypted field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": { + "random": "abc" + }, + "fieldName": "encrypted_string" + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/distinct.yml b/source/client-side-encryption/tests/legacy/distinct.yml new file mode 100644 index 0000000000..ca1d8fbf6b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/distinct.yml @@ -0,0 +1,66 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc2_encrypted { _id: 3, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "distinct with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: distinct + arguments: + filter: { encrypted_string: "string0" } + fieldName: "encrypted_string" + result: + - "string0" + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + distinct: *collection_name + key: encrypted_string + query: { encrypted_string: {$eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + command_name: distinct + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted + - description: "Distinct fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: distinct + arguments: + filter: { random: "abc" } + fieldName: "encrypted_string" + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/explain.json b/source/client-side-encryption/tests/legacy/explain.json new file mode 100644 index 0000000000..8ca3b48d37 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/explain.json @@ -0,0 +1,239 @@ +{ + "runOn": [ + { + "minServerVersion": "7.0.0" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "Explain a find with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "runCommand", + "object": "database", + "command_name": "explain", + "arguments": { + "command": { + "explain": { + "find": "default", + "filter": { + "encrypted_string": "string1" + } + } + } + } + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "explain": { + "find": "default", + "filter": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + } + }, + "verbosity": "allPlansExecution" + }, + "command_name": "explain" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/explain.yml b/source/client-side-encryption/tests/legacy/explain.yml new file mode 100644 index 0000000000..325928a72e --- /dev/null +++ b/source/client-side-encryption/tests/legacy/explain.yml @@ -0,0 +1,57 @@ +runOn: + - minServerVersion: "7.0.0" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "Explain a find with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: runCommand + object: database + command_name: explain + arguments: + command: + explain: + find: *collection_name + filter: { encrypted_string : "string1" } + + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + explain: + find: *collection_name + filter: + { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } } + verbosity: "allPlansExecution" + command_name: explain + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/find.json b/source/client-side-encryption/tests/legacy/find.json new file mode 100644 index 0000000000..1feddab0e3 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/find.json @@ -0,0 +1,408 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ], + "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": "Find with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "result": [ + { + "_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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Find with $in with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1", + "random": "abc" + } + ] + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "Find fails when filtering on a random encrypted field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "random": "abc" + } + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/find.yml b/source/client-side-encryption/tests/legacy/find.yml new file mode 100644 index 0000000000..20179a314c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/find.yml @@ -0,0 +1,105 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} , random: {'$binary': {'base64': 'AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==', 'subType': '06'}} } +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: "Find with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { encrypted_string: "string0" } + result: + - &doc0 { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + find: *collection_name + filter: + { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - description: "Find with $in with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: + { encrypted_string: { $in: [ "string0", "string1" ] } } + result: + - { _id: 1, encrypted_string: "string0" } + - &doc1 { _id: 2, encrypted_string: "string1", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + find: *collection_name + filter: + # Note, the values are re-ordered, but this is logically equivalent. + { encrypted_string: { $in: [ {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}}, {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} ] } } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - description: "Find fails when filtering on a random encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment + operations: + - name: find + arguments: + filter: { random: "abc" } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/findOneAndDelete.json b/source/client-side-encryption/tests/legacy/findOneAndDelete.json new file mode 100644 index 0000000000..e418a4581b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndDelete.json @@ -0,0 +1,221 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "findOneAndDelete with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "findOneAndDelete", + "arguments": { + "filter": { + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "remove": true + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/findOneAndDelete.yml b/source/client-side-encryption/tests/legacy/findOneAndDelete.yml new file mode 100644 index 0000000000..6650f2132d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndDelete.yml @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "findOneAndDelete with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndDelete + arguments: + filter: + { encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: + { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + remove: true + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/findOneAndReplace.json b/source/client-side-encryption/tests/legacy/findOneAndReplace.json new file mode 100644 index 0000000000..78baca8432 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndReplace.json @@ -0,0 +1,227 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "findOneAndReplace with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "findOneAndReplace", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "replacement": { + "encrypted_string": "string1" + }, + "returnDocument": "Before" + }, + "result": { + "_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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "update": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/findOneAndReplace.yml b/source/client-side-encryption/tests/legacy/findOneAndReplace.yml new file mode 100644 index 0000000000..f39f3c0fb4 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndReplace.yml @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "findOneAndReplace with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndReplace + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1" } + returnDocument: Before + result: { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + update: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/findOneAndUpdate.json b/source/client-side-encryption/tests/legacy/findOneAndUpdate.json new file mode 100644 index 0000000000..1d85851151 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndUpdate.json @@ -0,0 +1,231 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "findOneAndUpdate with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + }, + "returnDocument": "Before" + }, + "result": { + "_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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/findOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/findOneAndUpdate.yml new file mode 100644 index 0000000000..459902fd44 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/findOneAndUpdate.yml @@ -0,0 +1,50 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "findOneAndUpdate with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: findOneAndUpdate + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + returnDocument: Before + result: { _id: 1, encrypted_string: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + findAndModify: *collection_name + query: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + update: { $set: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } } + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-BypassQueryAnalysis.json b/source/client-side-encryption/tests/legacy/fle2v2-BypassQueryAnalysis.json new file mode 100644 index 0000000000..9b28df2f9a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-BypassQueryAnalysis.yml b/source/client-side-encryption/tests/legacy/fle2v2-BypassQueryAnalysis.yml new file mode 100644 index 0000000000..51e7a56758 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-BypassQueryAnalysis.yml @@ -0,0 +1,89 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: &doc0_encrypted { + "_id": 1, + "encryptedIndexed": { + "$binary": { + # Payload has an IndexKey of key1 and UserKey of key1. + "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: *collection_name + command_name: listCollections + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + 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" } }] } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Compact.json b/source/client-side-encryption/tests/legacy/fle2v2-Compact.json new file mode 100644 index 0000000000..868095e1e6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Compact.json @@ -0,0 +1,233 @@ +{ + "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" + } + }, + "result": { + "ok": 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" + } + }, + { + "$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/source/client-side-encryption/tests/legacy/fle2v2-Compact.yml b/source/client-side-encryption/tests/legacy/fle2v2-Compact.yml new file mode 100644 index 0000000000..125e531ee8 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Compact.yml @@ -0,0 +1,84 @@ +# Requires libmongocrypt 1.8.0. libmongocrypt 1.10.0 has a bug (MONGOCRYPT-699) that may cause this test to fail on server version 7. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: *collection_name + result: + ok: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + 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: *collection_name + result: + errorContains: "'compactStructuredEncryptionData.compactionTokens' is missing" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection-OldServer.json b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection-OldServer.json new file mode 100644 index 0000000000..c266aa6b83 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection-OldServer.yml b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection-OldServer.yml new file mode 100644 index 0000000000..5cc6ead0f6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection-OldServer.yml @@ -0,0 +1,61 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "6.0.0" + maxServerVersion: "6.3.99" + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] + +database_name: &database_name "default" +collection_name: &collection_name "default" + +tests: + - description: "driver returns an error if creating a QEv2 collection on unsupported server" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }} + } + ] + } + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - 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." + # Assert no collections were created. + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: &esc_collection_name "enxcol_.encryptedCollection.esc" + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: &ecc_collection_name "enxcol_.encryptedCollection.ecc" + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: &ecoc_collection_name "enxcol_.encryptedCollection.ecoc" + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: encryptedCollection diff --git a/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection.json b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection.json new file mode 100644 index 0000000000..c324be8abc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection.yml b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection.yml new file mode 100644 index 0000000000..590be5f8a6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-CreateCollection.yml @@ -0,0 +1,937 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] + +database_name: &database_name "default" +collection_name: &collection_name "default" + +tests: + - description: "state collections and index are created" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: &encrypted_fields { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" }} + } + ] + } + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: &encrypted_collection_name "encryptedCollection" + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: &esc_collection_name "enxcol_.encryptedCollection.esc" + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: &ecc_collection_name "enxcol_.encryptedCollection.ecc" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: &ecoc_collection_name "enxcol_.encryptedCollection.ecoc" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + # Data collection is created after. + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: &encrypted_fields_expectation { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" }} + } + ] + } + command_name: create + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + - description: "default state collection names are applied" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: *encrypted_fields + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + # Data collection is created after. + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + command_name: create + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + - description: "drop removes all state collections" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: *encrypted_fields + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexNotExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + # Data collection is created after. + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields + command_name: create + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + - description: "CreateCollection without encryptedFields." + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: *encrypted_fields + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "plaintextCollection" + - name: createCollection + object: database + arguments: + collection: "plaintextCollection" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: "plaintextCollection" + + expectations: + # events from dropCollection ... begin + # expect listCollections to be sent on drop to check for remote encryptedFields. + - command_started_event: + command: + listCollections: 1 + filter: { name: "plaintextCollection" } + command_name: listCollections + database_name: *database_name + - command_started_event: + command: + drop: "plaintextCollection" + command_name: drop + database_name: *database_name + # events from dropCollection ... end + - command_started_event: + command: + create: "plaintextCollection" + command_name: create + database_name: *database_name + - description: "CreateCollection from encryptedFieldsMap." + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: *encrypted_fields + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + # Data collection is created after. + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + command_name: create + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + - description: "CreateCollection from encryptedFields." + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + # Data collection is created after. + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + command_name: create + database_name: *database_name + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - command_started_event: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + command_name: listCollections + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + + - description: "DropCollection from encryptedFieldsMap" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: *encrypted_fields + operations: + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + - description: "DropCollection from encryptedFields" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: {} + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + command_name: create + database_name: *database_name + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - command_started_event: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + command_name: listCollections + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + + - description: "DropCollection from remote encryptedFields" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: {} + + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + index: __safeContent___1 + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + database: *database_name + collection: *encrypted_collection_name + + expectations: + # events from dropCollection ... begin + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + # events from createCollection ... begin + - command_started_event: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + command_name: create + database_name: *database_name + - command_started_event: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + command_name: create + database_name: *database_name + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - command_started_event: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + command_name: listCollections + database_name: *database_name + # Index on __safeContents__ is then created. + - command_started_event: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + command_name: createIndexes + database_name: *database_name + # events from createCollection ... end + # events from dropCollection ... begin + - command_started_event: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + command_name: listCollections + database_name: *database_name + - command_started_event: + command: + drop: *esc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *ecoc_collection_name + command_name: drop + database_name: *database_name + - command_started_event: + command: + drop: *encrypted_collection_name + command_name: drop + database_name: *database_name + # events from dropCollection ... end + - description: "encryptedFields are consulted for metadata collection names" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + encryptedFieldsMap: + default.encryptedCollection: { + "escCollection": "invalid_esc_name", + "ecocCollection": "invalid_ecoc_name", + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" }} + } + ] + } + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: database + arguments: + collection: *encrypted_collection_name + result: + # Expect error due to server constraints added in SERVER-74069 + errorContains: "Encrypted State Collection name should follow" diff --git a/source/client-side-encryption/tests/legacy/fle2v2-DecryptExistingData.json b/source/client-side-encryption/tests/legacy/fle2v2-DecryptExistingData.json new file mode 100644 index 0000000000..1fb4c1d1bc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-DecryptExistingData.yml b/source/client-side-encryption/tests/legacy/fle2v2-DecryptExistingData.yml new file mode 100644 index 0000000000..f04d752b15 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-DecryptExistingData.yml @@ -0,0 +1,66 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [ + &doc0 { + "_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: *collection_name + command_name: listCollections + - command_started_event: + command: + find: *collection_name + 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 \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Delete.json new file mode 100644 index 0000000000..ddfe57b00c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Delete.yml new file mode 100644 index 0000000000..4e1a59c93f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Delete.yml @@ -0,0 +1,100 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: &doc0 {"_id": 1, "encryptedIndexed": "value123" } + - name: deleteOne + arguments: + filter: { "encryptedIndexed": "value123" } + result: + deletedCount: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: + - { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json new file mode 100644 index 0000000000..bdc5c99bc2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml new file mode 100644 index 0000000000..d5c83d0814 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml @@ -0,0 +1,79 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &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: + # EncryptedFieldsMap overrides remote encryptedFields. + # Automatic encryption does not occur on encryptedUnindexed. The value is validated on the server. + - name: insertOne + arguments: + document: &doc0 { + _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: *collection_name + documents: + - *doc0 + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + 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: + - *doc0 \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-jsonSchema.json b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-jsonSchema.json new file mode 100644 index 0000000000..8e0c6dafa3 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-jsonSchema.yml b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-jsonSchema.yml new file mode 100644 index 0000000000..4577d16eb9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFields-vs-jsonSchema.yml @@ -0,0 +1,102 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +json_schema: { + "properties": {}, + "bsonType": "object" + } +encrypted_fields: &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: &doc0 { _id: 1, encryptedIndexed: "123" } + - name: find + arguments: + filter: { encryptedIndexed: "123" } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType" : "00" } }] } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFieldsMap-defaults.json b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFieldsMap-defaults.json new file mode 100644 index 0000000000..1c0a057cad --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFieldsMap-defaults.yml b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFieldsMap-defaults.yml new file mode 100644 index 0000000000..950cec2f36 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-EncryptedFieldsMap-defaults.yml @@ -0,0 +1,58 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &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: &efm { + "default.default": { + "fields": [] + } + } + operations: + - name: insertOne + arguments: + document: &doc0 { + _id: 1, + # Include a FLE2FindEncryptedPayload for 'encryptionInformation' to be appended. + foo: { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + expectations: + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + encryptionInformation: { + "type": { + "$numberInt": "1" + }, + "schema": { + "default.default": { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [] + } + } + } + ordered: true + command_name: insert + outcome: + collection: + data: + - *doc0 \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-FindOneAndUpdate.json new file mode 100644 index 0000000000..c5e689a3de --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-FindOneAndUpdate.yml new file mode 100644 index 0000000000..7849b6032a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-FindOneAndUpdate.yml @@ -0,0 +1,200 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: *collection_name + 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: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": { "foo": "bar"} } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + 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: *collection_name + 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: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": { "encryptedIndexed": { "$$type": "binData" }} } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: findAndModify + - command_started_event: + command: + find: *collection_name + filter: { "_id": { "$eq": 1 }} + command_name: find + outcome: + collection: + data: + - { "_id": 1, "encryptedIndexed": { "$$type": "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", "subType" : "00" } }] } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Indexed.json b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Indexed.json new file mode 100644 index 0000000000..6e156ffc60 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Indexed.yml b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Indexed.yml new file mode 100644 index 0000000000..2acd97585b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Indexed.yml @@ -0,0 +1,96 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: &doc0 { _id: 1, encryptedIndexed: "123" } + - name: find + arguments: + filter: { encryptedIndexed: "123" } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType" : "00" } }] } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Unindexed.json b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Unindexed.json new file mode 100644 index 0000000000..48280f5bd4 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Unindexed.yml b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Unindexed.yml new file mode 100644 index 0000000000..ddba6c7439 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-InsertFind-Unindexed.yml @@ -0,0 +1,89 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &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: &doc0 { _id: 1, encryptedUnindexed: "value123" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 1, "encryptedUnindexed": { $$type: "binData" } } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + 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: + # Expected error message changed in https://github.com/10gen/mongo-enterprise-modules/commit/212b584d4f7a44bed41c826a180a4aff00923d7a#diff-5f12b55e8d5c52c2f62853ec595dc2c1e2e5cb4fdbf7a32739a8e3acb3c6f818 + # Before the message was "cannot query non-indexed fields with the randomized encryption algorithm" + # After: "can only execute encrypted equality queries with an encrypted equality index" + # Use a small common substring. + errorContains: "encrypt" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-MissingKey.json b/source/client-side-encryption/tests/legacy/fle2v2-MissingKey.json new file mode 100644 index 0000000000..1e655f0a9c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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 missing 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 missing 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/source/client-side-encryption/tests/legacy/fle2v2-MissingKey.yml b/source/client-side-encryption/tests/legacy/fle2v2-MissingKey.yml new file mode 100644 index 0000000000..1152c01886 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-MissingKey.yml @@ -0,0 +1,43 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [ + &doc0 { + "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 missing 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 missing key" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: find + arguments: + filter: { } + result: + errorContains: "not all keys requested were satisfied" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-NoEncryption.json b/source/client-side-encryption/tests/legacy/fle2v2-NoEncryption.json new file mode 100644 index 0000000000..a6843c4737 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-NoEncryption.yml b/source/client-side-encryption/tests/legacy/fle2v2-NoEncryption.yml new file mode 100644 index 0000000000..8d407f7260 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-NoEncryption.yml @@ -0,0 +1,44 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &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: &doc0 { + _id: 1, + foo: "bar" + } + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + command_name: insert + outcome: + collection: + data: + - *doc0 \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.json new file mode 100644 index 0000000000..59241927ca --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.json @@ -0,0 +1,289 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "Compact works with 'range' fields", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "command_name": "compactStructuredEncryptionData", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + } + }, + "result": { + "ok": 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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "compactStructuredEncryptionData": "default", + "compactionTokens": { + "encryptedInt": { + "ecoc": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "QxKJD2If48p0l8NAXf2Kr0aleMd/dATSjBK6hTpNMyc=", + "subType": "00" + } + } + } + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "compactStructuredEncryptionData" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.yml new file mode 100644 index 0000000000..17d2c2e854 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Compact.yml @@ -0,0 +1,93 @@ +# Requires libmongocrypt 1.10.1. +runOn: + - minServerVersion: "8.0.0" # Require range v2 support on server. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: "Compact works with 'range' fields" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + # Insert before running compact to ensure the server errors if `encryptionInformation` is incorrectly omitted. + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: runCommand + object: database + command_name: compactStructuredEncryptionData + arguments: + command: + compactStructuredEncryptionData: *collection_name + result: + ok: 1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + compactStructuredEncryptionData: *collection_name + compactionTokens: + "encryptedInt": { + "ecoc": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "QxKJD2If48p0l8NAXf2Kr0aleMd/dATSjBK6hTpNMyc=", + "subType": "00" + } + } + } + encryptionInformation: *encryptionInformation + command_name: compactStructuredEncryptionData \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.json new file mode 100644 index 0000000000..df2161cc36 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.json @@ -0,0 +1,508 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.yml new file mode 100644 index 0000000000..cc5fd4d267 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Aggregate.yml @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDate": { $gt: {$date: {$numberLong: "0" }}} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.json new file mode 100644 index 0000000000..fae25a1c02 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.json @@ -0,0 +1,1842 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.yml new file mode 100644 index 0000000000..f7ed9fb93f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Correctness.yml @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "0" } } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: {$numberLong: "0"}}, $lte: { $date: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } + result: [*doc0] + + - 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: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDate: { $date: { $numberLong: "200" } }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: {$numberLong: "2"}} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "0" } } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "1" } } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: {$date: {$numberLong: "0"}}, $lte: {$date: {$numberLong: "200"}} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $in: [ {$date: {$numberLong: "0"}} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error mongocryptd. + errorContains: "value type is a date" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.json new file mode 100644 index 0000000000..b4f15d9b1f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.json @@ -0,0 +1,442 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.yml new file mode 100644 index 0000000000..188b75940b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Delete.yml @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.json new file mode 100644 index 0000000000..97ab4aaeb9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.json @@ -0,0 +1,514 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.yml new file mode 100644 index 0000000000..2c53412e9a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-FindOneAndUpdate.yml @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: {$date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: {$date: { $numberLong: "1" }} } + - name: findOneAndUpdate + arguments: + filter: { encryptedDate: { $gt: {$date: {$numberLong: "0"}}} } + update: { "$set": { "encryptedDate": {$date: {$numberLong: "2"}}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDate": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.json new file mode 100644 index 0000000000..a011c388e4 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.json @@ -0,0 +1,499 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.yml new file mode 100644 index 0000000000..db072f61ac --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-InsertFind.yml @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" }} } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" }} } + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" }} } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.json new file mode 100644 index 0000000000..6bab6499f5 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.json @@ -0,0 +1,516 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.yml new file mode 100644 index 0000000000..f7dde0a517 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Date-Update.yml @@ -0,0 +1,240 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDate', 'bsonType': 'date', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } }} + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.json new file mode 100644 index 0000000000..d1a82c2164 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.json @@ -0,0 +1,1902 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.yml new file mode 100644 index 0000000000..97a95b2d94 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Aggregate.yml @@ -0,0 +1,1675 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimalNoPrecision": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.json new file mode 100644 index 0000000000..4316a31c3e --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.json @@ -0,0 +1,1158 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.yml new file mode 100644 index 0000000000..8154624799 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Correctness.yml @@ -0,0 +1,293 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.json new file mode 100644 index 0000000000..19cae3c64f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.json @@ -0,0 +1,1116 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.yml new file mode 100644 index 0000000000..daf89dd91c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Delete.yml @@ -0,0 +1,899 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json new file mode 100644 index 0000000000..4ab3b63ea5 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json @@ -0,0 +1,1906 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml new file mode 100644 index 0000000000..e470f42db6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml @@ -0,0 +1,1672 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimalNoPrecision": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimalNoPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.json new file mode 100644 index 0000000000..5a2adf6907 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.json @@ -0,0 +1,1893 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.yml new file mode 100644 index 0000000000..ab4d44d201 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-InsertFind.yml @@ -0,0 +1,1668 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.json new file mode 100644 index 0000000000..b840d38347 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.json @@ -0,0 +1,1910 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.yml new file mode 100644 index 0000000000..723131083a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Decimal-Update.yml @@ -0,0 +1,1685 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topology: [ "replicaset" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalNoPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.json new file mode 100644 index 0000000000..271f57b125 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.json @@ -0,0 +1,584 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml new file mode 100644 index 0000000000..e6d153a08d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml @@ -0,0 +1,317 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDecimalPrecision": { $gt: {$numberDecimal: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "DRYKAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYAAQAAABNtbgAAAAAAAAAAAAAAAAAAAD4wE214ANAHAAAAAAAAAAAAAAAAPjAA", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.json new file mode 100644 index 0000000000..8954445887 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.json @@ -0,0 +1,1650 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.yml new file mode 100644 index 0000000000..cd1ced0b8f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Correctness.yml @@ -0,0 +1,424 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } + result: [*doc0] + + - 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: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDecimalPrecision: { $numberDecimal: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: {$numberDecimal: "0.0"}, $lte: {$numberDecimal: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $in: [ {$numberDecimal: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.json new file mode 100644 index 0000000000..7b3d5d8225 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.json @@ -0,0 +1,476 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.yml new file mode 100644 index 0000000000..af78be2dc2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Delete.yml @@ -0,0 +1,220 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json new file mode 100644 index 0000000000..af371f7b3f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml new file mode 100644 index 0000000000..ac1de049fd --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml @@ -0,0 +1,315 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalPrecision: { $gt: {$numberDecimal: "0"}} } + update: { "$set": { "encryptedDecimalPrecision": {$numberDecimal: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDecimalPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.json new file mode 100644 index 0000000000..bbe81f87ad --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.json @@ -0,0 +1,571 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml new file mode 100644 index 0000000000..552bc97426 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml @@ -0,0 +1,307 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.json new file mode 100644 index 0000000000..987bdf1aa6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.yml new file mode 100644 index 0000000000..c66001ad31 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DecimalPrecision-Update.yml @@ -0,0 +1,324 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDecimalPrecision', 'bsonType': 'decimal', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.json new file mode 100644 index 0000000000..c2a119cb7f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.json @@ -0,0 +1,381 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "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 applies defaults for trimFactor and sparsity", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$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": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + } + ] + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.yml new file mode 100644 index 0000000000..493bc28bc2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Defaults.yml @@ -0,0 +1,157 @@ +# Test "range" field with defaults for `trimFactor` and `sparsity`. +# Test requires libmongocrypt with changes in MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + topology: [ "replicaset", "sharded", "load-balanced" ] # Exclude "standalone". QE collections are not supported on standalone. +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + # Exclude `trimFactor` and `sparsity` + "contention": { "$numberLong": "0" }, + "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 applies defaults for trimFactor and sparsity" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: *encryptionInformation + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: *encryptionInformation + command_name: find + outcome: + collection: + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + # Expected contents of `__safeContent__` require MONGOCRYPT-698 to apply expected `trimFactor`. + "__safeContent__": [ + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + } + ] + } + - + { + "_id": { + "$numberInt": "1" + }, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.json new file mode 100644 index 0000000000..daa7f4e973 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.json @@ -0,0 +1,1132 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.yml new file mode 100644 index 0000000000..23453decfc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Aggregate.yml @@ -0,0 +1,901 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDoubleNoPrecision": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.json new file mode 100644 index 0000000000..edb336743c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.json @@ -0,0 +1,1160 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.yml new file mode 100644 index 0000000000..54a116e5ce --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Correctness.yml @@ -0,0 +1,292 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.json new file mode 100644 index 0000000000..4a9c1f27b5 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.json @@ -0,0 +1,732 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.yml new file mode 100644 index 0000000000..ff71e8f1d9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Delete.yml @@ -0,0 +1,512 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.json new file mode 100644 index 0000000000..d7860de83e --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.json @@ -0,0 +1,1136 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "DbMkAAADcGF5bG9hZABXJAAABGcAQyQAAAMwAH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzEAfQAAAAVkACAAAAAA2kiWNvEc4zunJ1jzvuClFC9hjZMYruKCqAaxq+oY8EAFcwAgAAAAACofIS72Cm6s866UCk+evTH3CvKBj/uZd72sAL608rzTBWwAIAAAAADuCQ/M2xLeALF0UFZtJb22QGOhHmJv6xoO+kZIHcDeiAADMgB9AAAABWQAIAAAAABkfoBGmU3hjYBvQbjNW19kfXneBQsQQPRfUL3UAwI2cAVzACAAAAAAUpK2BUOqX/DGdX5YJniEZMWkofxHqeAbXceEGJxhp8AFbAAgAAAAAKUaLzIldNIZv6RHE+FwbMjzcNHqPESwF/37mm43VPrsAAMzAH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzQAfQAAAAVkACAAAAAAODI+pB2pCuB+YmNEUAgtMfNdt3DmSkrJ96gRzLphgb8FcwAgAAAAAAT7dewFDxUDECQ3zVq75/cUN4IP+zsqhkP5+czUwlJIBWwAIAAAAACFGeOtd5zBXTJ4JYonkn/HXZfHipUlqGwIRUcH/VTatwADNQB9AAAABWQAIAAAAACNAk+yTZ4Ewk1EnotQK8O3h1gg9I7pr9q2+4po1iJVgAVzACAAAAAAUj/LesmtEsgqYVzMJ67umVA11hJTdDXwbxDoQ71vWyUFbAAgAAAAABlnhpgTQ0WjLb5u0b/vEydrCeFjVynKd7aqb+UnvVLeAAM2AH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcAfQAAAAVkACAAAAAAciRW40ORJLVwchOEpz87Svb+5toAFM6LxDWv928ECwQFcwAgAAAAAN0dipyESIkszfjRzdDi8kAGaa2Hf4wrPAtiWwboZLuxBWwAIAAAAAANr4o/+l1OIbbaX5lZ3fQ/WIeOcEXjNI1F0WbSgQrzaQADOAB9AAAABWQAIAAAAACZqAyCzYQupJ95mrBJX54yIz9VY7I0WrxpNYElCI4dTQVzACAAAAAA/eyJb6d1xfE+jJlVXMTD3HS/NEYENPVKAuj56Dr2dSEFbAAgAAAAANkSt154Or/JKb31VvbZFV46RPgUp8ff/hcPORL7PpFBAAM5AH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzEwAH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzExAH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzEyAH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzEzAH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzE0AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzE1AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzE2AH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzE3AH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzE4AH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzE5AH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzIwAH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzIxAH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzIyAH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzIzAH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzI0AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzI1AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzI2AH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzI3AH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzI4AH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzI5AH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzMwAH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzMxAH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzMyAH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzMzAH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzM0AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzM1AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzM2AH0AAAAFZAAgAAAAAMkN0L1oQWXhjwn9rAdudcYeN8/5VdCKU8cmDt7BokjsBXMAIAAAAAAT62pGXoRwExe9uvgYOI0hg5tOxilrWfoEmT0SMglWJwVsACAAAAAAlVz4dhiprSbUero6JFfxzSJGclg63oAkAmgbSwbcYxIAAzM3AH0AAAAFZAAgAAAAANxfa4xCoaaB7k1C1RoH1LBhsCbN2yEq15BT9b+iqEC4BXMAIAAAAACAX9LV8Pemfw7NF0iB1/85NzM1Ef+1mUfyehacUVgobQVsACAAAAAAVq4xpbymLk0trPC/a2MvB39I7hRiX8EJsVSI5E5hSBkAAzM4AH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzM5AH0AAAAFZAAgAAAAAIy0+bXZi10QC+q7oSOLXK5Fee7VEk/qHSXukfeVIfgzBXMAIAAAAAAQ3IIV/JQCHW95AEbH5zGIHtJqyuPjWPMIZ+VmQHlxEwVsACAAAAAAp0jYsyohKv9Pm+4k+DplEGbl9WLZpAJzitrcDj4CNsMAAzQwAH0AAAAFZAAgAAAAAL5SOJQ3LOhgdXJ5v086NNeAl1qonQnchObdpZJ1kHeEBXMAIAAAAAA+tEqTXODtik+ydJZSnUqXF9f18bPeze9eWtR7ExZJgQVsACAAAAAAbrkZCVgB9Qsp4IAbdf+bD4fT6Boqk5UtuA/zhNrh1y0AAzQxAH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzQyAH0AAAAFZAAgAAAAAFvotcNaoKnVt5CBCOPwjexFO0WGWuaIGL6H/6KSau+6BXMAIAAAAAD2y2mBN5xPu5PJoY2zcr0GnQDtHRBogA5+xzIxccE9fwVsACAAAAAAdS34xzJesnUfxLCcc1U7XzUqLy8MAzV/tcjbqaD3lkMAAzQzAH0AAAAFZAAgAAAAAPezU0/vNT4Q4YKbTbaeHqcwNLT+IjW/Y9QFpIooihjPBXMAIAAAAACj2x4O4rHter8ZnTws5LAP9jJ/6kk9C/V3vL50LoFZHAVsACAAAAAAQdBDF3747uCVP5lB/zr8VmzxJfTSZHBKeIgm5FyONXwAAzQ0AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzQ1AH0AAAAFZAAgAAAAANCeyW+3oebaQk+aqxNVhAcT/BZ5nhsTVdKS3tMrLSvWBXMAIAAAAADxRFMDhkyuEc++WnndMfoUMLNL7T7rWoeblcrpSI6soQVsACAAAAAAdBuBMJ1lxt0DRq9pOZldQqchLs3B/W02txcMLD490FEAAzQ2AH0AAAAFZAAgAAAAAIbo5YBTxXM7HQhl7UP9NNgpPGFkBx871r1B65G47+K8BXMAIAAAAAC21dJSxnEhnxO5gzN5/34BL4von45e1meW92qowzb8fQVsACAAAAAAm3Hk2cvBN0ANaR5jzeZE5TsdxDvJCTOT1I01X7cNVaYAAzQ3AH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzQ4AH0AAAAFZAAgAAAAAJ/D3+17gaQdkBqkL2wMwccdmCaVOtxzIkM8VyI4xI5zBXMAIAAAAAAggLVmkc5u+YzBR+oNE+XgLVp64fC6MzUb/Ilu/Jsw0AVsACAAAAAACz3HVKdWkx82/kGbVpcbAeZtsj2R5Zr0dEPfle4IErkAAzQ5AH0AAAAFZAAgAAAAAJMRyUW50oaTzspS6A3TUoXyC3gNYQoShUGPakMmeVZrBXMAIAAAAACona2Pqwt4U2PmFrtmu37jB9kQ/12okyAVtYa8TQkDiQVsACAAAAAAltJJKjCMyBTJ+4PkdDCPJdeX695P8P5h7WOZ+kmExMAAAzUwAH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzUxAH0AAAAFZAAgAAAAAHEzLtfmF/sBcYPPdj8867VmmQyU1xK9I/3Y0478azvABXMAIAAAAAAcmyFajZPnBTbO+oLInNwlApBocUekKkxz2hYFeSlQ+gVsACAAAAAAZ6IkrOVRcC8vSA6Vb4fPWZJrYexXhEabIuYIeXNsCSgAAzUyAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzUzAH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzU0AH0AAAAFZAAgAAAAACbzcUD3INSnCRspOKF7ubne74OK9L0FTZvi9Ay0JVDYBXMAIAAAAADPebVQH8Btk9rhBIoUOdSAdpPvz7qIY4UC2i6IGisSAQVsACAAAAAAiBunJi0mPnnXdnldiq+If8dcb/n6apHnaIFt+oyYO1kAAzU1AH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzU2AH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzU3AH0AAAAFZAAgAAAAAAL8jhNBG0KXXZhmZ0bPXtfgapJCB/AI+BEHB0eZ3C75BXMAIAAAAADHx/fPa639EBmGV5quLi8IQT600ifiKSOhTDOK19DnzwVsACAAAAAAlyLTDVkHxbayklD6Qymh3odIK1JHaOtps4f4HR+PcDgAAzU4AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzU5AH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzYwAH0AAAAFZAAgAAAAAB89SjLtDJkqEghRGyj6aQ/2qvWLNuMROoXmzbYbCMKMBXMAIAAAAAC8sywgND+CjhVTF6HnRQeay8y9/HnVzDI42dEPah28LQVsACAAAAAAoxv7UKh0RqUAWcOsQvU123zO1qZn73Xfib0qncZCB34AAzYxAH0AAAAFZAAgAAAAABN2alGq9Aats1mwERNGwL/fIwZSvVCe9/8XMHTFlpUpBXMAIAAAAACuDPjJgvvbBYhbLpjMiWUCsVppiYrhvR+yMysNPN8cZAVsACAAAAAAKpADjc4bzIZMi9Q/+oe0EMRJHYQt6dlo1x/lRquagqkAAzYyAH0AAAAFZAAgAAAAAL8YB6VAqGBiWD4CBv16IBscg5J7VQCTZu87n6pj+86KBXMAIAAAAAAmxm8e68geeyAdUjSMWBHzUjneVB0pG9TBXIoE6467hAVsACAAAAAAV76JZAlYpgC/Zl8awx2ArCg1uuyy2XVTSkp0wUMi/7UAAzYzAH0AAAAFZAAgAAAAAL4yLkCTV5Dmxa5toBu4JT8ge/cITAaURIOuFuOtFUkeBXMAIAAAAAAXoFNQOMGkAj7qEJP0wQafmFSXgWGeorDVbwyOxWLIsgVsACAAAAAAc4Un6dtIFe+AQ+RSfNWs3q63RTHhmyc+5GKRRdpWRv8AAzY0AH0AAAAFZAAgAAAAAEU8DoUp46YtYjNFS9kNXwdYxQ9IW27vCTb+VcqqfnKNBXMAIAAAAADe7vBOgYReE8X78k5ARuUnv4GmzPZzg6SbConf4L2G3wVsACAAAAAA78YHWVkp6HbZ0zS4UL2z/2pj9vPDcMDt7zTv6NcRsVsAAzY1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzY2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzY3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzY4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzY5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzcwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzcxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAABbW4A////////7/8BbXgA////////738A", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.yml new file mode 100644 index 0000000000..48f018e468 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-FindOneAndUpdate.yml @@ -0,0 +1,899 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDoubleNoPrecision": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDoubleNoPrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.json new file mode 100644 index 0000000000..934af381f1 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.json @@ -0,0 +1,1123 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.yml new file mode 100644 index 0000000000..bc80133cc6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-InsertFind.yml @@ -0,0 +1,895 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.json new file mode 100644 index 0000000000..ec95e0334a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.json @@ -0,0 +1,1140 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.yml new file mode 100644 index 0000000000..4b4d9e4da6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Double-Update.yml @@ -0,0 +1,912 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoubleNoPrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "DbMkAAADcGF5bG9hZABXJAAABGcAQyQAAAMwAH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzEAfQAAAAVkACAAAAAA2kiWNvEc4zunJ1jzvuClFC9hjZMYruKCqAaxq+oY8EAFcwAgAAAAACofIS72Cm6s866UCk+evTH3CvKBj/uZd72sAL608rzTBWwAIAAAAADuCQ/M2xLeALF0UFZtJb22QGOhHmJv6xoO+kZIHcDeiAADMgB9AAAABWQAIAAAAABkfoBGmU3hjYBvQbjNW19kfXneBQsQQPRfUL3UAwI2cAVzACAAAAAAUpK2BUOqX/DGdX5YJniEZMWkofxHqeAbXceEGJxhp8AFbAAgAAAAAKUaLzIldNIZv6RHE+FwbMjzcNHqPESwF/37mm43VPrsAAMzAH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzQAfQAAAAVkACAAAAAAODI+pB2pCuB+YmNEUAgtMfNdt3DmSkrJ96gRzLphgb8FcwAgAAAAAAT7dewFDxUDECQ3zVq75/cUN4IP+zsqhkP5+czUwlJIBWwAIAAAAACFGeOtd5zBXTJ4JYonkn/HXZfHipUlqGwIRUcH/VTatwADNQB9AAAABWQAIAAAAACNAk+yTZ4Ewk1EnotQK8O3h1gg9I7pr9q2+4po1iJVgAVzACAAAAAAUj/LesmtEsgqYVzMJ67umVA11hJTdDXwbxDoQ71vWyUFbAAgAAAAABlnhpgTQ0WjLb5u0b/vEydrCeFjVynKd7aqb+UnvVLeAAM2AH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcAfQAAAAVkACAAAAAAciRW40ORJLVwchOEpz87Svb+5toAFM6LxDWv928ECwQFcwAgAAAAAN0dipyESIkszfjRzdDi8kAGaa2Hf4wrPAtiWwboZLuxBWwAIAAAAAANr4o/+l1OIbbaX5lZ3fQ/WIeOcEXjNI1F0WbSgQrzaQADOAB9AAAABWQAIAAAAACZqAyCzYQupJ95mrBJX54yIz9VY7I0WrxpNYElCI4dTQVzACAAAAAA/eyJb6d1xfE+jJlVXMTD3HS/NEYENPVKAuj56Dr2dSEFbAAgAAAAANkSt154Or/JKb31VvbZFV46RPgUp8ff/hcPORL7PpFBAAM5AH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzEwAH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzExAH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzEyAH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzEzAH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzE0AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzE1AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzE2AH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzE3AH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzE4AH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzE5AH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzIwAH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzIxAH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzIyAH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzIzAH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzI0AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzI1AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzI2AH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzI3AH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzI4AH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzI5AH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzMwAH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzMxAH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzMyAH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzMzAH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzM0AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzM1AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzM2AH0AAAAFZAAgAAAAAMkN0L1oQWXhjwn9rAdudcYeN8/5VdCKU8cmDt7BokjsBXMAIAAAAAAT62pGXoRwExe9uvgYOI0hg5tOxilrWfoEmT0SMglWJwVsACAAAAAAlVz4dhiprSbUero6JFfxzSJGclg63oAkAmgbSwbcYxIAAzM3AH0AAAAFZAAgAAAAANxfa4xCoaaB7k1C1RoH1LBhsCbN2yEq15BT9b+iqEC4BXMAIAAAAACAX9LV8Pemfw7NF0iB1/85NzM1Ef+1mUfyehacUVgobQVsACAAAAAAVq4xpbymLk0trPC/a2MvB39I7hRiX8EJsVSI5E5hSBkAAzM4AH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzM5AH0AAAAFZAAgAAAAAIy0+bXZi10QC+q7oSOLXK5Fee7VEk/qHSXukfeVIfgzBXMAIAAAAAAQ3IIV/JQCHW95AEbH5zGIHtJqyuPjWPMIZ+VmQHlxEwVsACAAAAAAp0jYsyohKv9Pm+4k+DplEGbl9WLZpAJzitrcDj4CNsMAAzQwAH0AAAAFZAAgAAAAAL5SOJQ3LOhgdXJ5v086NNeAl1qonQnchObdpZJ1kHeEBXMAIAAAAAA+tEqTXODtik+ydJZSnUqXF9f18bPeze9eWtR7ExZJgQVsACAAAAAAbrkZCVgB9Qsp4IAbdf+bD4fT6Boqk5UtuA/zhNrh1y0AAzQxAH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzQyAH0AAAAFZAAgAAAAAFvotcNaoKnVt5CBCOPwjexFO0WGWuaIGL6H/6KSau+6BXMAIAAAAAD2y2mBN5xPu5PJoY2zcr0GnQDtHRBogA5+xzIxccE9fwVsACAAAAAAdS34xzJesnUfxLCcc1U7XzUqLy8MAzV/tcjbqaD3lkMAAzQzAH0AAAAFZAAgAAAAAPezU0/vNT4Q4YKbTbaeHqcwNLT+IjW/Y9QFpIooihjPBXMAIAAAAACj2x4O4rHter8ZnTws5LAP9jJ/6kk9C/V3vL50LoFZHAVsACAAAAAAQdBDF3747uCVP5lB/zr8VmzxJfTSZHBKeIgm5FyONXwAAzQ0AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzQ1AH0AAAAFZAAgAAAAANCeyW+3oebaQk+aqxNVhAcT/BZ5nhsTVdKS3tMrLSvWBXMAIAAAAADxRFMDhkyuEc++WnndMfoUMLNL7T7rWoeblcrpSI6soQVsACAAAAAAdBuBMJ1lxt0DRq9pOZldQqchLs3B/W02txcMLD490FEAAzQ2AH0AAAAFZAAgAAAAAIbo5YBTxXM7HQhl7UP9NNgpPGFkBx871r1B65G47+K8BXMAIAAAAAC21dJSxnEhnxO5gzN5/34BL4von45e1meW92qowzb8fQVsACAAAAAAm3Hk2cvBN0ANaR5jzeZE5TsdxDvJCTOT1I01X7cNVaYAAzQ3AH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzQ4AH0AAAAFZAAgAAAAAJ/D3+17gaQdkBqkL2wMwccdmCaVOtxzIkM8VyI4xI5zBXMAIAAAAAAggLVmkc5u+YzBR+oNE+XgLVp64fC6MzUb/Ilu/Jsw0AVsACAAAAAACz3HVKdWkx82/kGbVpcbAeZtsj2R5Zr0dEPfle4IErkAAzQ5AH0AAAAFZAAgAAAAAJMRyUW50oaTzspS6A3TUoXyC3gNYQoShUGPakMmeVZrBXMAIAAAAACona2Pqwt4U2PmFrtmu37jB9kQ/12okyAVtYa8TQkDiQVsACAAAAAAltJJKjCMyBTJ+4PkdDCPJdeX695P8P5h7WOZ+kmExMAAAzUwAH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzUxAH0AAAAFZAAgAAAAAHEzLtfmF/sBcYPPdj8867VmmQyU1xK9I/3Y0478azvABXMAIAAAAAAcmyFajZPnBTbO+oLInNwlApBocUekKkxz2hYFeSlQ+gVsACAAAAAAZ6IkrOVRcC8vSA6Vb4fPWZJrYexXhEabIuYIeXNsCSgAAzUyAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzUzAH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzU0AH0AAAAFZAAgAAAAACbzcUD3INSnCRspOKF7ubne74OK9L0FTZvi9Ay0JVDYBXMAIAAAAADPebVQH8Btk9rhBIoUOdSAdpPvz7qIY4UC2i6IGisSAQVsACAAAAAAiBunJi0mPnnXdnldiq+If8dcb/n6apHnaIFt+oyYO1kAAzU1AH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzU2AH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzU3AH0AAAAFZAAgAAAAAAL8jhNBG0KXXZhmZ0bPXtfgapJCB/AI+BEHB0eZ3C75BXMAIAAAAADHx/fPa639EBmGV5quLi8IQT600ifiKSOhTDOK19DnzwVsACAAAAAAlyLTDVkHxbayklD6Qymh3odIK1JHaOtps4f4HR+PcDgAAzU4AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzU5AH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzYwAH0AAAAFZAAgAAAAAB89SjLtDJkqEghRGyj6aQ/2qvWLNuMROoXmzbYbCMKMBXMAIAAAAAC8sywgND+CjhVTF6HnRQeay8y9/HnVzDI42dEPah28LQVsACAAAAAAoxv7UKh0RqUAWcOsQvU123zO1qZn73Xfib0qncZCB34AAzYxAH0AAAAFZAAgAAAAABN2alGq9Aats1mwERNGwL/fIwZSvVCe9/8XMHTFlpUpBXMAIAAAAACuDPjJgvvbBYhbLpjMiWUCsVppiYrhvR+yMysNPN8cZAVsACAAAAAAKpADjc4bzIZMi9Q/+oe0EMRJHYQt6dlo1x/lRquagqkAAzYyAH0AAAAFZAAgAAAAAL8YB6VAqGBiWD4CBv16IBscg5J7VQCTZu87n6pj+86KBXMAIAAAAAAmxm8e68geeyAdUjSMWBHzUjneVB0pG9TBXIoE6467hAVsACAAAAAAV76JZAlYpgC/Zl8awx2ArCg1uuyy2XVTSkp0wUMi/7UAAzYzAH0AAAAFZAAgAAAAAL4yLkCTV5Dmxa5toBu4JT8ge/cITAaURIOuFuOtFUkeBXMAIAAAAAAXoFNQOMGkAj7qEJP0wQafmFSXgWGeorDVbwyOxWLIsgVsACAAAAAAc4Un6dtIFe+AQ+RSfNWs3q63RTHhmyc+5GKRRdpWRv8AAzY0AH0AAAAFZAAgAAAAAEU8DoUp46YtYjNFS9kNXwdYxQ9IW27vCTb+VcqqfnKNBXMAIAAAAADe7vBOgYReE8X78k5ARuUnv4GmzPZzg6SbConf4L2G3wVsACAAAAAA78YHWVkp6HbZ0zS4UL2z/2pj9vPDcMDt7zTv6NcRsVsAAzY1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzY2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzY3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzY4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzY5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzcwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzcxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAABbW4A////////7/8BbXgA////////738A", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoubleNoPrecision": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.json new file mode 100644 index 0000000000..e8a50ebeca --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.json @@ -0,0 +1,580 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DQYKAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYAAQAAAAFtbgAAAAAAAAAAAAFteAAAAAAAAABpQAA=", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.yml new file mode 100644 index 0000000000..87475991a4 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Aggregate.yml @@ -0,0 +1,313 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedDoublePrecision": { $gt: {$numberDouble: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.json new file mode 100644 index 0000000000..87d0e3dd8c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.json @@ -0,0 +1,1650 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.yml new file mode 100644 index 0000000000..9c7a8d22f2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Correctness.yml @@ -0,0 +1,424 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0.0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1.0" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "0.0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "1.0" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } + result: [*doc0] + + - 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: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDoublePrecision: { $numberDouble: "200.0" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "0.0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "1.0" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: {$numberDouble: "0.0"}, $lte: {$numberDouble: "200.0"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $in: [ {$numberDouble: "0.0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.json new file mode 100644 index 0000000000..8a0fecf786 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.json @@ -0,0 +1,474 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DQYKAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYAAQAAAAFtbgAAAAAAAAAAAAFteAAAAAAAAABpQAA=", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.yml new file mode 100644 index 0000000000..09952c2f37 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Delete.yml @@ -0,0 +1,218 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json new file mode 100644 index 0000000000..ac77931d61 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json @@ -0,0 +1,584 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml new file mode 100644 index 0000000000..e276b54e46 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml @@ -0,0 +1,311 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedDoublePrecision: { $gt: {$numberDouble: "0"}} } + update: { "$set": { "encryptedDoublePrecision": {$numberDouble: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedDoublePrecision": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.json new file mode 100644 index 0000000000..5dcc09dca9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.json @@ -0,0 +1,571 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.yml new file mode 100644 index 0000000000..c309d6ecef --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-InsertFind.yml @@ -0,0 +1,307 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.json new file mode 100644 index 0000000000..483e3d52e6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.json @@ -0,0 +1,588 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.yml new file mode 100644 index 0000000000..6797239b84 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-DoublePrecision-Update.yml @@ -0,0 +1,326 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedDoublePrecision', 'bsonType': 'double', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.json new file mode 100644 index 0000000000..6cd837c789 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.json @@ -0,0 +1,484 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.yml new file mode 100644 index 0000000000..f677bdedf9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Aggregate.yml @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedInt": { $gt: {$numberInt: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.json new file mode 100644 index 0000000000..9dc4e4e501 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.json @@ -0,0 +1,1644 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.yml new file mode 100644 index 0000000000..9cb1cb368f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Correctness.yml @@ -0,0 +1,423 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberInt: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $lte: { $numberInt: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } + result: [*doc0] + + - 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: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedInt: { $numberInt: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: { $numberInt: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lte: { $numberInt: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: {$numberInt: "0"}, $lte: {$numberInt: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $in: [ {$numberInt: "0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.json new file mode 100644 index 0000000000..b251db9157 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.json @@ -0,0 +1,420 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.yml new file mode 100644 index 0000000000..158358d884 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Delete.yml @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.json new file mode 100644 index 0000000000..6e09b5ea2c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.json @@ -0,0 +1,488 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.yml new file mode 100644 index 0000000000..6b7fe8f026 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-FindOneAndUpdate.yml @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedInt: { $gt: {$numberInt: "0"}} } + update: { "$set": { "encryptedInt": {$numberInt: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedInt": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.json new file mode 100644 index 0000000000..cbab7e7699 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.json @@ -0,0 +1,475 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.yml new file mode 100644 index 0000000000..8e87d38de5 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-InsertFind.yml @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.json new file mode 100644 index 0000000000..cb6b223943 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.json @@ -0,0 +1,492 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.yml new file mode 100644 index 0000000000..d5530d09cc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Int-Update.yml @@ -0,0 +1,242 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.json new file mode 100644 index 0000000000..5c4bf10101 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.json @@ -0,0 +1,484 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "aggregate" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.yml new file mode 100644 index 0000000000..04b315bad1 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Aggregate.yml @@ -0,0 +1,229 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: aggregate + arguments: + pipeline: [{ $match: { "encryptedLong": { $gt: {$numberLong: "0" }} } }] + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + aggregate: *collection_name + pipeline: [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + } + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: aggregate + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.json new file mode 100644 index 0000000000..d81e0933f8 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.json @@ -0,0 +1,1644 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.yml new file mode 100644 index 0000000000..97b7db2b71 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Correctness.yml @@ -0,0 +1,422 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } }} + result: [*doc1] + + - description: "Find with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberLong: "0" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "1" } }} + result: [*doc0] + + - description: "Find with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $lte: { $numberLong: "1" } }} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} + result: [*doc1] + + - description: "Find with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "0" } } + result: [*doc0] + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "1" } } + result: [*doc1] + + - description: "Find with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc1] + + - description: "Find with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: find + arguments: + filter: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } + result: [*doc0] + + - 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: *doc0 + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedLong: { $numberLong: "200" }} + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + result: [*doc0, *doc200] + + - description: "Aggregate with $gte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: { $numberLong: "0" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "1" } }} } + result: [*doc0] + + - description: "Aggregate with $lte" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lte: { $numberLong: "1" } }} } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - 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: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2"} }} } + result: [*doc1] + + - description: "Aggregate with equality" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "0" } } } + result: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "1" } } } + result: [*doc1] + + - description: "Aggregate with full range" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: {$numberLong: "0"}, $lte: {$numberLong: "200"} } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 }} + result: [*doc0, *doc1] + + - description: "Aggregate with $in" + clientOptions: + autoEncryptOpts: + kmsProviders: + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: { document: *doc0 } + - name: insertOne + arguments: { document: *doc1 } + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $in: [ {$numberLong: "0"} ] } } } + result: [*doc0] + + - 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: + # Expect an error from mongocryptd. + 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: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.json new file mode 100644 index 0000000000..faf0c401b7 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.json @@ -0,0 +1,420 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "delete" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.yml new file mode 100644 index 0000000000..9bbbf71ae2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Delete.yml @@ -0,0 +1,176 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + delete: *collection_name + deletes: [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: delete + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.json new file mode 100644 index 0000000000..b233b40b54 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.json @@ -0,0 +1,488 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "findAndModify": "default", + "query": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "findAndModify" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.yml new file mode 100644 index 0000000000..8f74e442f1 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-FindOneAndUpdate.yml @@ -0,0 +1,227 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: findOneAndUpdate + arguments: + filter: { encryptedLong: { $gt: {$numberLong: "0"}} } + update: { "$set": { "encryptedLong": {$numberLong: "2"}}} + returnDocument: Before + result: *doc1 + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + findAndModify: *collection_name + query: { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + } + update: { "$set": {"encryptedLong": { $$type: "binData" }} } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: findAndModify + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.json new file mode 100644 index 0000000000..1b787d4cb6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.json @@ -0,0 +1,475 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.yml new file mode 100644 index 0000000000..20cefceecc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-InsertFind.yml @@ -0,0 +1,223 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + result: [*doc1] + expectations: + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.json new file mode 100644 index 0000000000..07182bb5e2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.json @@ -0,0 +1,492 @@ +{ + "runOn": [ + { + "minServerVersion": "8.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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "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": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + "$db": "default" + } + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.yml new file mode 100644 index 0000000000..758c1bddc3 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-Long-Update.yml @@ -0,0 +1,242 @@ + +# Requires libmongocrypt including MONGOCRYPT-702. +runOn: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedLong', 'bsonType': 'long', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberInt': '1'}, '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: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + - name: insertOne + arguments: + document: &doc1 { _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: *collection_name + 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: *collection_name + documents: + - &doc0_encrypted { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + command_name: insert + - command_started_event: + command_name: update + command: + + "update": "default" + "ordered": true + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { $$type: "binData" } + } + } + } + ] + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - + { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.json b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.json new file mode 100644 index 0000000000..6215604508 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.json @@ -0,0 +1,163 @@ +{ + "runOn": [ + { + "minServerVersion": "8.0.0", + "topology": [ + "replicaset", + "sharded", + "load-balanced" + ], + "maxServerVersion": "8.99.99" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "encrypted_fields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberLong": "1" + }, + "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/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.yml b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.yml new file mode 100644 index 0000000000..432f86b423 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Rangev2-WrongType.yml @@ -0,0 +1,44 @@ +# Test correctness results. +# Does not include command monitoring expectations or outcome assertions to make tests more readable. + +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] + maxServerVersion: "8.99.99" +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &encrypted_fields { 'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'range', 'contention': {'$numberLong': '0'}, 'trimFactor': {'$numberLong': '1'}, '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: + # Expect an error from mongocryptd. + 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 so results from range queries are ordered. + sort: { _id: 1 } + result: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/legacy/fle2v2-Update.json b/source/client-side-encryption/tests/legacy/fle2v2-Update.json new file mode 100644 index 0000000000..cb260edc0d --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-Update.yml b/source/client-side-encryption/tests/legacy/fle2v2-Update.yml new file mode 100644 index 0000000000..3bed027181 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-Update.yml @@ -0,0 +1,210 @@ +# Requires libmongocrypt 1.8.0. +runOn: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] +database_name: &database_name "default" +collection_name: &collection_name "default" +data: [] +encrypted_fields: &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: *collection_name + 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: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + update: *collection_name + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + 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: *collection_name + 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: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: insert + - command_started_event: + command: + update: *collection_name + 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": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + + command_name: update + - command_started_event: + command: + find: *collection_name + filter: { "_id": { "$eq": 1 }} + command_name: find + outcome: + collection: + data: + - { "_id": 1, "encryptedIndexed": { "$$type": "binData" }, "__safeContent__": [{ "$binary" : { "base64" : "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", "subType" : "00" } }] } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/fle2v2-validatorAndPartialFieldExpression.json b/source/client-side-encryption/tests/legacy/fle2v2-validatorAndPartialFieldExpression.json new file mode 100644 index 0000000000..901c4dd841 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/fle2v2-validatorAndPartialFieldExpression.yml b/source/client-side-encryption/tests/legacy/fle2v2-validatorAndPartialFieldExpression.yml new file mode 100644 index 0000000000..3eb90ea935 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/fle2v2-validatorAndPartialFieldExpression.yml @@ -0,0 +1,169 @@ +# Requires libmongocrypt 1.8.0. +runOn: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topology: [ "replicaset", "sharded", "load-balanced" ] + +database_name: &database_name "default" +collection_name: &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: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "encryptedCollection" + - name: createCollection + object: database + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + 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: + # Drop to remove a collection that may exist from previous test runs. + - 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" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/gcpKMS.json b/source/client-side-encryption/tests/legacy/gcpKMS.json new file mode 100644 index 0000000000..65f12ec139 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/gcpKMS.json @@ -0,0 +1,237 @@ +{ + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "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/source/client-side-encryption/tests/legacy/gcpKMS.yml b/source/client-side-encryption/tests/legacy/gcpKMS.yml new file mode 100644 index 0000000000..1bf2121b64 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/gcpKMS.yml @@ -0,0 +1,46 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], '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: &doc0 { _id: 1, encrypted_string_gcp: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - 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: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_gcp: {'$binary': {'base64': 'ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/getMore.json b/source/client-side-encryption/tests/legacy/getMore.json new file mode 100644 index 0000000000..94e788ef61 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/getMore.json @@ -0,0 +1,266 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + } + } + ], + "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": "getMore with encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "batchSize": 2, + "filter": {} + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + }, + { + "_id": 3, + "encrypted_string": "string2" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "batchSize": 2 + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "default", + "batchSize": 2 + }, + "command_name": "getMore" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/getMore.yml b/source/client-side-encryption/tests/legacy/getMore.yml new file mode 100644 index 0000000000..dd246e9fa1 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/getMore.yml @@ -0,0 +1,61 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } + - &doc2_encrypted { _id: 3, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==', 'subType': '06'}} } +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: "getMore with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + batchSize: 2 + filter: {} + result: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string1" } + - { _id: 3, encrypted_string: "string2" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + find: *collection_name + batchSize: 2 + command_name: find + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 2 + command_name: getMore + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/insert.json b/source/client-side-encryption/tests/legacy/insert.json new file mode 100644 index 0000000000..cf2910fd7a --- /dev/null +++ b/source/client-side-encryption/tests/legacy/insert.json @@ -0,0 +1,344 @@ +{ + "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": "insertOne with encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + } + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + }, + { + "description": "insertMany with encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + } + } + ], + "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", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/insert.yml b/source/client-side-encryption/tests/legacy/insert.yml new file mode 100644 index 0000000000..99521f0350 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/insert.yml @@ -0,0 +1,88 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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: "insertOne with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}}, random: { $$type: "binData" } } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "insertMany with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertMany + arguments: + documents: + - *doc0 + - &doc1 { _id: 2, encrypted_string: "string1" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0_encrypted + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/keyAltName.json b/source/client-side-encryption/tests/legacy/keyAltName.json new file mode 100644 index 0000000000..7f71b9dbeb --- /dev/null +++ b/source/client-side-encryption/tests/legacy/keyAltName.json @@ -0,0 +1,228 @@ +{ + "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 with encryption using key alt name", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_w_altname": "string0", + "altname": "altname" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [] + } + }, + { + "keyAltNames": { + "$in": [ + "altname" + ] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_w_altname": { + "$$type": "binData" + }, + "altname": "altname" + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_w_altname": { + "$$type": "binData" + }, + "altname": "altname" + } + ] + } + } + }, + { + "description": "Replace with key alt name fails", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_w_altname": "string0" + } + }, + "upsert": true + }, + "result": { + "errorContains": "A non-static (JSONPointer) keyId is not supported" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/keyAltName.yml b/source/client-side-encryption/tests/legacy/keyAltName.yml new file mode 100644 index 0000000000..a7660ba38c --- /dev/null +++ b/source/client-side-encryption/tests/legacy/keyAltName.yml @@ -0,0 +1,64 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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 with encryption using key alt name" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_w_altname: "string0", altname: "altname" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {$or: [ { _id: { $in: [] } }, { keyAltNames: { $in: [ "altname" ] } } ] } + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_w_altname: { $$type: "binData" }, altname: "altname" } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "Replace with key alt name fails" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: {} + update: { $set: { encrypted_w_altname: "string0" } } + upsert: true + result: + errorContains: "A non-static (JSONPointer) keyId is not supported" + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/keyCache.json b/source/client-side-encryption/tests/legacy/keyCache.json new file mode 100644 index 0000000000..912ce80020 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/keyCache.json @@ -0,0 +1,270 @@ +{ + "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 with deterministic encryption, then find it", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "keyExpirationMS": 1 + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 50 + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_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", + "readConcern": { + "level": "majority" + } + }, + "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" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/keyCache.yml b/source/client-side-encryption/tests/legacy/keyCache.yml new file mode 100644 index 0000000000..3af117ca81 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/keyCache.yml @@ -0,0 +1,69 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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 with deterministic encryption, then find it" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + keyExpirationMS: 1 + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: wait + object: testRunner + arguments: + ms: 50 # Wait long enough to account for coarse time resolution on Windows (CDRIVER-4526). + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + # The cache has expired and the key must be fetched again + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/kmipKMS.json b/source/client-side-encryption/tests/legacy/kmipKMS.json new file mode 100644 index 0000000000..349328b433 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/kmipKMS.json @@ -0,0 +1,362 @@ +{ + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "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" + ] + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + }, + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": "11" + }, + "keyAltNames": [ + "delegated" + ] + } + ], + "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" + } + } + } + ] + } + } + }, + { + "description": "Insert a document with auto encryption using KMIP delegated KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "kmip": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip_delegated": "string0" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/kmipKMS.yml b/source/client-side-encryption/tests/legacy/kmipKMS.yml new file mode 100644 index 0000000000..51fa42cc73 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/kmipKMS.yml @@ -0,0 +1,83 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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'}}, 'encrypted_string_kmip_delegated': {'encrypt': {'keyId': [{'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}], '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']},{'_id': {'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'}, 'keyMaterial': {'$binary': {'base64': '5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1634220190041'}}, 'updateDate': {'$date': {'$numberLong': '1634220190041'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'kmip', 'delegated': True, 'keyId': '11'}, 'keyAltNames': ['delegated']}] + +tests: + - description: "Insert a document with auto encryption using KMIP KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string_kmip: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - 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: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string_kmip: {'$binary': {'base64': 'AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + + - description: "Insert a document with auto encryption using KMIP delegated KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + kmip: {} + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_string_kmip_delegated: "string0" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: { $or: [ { _id: { $in: [ {'$uuid': '7411e9af-c688-4df7-8143-5e60ae96cba6'} ] } }, { keyAltNames: { $in: [] } } ] } + $db: keyvault + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { _id: 1, encrypted_string_kmip_delegated: {'$binary': {'base64': 'AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==', 'subType': '06'}} } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/localKMS.json b/source/client-side-encryption/tests/legacy/localKMS.json new file mode 100644 index 0000000000..67c4ba1308 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/localKMS.json @@ -0,0 +1,191 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "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" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using local KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {}, + "local": { + "key": { + "$binary": { + "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + } + } + ], + "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": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/localKMS.yml b/source/client-side-encryption/tests/legacy/localKMS.yml new file mode 100644 index 0000000000..4c0d962f32 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/localKMS.yml @@ -0,0 +1,47 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'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'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1552949630483'}}, 'updateDate': {'$date': {'$numberLong': '1552949630483'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local'}}] + +tests: + - description: "Insert a document with auto encryption using local KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + local: {'key': {'$binary': {'base64': 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - 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: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==', 'subType': '06'}}, random: { $$type: "binData" } } + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/localSchema.json b/source/client-side-encryption/tests/legacy/localSchema.json new file mode 100644 index 0000000000..4698520f6f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/localSchema.json @@ -0,0 +1,258 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": {}, + "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": "A local schema should override", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "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" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "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" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "A local schema with no encryption is an error", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "test": { + "bsonType": "string" + } + }, + "bsonType": "object", + "required": [ + "test" + ] + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "JSON schema keyword 'required' is only allowed with a remote schema" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/localSchema.yml b/source/client-side-encryption/tests/legacy/localSchema.yml new file mode 100644 index 0000000000..89b4bd51d0 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/localSchema.yml @@ -0,0 +1,65 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +# configure an empty schema +json_schema: {} +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: "A local schema should override" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'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'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "A local schema with no encryption is an error" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'test': {'bsonType': 'string'}}, 'bsonType': 'object', 'required': ['test']} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string: "string0" } + result: + errorContains: "JSON schema keyword 'required' is only allowed with a remote schema" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/malformedCiphertext.json b/source/client-side-encryption/tests/legacy/malformedCiphertext.json new file mode 100644 index 0000000000..c81330ce83 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/malformedCiphertext.json @@ -0,0 +1,321 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "00" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQ==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAa2V2aW4gYWxiZXJ0c29uCg==", + "subType": "06" + } + } + } + ], + "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": "Wrong subtype", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "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" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "Empty data", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "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" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 2 + } + }, + "result": { + "errorContains": "malformed ciphertext" + } + } + ] + }, + { + "description": "Malformed data", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "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" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 3 + } + }, + "result": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/malformedCiphertext.yml b/source/client-side-encryption/tests/legacy/malformedCiphertext.yml new file mode 100644 index 0000000000..e27c1dfdc2 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/malformedCiphertext.yml @@ -0,0 +1,69 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0 + _id: 1 + encrypted_string: + $binary: + base64: AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg== + subType: "00" + - _id: 2 + encrypted_string: + $binary: + base64: "AQ==" + subType: "06" + - _id: 3 + encrypted_string: + $binary: + base64: "AQAAa2V2aW4gYWxiZXJ0c29uCg==" + subType: "06" + +# Since test requires invalid data to be inserted, use a local schema. +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: "Wrong subtype" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'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'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 1 } + result: + # gets returned without decryption + - *doc0 + - description: "Empty data" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'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'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 2 } + result: + errorContains: "malformed ciphertext" + - description: "Malformed data" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'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'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: find + arguments: + filter: { _id: 3 } + result: + # ciphertext can only validate subtype (which is correct) + # but takes the 16 byte UUID to look up key. Fails to find. + errorContains: "not all keys requested were satisfied" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/maxWireVersion.json b/source/client-side-encryption/tests/legacy/maxWireVersion.json new file mode 100644 index 0000000000..f04f58dffd --- /dev/null +++ b/source/client-side-encryption/tests/legacy/maxWireVersion.json @@ -0,0 +1,74 @@ +{ + "runOn": [ + { + "maxServerVersion": "4.0.99" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "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": "operation fails with maxWireVersion < 8", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + }, + "extraOptions": { + "mongocryptdBypassSpawn": true + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "encrypted_string": "string0" + } + }, + "result": { + "errorContains": "Auto-encryption requires a minimum MongoDB version of 4.2" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/maxWireVersion.yml b/source/client-side-encryption/tests/legacy/maxWireVersion.yml new file mode 100644 index 0000000000..87c4c993f9 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/maxWireVersion.yml @@ -0,0 +1,22 @@ +runOn: + - maxServerVersion: "4.0.99" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +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: "operation fails with maxWireVersion < 8" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + extraOptions: + mongocryptdBypassSpawn: true # mongocryptd probably won't be on the path + operations: + - name: insertOne + arguments: + document: { encrypted_string: "string0" } + result: + errorContains: "Auto-encryption requires a minimum MongoDB version of 4.2" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/missingKey.json b/source/client-side-encryption/tests/legacy/missingKey.json new file mode 100644 index 0000000000..275147bb72 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/missingKey.json @@ -0,0 +1,179 @@ +{ + "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 with encryption on a missing key", + "clientOptions": { + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.different", + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "result": { + "errorContains": "not all keys requested were satisfied" + } + } + ], + "outcome": { + "collection": { + "data": [] + } + }, + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "different", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/missingKey.yml b/source/client-side-encryption/tests/legacy/missingKey.yml new file mode 100644 index 0000000000..2be5395108 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/missingKey.yml @@ -0,0 +1,42 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &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 with encryption on a missing key" + clientOptions: + autoEncryptOpts: + keyVaultNamespace: "keyvault.different" + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + result: + errorContains: "not all keys requested were satisfied" + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: [] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: different + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/namedKMS.json b/source/client-side-encryption/tests/legacy/namedKMS.json new file mode 100644 index 0000000000..c859443585 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/namedKMS.json @@ -0,0 +1,197 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ], + "tests": [ + { + "description": "Automatically encrypt and decrypt with a named KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local:name2": { + "key": { + "$binary": { + "base64": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_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": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/namedKMS.yml b/source/client-side-encryption/tests/legacy/namedKMS.yml new file mode 100644 index 0000000000..7bd456abaf --- /dev/null +++ b/source/client-side-encryption/tests/legacy/namedKMS.yml @@ -0,0 +1,56 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1552949630483'}}, 'updateDate': {'$date': {'$numberLong': '1552949630483'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local:name2'}}] + +tests: + - description: "Automatically encrypt and decrypt with a named KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + "local:name2": {'key': {'$binary': {'base64': 'local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/noSchema.json b/source/client-side-encryption/tests/legacy/noSchema.json new file mode 100644 index 0000000000..095434f886 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/noSchema.yml b/source/client-side-encryption/tests/legacy/noSchema.yml new file mode 100644 index 0000000000..5c6811688f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/noSchema.yml @@ -0,0 +1,37 @@ +# Test auto encryption on a collection with no jsonSchema configured. +# This is a regression test for MONGOCRYPT-378/PYTHON-3188. +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "unencrypted" + +tests: + - description: "Insert on an unencrypted collection" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1 } + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + command_name: insert + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0 \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/replaceOne.json b/source/client-side-encryption/tests/legacy/replaceOne.json new file mode 100644 index 0000000000..9757686819 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/replaceOne.json @@ -0,0 +1,239 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "replaceOne with encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "replaceOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "replacement": { + "encrypted_string": "string1", + "random": "abc" + } + }, + "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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + } + ], + "ordered": true + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/replaceOne.yml b/source/client-side-encryption/tests/legacy/replaceOne.yml new file mode 100644 index 0000000000..e3b50a828b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/replaceOne.yml @@ -0,0 +1,54 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "replaceOne with encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: replaceOne + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1", random: "abc" } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + u: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}}, random: { $$type: "binData" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}}, random: { $$type: "binData" } } \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/timeoutMS.json b/source/client-side-encryption/tests/legacy/timeoutMS.json new file mode 100644 index 0000000000..b667767cfc --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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": 2 + }, + "data": { + "failCommands": [ + "listCollections", + "find" + ], + "blockConnection": true, + "blockTimeMS": 30 + } + }, + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + }, + "timeoutMS": 50 + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "result": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/timeoutMS.yml b/source/client-side-encryption/tests/legacy/timeoutMS.yml new file mode 100644 index 0000000000..bb71d67650 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/timeoutMS.yml @@ -0,0 +1,67 @@ +runOn: + - minServerVersion: "4.4" +database_name: &database_name "cse-timeouts-db" +collection_name: &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: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + result: + isTimeoutError: true + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + maxTimeMS: { $$type: ["int", "long"] } + command_name: listCollections + + # Test that timeoutMS applies to the sum of all operations done for client-side encryption. This is done by blocking + # listCollections and find for 30ms each and running an insertOne with timeoutMS=50. There should be one + # listCollections command and one "find" command, so the sum should take more than timeoutMS. A second listCollections + # event doesn't occur due to the internal MongoClient lacking configured auto encryption, plus libmongocrypt holds the + # collection schema in cache for a minute. + # + # This test does not include command monitoring expectations because the exact command sequence is dependent on the + # amount of time taken by mongocryptd communication. In slow runs, mongocryptd communication can breach the timeout + # and result in the final "find" not being sent. + - description: "remaining timeoutMS applied to find to get keyvault data" + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections", "find"] + blockConnection: true + blockTimeMS: 30 + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + timeoutMS: 50 + operations: + - name: insertOne + arguments: + document: *doc0 + result: + isTimeoutError: true \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/types.json b/source/client-side-encryption/tests/legacy/types.json new file mode 100644 index 0000000000..a6c6507e90 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/types.json @@ -0,0 +1,1646 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": {}, + "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": "type=objectId", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_objectId": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "objectId", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_objectId": { + "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_objectId": { + "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_objectId": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_objectId": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=symbol", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_symbol": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "symbol", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_symbol": { + "$symbol": "test" + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_symbol": { + "$symbol": "test" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_symbol": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_symbol": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=int", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_int": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "int", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_int": { + "$numberInt": "123" + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_int": { + "$numberInt": "123" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_int": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_int": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=double", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_double": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "double", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_double": { + "$numberDouble": "1.23" + } + } + }, + "result": { + "errorContains": "element of type: double" + } + } + ] + }, + { + "description": "type=decimal", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_decimal": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "decimal", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_decimal": { + "$numberDecimal": "1.23" + } + } + }, + "result": { + "errorContains": "element of type: decimal" + } + } + ] + }, + { + "description": "type=binData", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_binData": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "binData", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AAAA", + "subType": "00" + } + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AAAA", + "subType": "00" + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=javascript", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_javascript": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "javascript", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_javascript": { + "$code": "var x = 1;" + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_javascript": { + "$code": "var x = 1;" + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_javascript": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_javascript": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=javascriptWithScope", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_javascriptWithScope": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "javascriptWithScope", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_javascriptWithScope": { + "$code": "var x = 1;", + "$scope": {} + } + } + }, + "result": { + "errorContains": "element of type: javascriptWithScope" + } + } + ] + }, + { + "description": "type=object", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_object": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "object", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_object": {} + } + }, + "result": { + "errorContains": "element of type: object" + } + } + ] + }, + { + "description": "type=timestamp", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_timestamp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "timestamp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_timestamp": { + "$timestamp": { + "t": 123, + "i": 456 + } + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_timestamp": { + "$timestamp": { + "t": 123, + "i": 456 + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_timestamp": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_timestamp": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=regex", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_regex": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "regex", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_regex": { + "$regularExpression": { + "pattern": "test", + "options": "" + } + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_regex": { + "$regularExpression": { + "pattern": "test", + "options": "" + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_regex": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_regex": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=date", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_date": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "date", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_date": { + "$date": { + "$numberLong": "123" + } + } + } + } + }, + { + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": { + "_id": 1, + "encrypted_date": { + "$date": { + "$numberLong": "123" + } + } + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_date": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_date": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "type=minKey", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_minKey": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "minKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_minKey": { + "$minKey": 1 + } + } + }, + "result": { + "errorContains": "Cannot encrypt element of type: minKey" + } + } + ] + }, + { + "description": "type=maxKey", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_maxKey": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "maxKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_maxKey": { + "$maxKey": 1 + } + } + }, + "result": { + "errorContains": "Cannot encrypt element of type: maxKey" + } + } + ] + }, + { + "description": "type=undefined", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_undefined": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "undefined", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_undefined": { + "$undefined": true + } + } + }, + "result": { + "errorContains": "Cannot encrypt element of type: undefined" + } + } + ] + }, + { + "description": "type=array", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_array": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "array", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_array": [] + } + }, + "result": { + "errorContains": "element of type: array" + } + } + ] + }, + { + "description": "type=bool", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_bool": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "bool", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_bool": true + } + }, + "result": { + "errorContains": "element of type: bool" + } + } + ] + }, + { + "description": "type=null", + "clientOptions": { + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_null": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "null", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_null": true + } + }, + "result": { + "errorContains": "Cannot encrypt element of type: null" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/types.yml b/source/client-side-encryption/tests/legacy/types.yml new file mode 100644 index 0000000000..871bc115fa --- /dev/null +++ b/source/client-side-encryption/tests/legacy/types.yml @@ -0,0 +1,501 @@ +# Attempt to round trip some BSON types. +# Note: db pointer is excluded since it is deprecated and numberlong is excluded due to different driver interpretations of { $numberLong: '123' } in relaxed JSON parsing. + +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {} +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: "type=objectId" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_objectId': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'objectId', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_objectId: {"$oid": "AAAAAAAAAAAAAAAAAAAAAAAA"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc0 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_objectId: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted + - description: "type=symbol" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_symbol': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'symbol', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_symbol: {"$symbol": "test"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc1 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc1_encrypted { _id: 1, encrypted_symbol: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc1_encrypted + - description: "type=int" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_int': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'int', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc2 { _id: 1, encrypted_int: {"$numberInt": "123"} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc2 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc2_encrypted { _id: 1, encrypted_int: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc2_encrypted + - description: "type=double" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_double': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'double', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc4 { _id: 1, encrypted_double: {"$numberDouble": "1.23"} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: double" + # After it is: + # "Cannot encrypt element of type: double" + # Only check for the common suffix. + errorContains: "element of type: double" + - description: "type=decimal" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_decimal': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'decimal', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc5 { _id: 1, encrypted_decimal: {"$numberDecimal": "1.23"} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: decimal" + # After it is: + # "Cannot encrypt element of type: decimal" + # Only check for the common suffix. + errorContains: "element of type: decimal" + - description: "type=binData" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_binData': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'binData', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc6 { _id: 1, encrypted_binData: {"$binary": { base64: "AAAA", subType: "00" } } } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc6 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc6_encrypted { _id: 1, encrypted_binData: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc6_encrypted + - description: "type=javascript" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_javascript': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'javascript', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc7 { _id: 1, encrypted_javascript: {"$code": "var x = 1;" } } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc7 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc7_encrypted { _id: 1, encrypted_javascript: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc7_encrypted + - description: "type=javascriptWithScope" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_javascriptWithScope': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'javascriptWithScope', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc8 { _id: 1, encrypted_javascriptWithScope: {"$code": "var x = 1;", "$scope": {} } } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: javascriptWithScope" + # After it is: + # "Cannot encrypt element of type: javascriptWithScope" + # Only check for the common suffix. + errorContains: "element of type: javascriptWithScope" + - description: "type=object" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_object': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'object', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc9 { _id: 1, encrypted_object: {} } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: object" + # After it is: + # "Cannot encrypt element of type: object" + # Only check for the common suffix. + errorContains: "element of type: object" + - description: "type=timestamp" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_timestamp': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'timestamp', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc10 { _id: 1, encrypted_timestamp: {$timestamp: {t: 123, i: 456}} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc10 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc10_encrypted { _id: 1, encrypted_timestamp: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc10_encrypted + - description: "type=regex" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_regex': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'regex', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc11 { _id: 1, encrypted_regex: {$regularExpression: { pattern: "test", options: ""}} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc11 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc11_encrypted { _id: 1, encrypted_regex: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc11_encrypted + - description: "type=date" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_date': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'date', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc13 { _id: 1, encrypted_date: {$date: { $numberLong: "123" }} } + - name: findOne + arguments: + filter: { _id: 1 } + result: *doc13 + expectations: + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc13_encrypted { _id: 1, encrypted_date: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: {_id: 1} + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc13_encrypted + - description: "type=minKey" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_minKey': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'minKey', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc14 { _id: 1, encrypted_minKey: {$minKey: 1} } + result: + errorContains: "Cannot encrypt element of type: minKey" + - description: "type=maxKey" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_maxKey': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'maxKey', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc15 { _id: 1, encrypted_maxKey: {$maxKey: 1} } + result: + errorContains: "Cannot encrypt element of type: maxKey" + - description: "type=undefined" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_undefined': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'undefined', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc16 { _id: 1, encrypted_undefined: {$undefined: true} } + result: + errorContains: "Cannot encrypt element of type: undefined" + - description: "type=array" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_array': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'array', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc17 { _id: 1, encrypted_array: [] } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: array" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common suffix. + errorContains: "element of type: array" + - description: "type=bool" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_bool': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'bool', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc18 { _id: 1, encrypted_bool: true } + result: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: bool" + # After it is: + # "Cannot encrypt element of type: bool" + # Only check for the common suffix. + errorContains: "element of type: bool" + - description: "type=null" + clientOptions: + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_null': {'encrypt': {'keyId': [{'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'null', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: insertOne + arguments: + document: &doc19 { _id: 1, encrypted_null: true } + result: + errorContains: "Cannot encrypt element of type: null" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/unsupportedCommand.json b/source/client-side-encryption/tests/legacy/unsupportedCommand.json new file mode 100644 index 0000000000..3188715115 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/unsupportedCommand.json @@ -0,0 +1,152 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "x": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "x": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "mapReduce deterministic encryption (unsupported)", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "result": { + "errorContains": "command not supported for auto encryption: mapreduce" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/unsupportedCommand.yml b/source/client-side-encryption/tests/legacy/unsupportedCommand.yml new file mode 100644 index 0000000000..e9edb0f58f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/unsupportedCommand.yml @@ -0,0 +1,25 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, x: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, x: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "mapReduce deterministic encryption (unsupported)" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: mapReduce + arguments: + map: { $code: "function inc() { return emit(0, this.x + 1) }" } + reduce: { $code: "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" } + out: { inline: 1 } + result: + errorContains: "command not supported for auto encryption: mapreduce" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/updateMany.json b/source/client-side-encryption/tests/legacy/updateMany.json new file mode 100644 index 0000000000..823909044b --- /dev/null +++ b/source/client-side-encryption/tests/legacy/updateMany.json @@ -0,0 +1,307 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "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": "updateMany with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateMany", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + }, + "update": { + "$set": { + "encrypted_string": "string2", + "random": "abc" + } + } + }, + "result": { + "matchedCount": 2, + "modifiedCount": 2, + "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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + }, + "multi": true, + "upsert": false + } + ], + "ordered": true + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + }, + { + "description": "updateMany fails when filtering on a random field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateMany", + "arguments": { + "filter": { + "random": "abc" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/updateMany.yml b/source/client-side-encryption/tests/legacy/updateMany.yml new file mode 100644 index 0000000000..6d8c7e9707 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/updateMany.yml @@ -0,0 +1,70 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - &doc1_encrypted { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} } +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: "updateMany with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateMany + arguments: + filter: { encrypted_string: { $in: [ "string0", "string1" ] } } + update: { $set: { encrypted_string: "string2", random: "abc" } } + result: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $in: [ {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}}, {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}} ] } } + u: { $set: { encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==', 'subType': '06'}}, random: { $$type: "binData" } } } + multi: true + upsert: false + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==', 'subType': '06'}}, random: { $$type: "binData" } } + - { _id: 2, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==', 'subType': '06'}}, random: { $$type: "binData" } } + - description: "updateMany fails when filtering on a random field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateMany + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/updateOne.json b/source/client-side-encryption/tests/legacy/updateOne.json new file mode 100644 index 0000000000..23bada964f --- /dev/null +++ b/source/client-side-encryption/tests/legacy/updateOne.json @@ -0,0 +1,465 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "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": "updateOne with deterministic encryption", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1", + "random": "abc" + } + } + }, + "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": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + } + } + ], + "ordered": true + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + } + }, + { + "description": "updateOne fails when filtering on a random field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "random": "abc" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + }, + "result": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + }, + { + "description": "$unset works with an encrypted field", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$unset": { + "encrypted_string": "" + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": {}, + "u": { + "$unset": { + "encrypted_string": "" + } + } + } + ], + "ordered": true + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1 + } + ] + } + } + }, + { + "description": "$rename works if target value has same encryption options", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$rename": { + "encrypted_string": "encrypted_string_equivalent" + } + } + }, + "result": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "update": "default", + "updates": [ + { + "q": {}, + "u": { + "$rename": { + "encrypted_string": "encrypted_string_equivalent" + } + } + } + ], + "ordered": true + }, + "command_name": "update" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string_equivalent": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ] + } + } + }, + { + "description": "$rename fails if target value has different encryption options", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$rename": { + "encrypted_string": "random" + } + } + }, + "result": { + "errorContains": "$rename between two encrypted fields must have the same metadata or both be unencrypted" + } + } + ] + }, + { + "description": "an invalid update (no $ operators) is validated and errors", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "aws": {} + } + } + }, + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "encrypted_string": "random" + } + }, + "result": { + "errorContains": "" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/legacy/updateOne.yml b/source/client-side-encryption/tests/legacy/updateOne.yml new file mode 100644 index 0000000000..aef3716fad --- /dev/null +++ b/source/client-side-encryption/tests/legacy/updateOne.yml @@ -0,0 +1,160 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } +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: "updateOne with deterministic encryption" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1", random: "abc" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'AAAAAAAAAAAAAAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + update: *collection_name + updates: + - q: { encrypted_string: { $eq: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } } + u: { $set: {encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}}, random: { $$type: "binData" } } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==', 'subType': '06'}}, random: { $$type: "binData"} } + - description: "updateOne fails when filtering on a random field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + result: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "$unset works with an encrypted field" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $unset: { encrypted_string: "" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + update: *collection_name + updates: + - q: { } + u: { $unset: {encrypted_string: "" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1 } + - description: "$rename works if target value has same encryption options" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $rename: { encrypted_string: "encrypted_string_equivalent" } } + result: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + - command_started_event: + command: + update: *collection_name + updates: + - q: { } + u: { $rename: {encrypted_string: "encrypted_string_equivalent" } } + ordered: true + command_name: update + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - { _id: 1, encrypted_string_equivalent: {'$binary': {'base64': 'AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==', 'subType': '06'}} } + - description: "$rename fails if target value has different encryption options" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { $rename: { encrypted_string: "random" } } + result: + errorContains: "$rename between two encrypted fields must have the same metadata or both be unencrypted" + - description: "an invalid update (no $ operators) is validated and errors" + clientOptions: + autoEncryptOpts: + kmsProviders: + aws: {} # Credentials filled in from environment. + operations: + - name: updateOne + arguments: + filter: { } + update: { encrypted_string: "random" } + result: + errorContains: "" # Note, drivers differ in the error message. Just ensure an error is thrown. \ No newline at end of file diff --git a/source/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json b/source/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.json new file mode 100644 index 0000000000..e07137ce15 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/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/source/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml b/source/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml new file mode 100644 index 0000000000..d2b7ffaee6 --- /dev/null +++ b/source/client-side-encryption/tests/legacy/validatorAndPartialFieldExpression.yml @@ -0,0 +1,166 @@ +# This test requires libmongocrypt 1.5.0-alpha2. +runOn: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: "6.0.0" + +database_name: &database_name "default" +collection_name: &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: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: database + arguments: + collection: "encryptedCollection" + - name: createCollection + object: database + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + database: *database_name + 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: + # Drop to remove a collection that may exist from previous test runs. + - 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: *database_name + 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: + # Drop to remove a collection that may exist from previous test runs. + - 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" \ No newline at end of file diff --git a/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.json b/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.json new file mode 100644 index 0000000000..dc979b5019 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.json @@ -0,0 +1,219 @@ +{ + "description": "QE-Text-cleanupStructuredEncryptionData", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "9.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "QE Text cleanupStructuredEncryptionData works", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "cleanupStructuredEncryptionData": "coll" + }, + "commandName": "cleanupStructuredEncryptionData" + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "cleanupStructuredEncryptionData": "coll", + "cleanupTokens": { + "encryptedText": { + "ecoc": { + "$binary": { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "YAiF7Iwhqq1UyfxPvm70xfQJtrIRPrjfD2yRLG1+saQ=", + "subType": "00" + } + } + } + } + }, + "commandName": "cleanupStructuredEncryptionData" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.yml b/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.yml new file mode 100644 index 0000000000..69243bfa4f --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-cleanupStructuredEncryptionData.yml @@ -0,0 +1,128 @@ +description: QE-Text-cleanupStructuredEncryptionData +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "9.0.0" # Server 9.0.0 adds stable support for QE text prefix and suffix queries. + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.0 # For MONGOCRYPT-870. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "QE Text cleanupStructuredEncryptionData works" + operations: + - name: runCommand + object: *db + arguments: + command: + cleanupStructuredEncryptionData: *coll + commandName: cleanupStructuredEncryptionData + expectResult: { ok: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + { + "cleanupStructuredEncryptionData": *coll, + "cleanupTokens": { + "encryptedText": { + "ecoc": { + "$binary": { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "YAiF7Iwhqq1UyfxPvm70xfQJtrIRPrjfD2yRLG1+saQ=", + "subType": "00" + } + } + } + } + } + commandName: cleanupStructuredEncryptionData diff --git a/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.json b/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.json new file mode 100644 index 0000000000..1c3c6cc0de --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.json @@ -0,0 +1,261 @@ +{ + "description": "QE-Text-compactStructuredEncryptionData", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "9.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "QE Text compactStructuredEncryptionData works", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "compactStructuredEncryptionData": "coll" + }, + "commandName": "compactStructuredEncryptionData" + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "compactStructuredEncryptionData": "coll", + "encryptionInformation": { + "type": { + "$numberInt": "1" + }, + "schema": { + "db.coll": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ], + "strEncodeVersion": { + "$numberInt": "1" + }, + "escCollection": "enxcol_.coll.esc", + "ecocCollection": "enxcol_.coll.ecoc" + } + } + }, + "compactionTokens": { + "encryptedText": { + "ecoc": { + "$binary": { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "YAiF7Iwhqq1UyfxPvm70xfQJtrIRPrjfD2yRLG1+saQ=", + "subType": "00" + } + } + } + } + }, + "commandName": "compactStructuredEncryptionData" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.yml b/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.yml new file mode 100644 index 0000000000..4bdc55ee88 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-compactStructuredEncryptionData.yml @@ -0,0 +1,137 @@ +description: QE-Text-compactStructuredEncryptionData +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "9.0.0" # Server 9.0.0 adds stable support for QE text prefix and suffix queries. + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.0 # For MONGOCRYPT-870. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: &encryptedFields + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "QE Text compactStructuredEncryptionData works" + operations: + - name: runCommand + object: *db + arguments: + command: + compactStructuredEncryptionData: *coll + commandName: compactStructuredEncryptionData + expectResult: { ok: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + compactStructuredEncryptionData: *coll + encryptionInformation: + type: { "$numberInt": "1" } + schema: + db.coll: + <<: *encryptedFields + # libmongocrypt applies strEncodeVersion, escCollection, and ecocCollection: + strEncodeVersion: { "$numberInt": "1" } + escCollection: "enxcol_.coll.esc" + ecocCollection: "enxcol_.coll.ecoc" + compactionTokens: + encryptedText: + ecoc: + { + "$binary": + { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00", + }, + } + anchorPaddingToken: + { + "$binary": + { + "base64": "YAiF7Iwhqq1UyfxPvm70xfQJtrIRPrjfD2yRLG1+saQ=", + "subType": "00", + }, + } + commandName: compactStructuredEncryptionData diff --git a/source/client-side-encryption/tests/unified/QE-Text-prefix.json b/source/client-side-encryption/tests/unified/QE-Text-prefix.json new file mode 100644 index 0000000000..25475e2c3a --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-prefix.json @@ -0,0 +1,338 @@ +{ + "description": "QE-Text-prefix", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "9.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "prefix", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE prefix", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrStartsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrStartsWith": { + "input": "$encryptedText", + "prefix": "foo" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fmUMXTMV/XRiN0IL3VXxSEn6SQG9E6Po30kJKB8JJlQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vZIDMiFDgjmLNYVrrbnq1zT4hg7sGpe/PMtighSsnRc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "26Z5G+sHTzV3D7F8Y0m08389USZ2afinyFV3ez9UEBQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q/JEq8of7bE0QE5Id0XuOsNQ4qVpANYymcPQDUL2Ywk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Uvvv46LkfbgLoPqZ6xTBzpgoYRTM6FUgRdqZ9eaVojI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nMxdq2lladuBJA3lv3JC2MumIUtRJBNJVLp3PVE6nQk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hS3V0qq5CF/SkTl3ZWWWgXcAJ8G5yGtkY2RwcHNc5Oc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McgwYUxfKj5+4D0vskZymy4KA82s71MR25iV/Enutww=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ciqdk1b+t+Vrr6oIlFFk0Zdym5BPmwN3glQ0/VcsVdM=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrStartsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrStartsWith": { + "input": "$encryptedText", + "prefix": "bar" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-prefix.yml b/source/client-side-encryption/tests/unified/QE-Text-prefix.yml new file mode 100644 index 0000000000..7cdc4e45f7 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-prefix.yml @@ -0,0 +1,225 @@ +description: QE-Text-prefix +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "9.0.0" # Server 9.0.0 adds stable support for QE text prefix and suffix queries. + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.0 # For MONGOCRYPT-870. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "prefix", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE prefix" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrStartsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrStartsWith: { input: "$encryptedText", prefix: "foo" }, + }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "fmUMXTMV/XRiN0IL3VXxSEn6SQG9E6Po30kJKB8JJlQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "vZIDMiFDgjmLNYVrrbnq1zT4hg7sGpe/PMtighSsnRc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "26Z5G+sHTzV3D7F8Y0m08389USZ2afinyFV3ez9UEBQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "q/JEq8of7bE0QE5Id0XuOsNQ4qVpANYymcPQDUL2Ywk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "Uvvv46LkfbgLoPqZ6xTBzpgoYRTM6FUgRdqZ9eaVojI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "nMxdq2lladuBJA3lv3JC2MumIUtRJBNJVLp3PVE6nQk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "hS3V0qq5CF/SkTl3ZWWWgXcAJ8G5yGtkY2RwcHNc5Oc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "McgwYUxfKj5+4D0vskZymy4KA82s71MR25iV/Enutww=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "Ciqdk1b+t+Vrr6oIlFFk0Zdym5BPmwN3glQ0/VcsVdM=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrStartsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrStartsWith: { input: "$encryptedText", prefix: "bar" }, + }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.json b/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.json new file mode 100644 index 0000000000..51e72fd3ca --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.json @@ -0,0 +1,339 @@ +{ + "description": "QE-Text-prefixPreview", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "maxServerVersion": "8.99.99", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "prefixPreview", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE prefixPreview", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrStartsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrStartsWith": { + "input": "$encryptedText", + "prefix": "foo" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "fmUMXTMV/XRiN0IL3VXxSEn6SQG9E6Po30kJKB8JJlQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vZIDMiFDgjmLNYVrrbnq1zT4hg7sGpe/PMtighSsnRc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "26Z5G+sHTzV3D7F8Y0m08389USZ2afinyFV3ez9UEBQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "q/JEq8of7bE0QE5Id0XuOsNQ4qVpANYymcPQDUL2Ywk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Uvvv46LkfbgLoPqZ6xTBzpgoYRTM6FUgRdqZ9eaVojI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "nMxdq2lladuBJA3lv3JC2MumIUtRJBNJVLp3PVE6nQk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hS3V0qq5CF/SkTl3ZWWWgXcAJ8G5yGtkY2RwcHNc5Oc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "McgwYUxfKj5+4D0vskZymy4KA82s71MR25iV/Enutww=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "Ciqdk1b+t+Vrr6oIlFFk0Zdym5BPmwN3glQ0/VcsVdM=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrStartsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrStartsWith": { + "input": "$encryptedText", + "prefix": "bar" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.yml b/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.yml new file mode 100644 index 0000000000..d1fbe26a69 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-prefixPreview.yml @@ -0,0 +1,226 @@ +description: QE-Text-prefixPreview +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.2.0" # Server 8.2.0 adds preview support for QE text queries. + maxServerVersion: "8.99.99" # Server 9.0.0-rc0 removes support for "prefixPreview" and "suffixPreview": SERVER-123416 + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.1 # For MONGOCRYPT-937. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "prefixPreview", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE prefixPreview" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrStartsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrStartsWith: { input: "$encryptedText", prefix: "foo" }, + }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "fmUMXTMV/XRiN0IL3VXxSEn6SQG9E6Po30kJKB8JJlQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "vZIDMiFDgjmLNYVrrbnq1zT4hg7sGpe/PMtighSsnRc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "26Z5G+sHTzV3D7F8Y0m08389USZ2afinyFV3ez9UEBQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "q/JEq8of7bE0QE5Id0XuOsNQ4qVpANYymcPQDUL2Ywk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "Uvvv46LkfbgLoPqZ6xTBzpgoYRTM6FUgRdqZ9eaVojI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "nMxdq2lladuBJA3lv3JC2MumIUtRJBNJVLp3PVE6nQk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "hS3V0qq5CF/SkTl3ZWWWgXcAJ8G5yGtkY2RwcHNc5Oc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "McgwYUxfKj5+4D0vskZymy4KA82s71MR25iV/Enutww=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "Ciqdk1b+t+Vrr6oIlFFk0Zdym5BPmwN3glQ0/VcsVdM=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrStartsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrStartsWith: { input: "$encryptedText", prefix: "bar" }, + }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/QE-Text-substring.json b/source/client-side-encryption/tests/unified/QE-Text-substring.json new file mode 100644 index 0000000000..f42a297278 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-substring.json @@ -0,0 +1,467 @@ +{ + "description": "QE-Text-substring", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "9.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.20.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "substring", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "6" + }, + "strMaxLength": { + "$numberLong": "20" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE substring", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrContains", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrContains": { + "input": "$encryptedText", + "substring": "oba" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IpY3x/jjm8j/74jAdUhgxdM5hk68zR0zv/lTKm/72Vg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G+ky260C6QiOfIxKz14FmaMbAxvui1BKJO/TnLOHlGk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7dv3gAKe9vwJMZmpB40pRCwRTmc7ds9UkGhxH8j084E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o0V+Efn6x8XQdE80F1tztNaT3qxHjcsd9DOQ47BtmQk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sJvrCjyVot7PIZFsdRehWFANKAj6fmBaj3FLbz/dZLE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e98auxFmu02h5MfBIARk29MI7hSmvN3F9DaQ0xjqoEM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US83krGNov/ezL6IhsY5eEOCxv1xUPDIEL/nmY0IKi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P2Aq5+OHZPG0CWIdmZvWq9c/18ZKVYW3vbxd+WU/TXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8AdPRPnSzcd5uhq4TZfNvNeF0XjLNVwAsJJMTtktw84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9O6u/G51I4ZHFLhL4ZLuudbr0s202A2QnPfThmOXPhI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N7AjYVyVlv6+lVSTM+cIxRL3SMgs3G5LgxSs+jrgDkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RbGF7dQbPGYQFd9DDO1hPz1UlLOJ77FAC6NsjGwJeos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m7srHMgKm6kZwsNx8rc45pmw0/9Qro6xuQ8lZS3+RYk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K75CNU3JyKFqZWPiIsVi4+n7DhYmcPl/nEhQ3d88mVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c7bwGpUZc/7JzEnMS7qQ/TPuXZyrmMihFaAV6zIqbZc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rDvEdUgEk8u4Srt3ETokWs2FXcnyJaRGQ+NbkFwi2rQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VcdZj9zfveRBRlpCR2OYWau2+GokOFb73TE3gpElNiU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOa9o2xfA6OgkbYUxd6wQJicaeN6guhy2V66W3ALsaA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1xGkJh+um70XiRd8lKLDtyHgDqrf7/59Mg7X0+KZh8k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OSvllqHxycbcZN4phR6NDujY3ttA59o7nQJ6V9eJpX0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZTX1pyk8Vdw0BSbJx7GeJNcQf3tGKxbrrNSTqBqUWkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cn7V05zb5iXwYrePGMHztC+GRq+Tj8IMpRDraauPhSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E9bV9KyrZxHJSUmMg0HrDK4gGN+75ruelAnrM6hXQgY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrssTNmdgXoTGpbaF0JLRCGH6cDQuz1XEFNTy98nrb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jZmyOJP35dsxQ/OY5U4ISpVRIYr8iedNfcwZiKt29Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d2mocORMbX9MX+/itAW8r1kxVw2/uii4vzXtc+2CIRQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JBnJy58eRPhDo3DuZvsHbvQDiHXxdtAx1Eif66k5SfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OjbDulC8s62v0pgweBSsQqtJjJBwH5JinfJpj7nVr+A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "85i7KT2GP9nSda3Gsil5LKubhq0LDtc22pxBxHpR+nE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "u9Fvsclwrs9lwIcMPV/fMZD7L3d5anSfJQVjQb9mgLg=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrContains", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrContains": { + "input": "$encryptedText", + "substring": "blah" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-substring.yml b/source/client-side-encryption/tests/unified/QE-Text-substring.yml new file mode 100644 index 0000000000..b034c50dd6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-substring.yml @@ -0,0 +1,374 @@ +description: QE-Text-substring +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "9.0.0" # Server 9.0.0 adds stable support for QE text substring queries. + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.20.0 # For MONGOCRYPT-936. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "substring", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "6" }, + "strMaxLength": { "$numberLong": "20" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE substring" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrContains" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrContains: + { input: "$encryptedText", substring: "oba" }, + }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "IpY3x/jjm8j/74jAdUhgxdM5hk68zR0zv/lTKm/72Vg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "G+ky260C6QiOfIxKz14FmaMbAxvui1BKJO/TnLOHlGk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "7dv3gAKe9vwJMZmpB40pRCwRTmc7ds9UkGhxH8j084E=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "o0V+Efn6x8XQdE80F1tztNaT3qxHjcsd9DOQ47BtmQk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "sJvrCjyVot7PIZFsdRehWFANKAj6fmBaj3FLbz/dZLE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "e98auxFmu02h5MfBIARk29MI7hSmvN3F9DaQ0xjqoEM=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "US83krGNov/ezL6IhsY5eEOCxv1xUPDIEL/nmY0IKi0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "P2Aq5+OHZPG0CWIdmZvWq9c/18ZKVYW3vbxd+WU/TXU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "8AdPRPnSzcd5uhq4TZfNvNeF0XjLNVwAsJJMTtktw84=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "9O6u/G51I4ZHFLhL4ZLuudbr0s202A2QnPfThmOXPhI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "N7AjYVyVlv6+lVSTM+cIxRL3SMgs3G5LgxSs+jrgDkI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "RbGF7dQbPGYQFd9DDO1hPz1UlLOJ77FAC6NsjGwJeos=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "m7srHMgKm6kZwsNx8rc45pmw0/9Qro6xuQ8lZS3+RYk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "K75CNU3JyKFqZWPiIsVi4+n7DhYmcPl/nEhQ3d88mVI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "c7bwGpUZc/7JzEnMS7qQ/TPuXZyrmMihFaAV6zIqbZc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "rDvEdUgEk8u4Srt3ETokWs2FXcnyJaRGQ+NbkFwi2rQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "VcdZj9zfveRBRlpCR2OYWau2+GokOFb73TE3gpElNiU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "eOa9o2xfA6OgkbYUxd6wQJicaeN6guhy2V66W3ALsaA=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "1xGkJh+um70XiRd8lKLDtyHgDqrf7/59Mg7X0+KZh8k=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "OSvllqHxycbcZN4phR6NDujY3ttA59o7nQJ6V9eJpX0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "ZTX1pyk8Vdw0BSbJx7GeJNcQf3tGKxbrrNSTqBqUWkg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "cn7V05zb5iXwYrePGMHztC+GRq+Tj8IMpRDraauPhSE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "E9bV9KyrZxHJSUmMg0HrDK4gGN+75ruelAnrM6hXQgY=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "WrssTNmdgXoTGpbaF0JLRCGH6cDQuz1XEFNTy98nrb0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "jZmyOJP35dsxQ/OY5U4ISpVRIYr8iedNfcwZiKt29Qc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "d2mocORMbX9MX+/itAW8r1kxVw2/uii4vzXtc+2CIRQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "JBnJy58eRPhDo3DuZvsHbvQDiHXxdtAx1Eif66k5SfA=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "OjbDulC8s62v0pgweBSsQqtJjJBwH5JinfJpj7nVr+A=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "85i7KT2GP9nSda3Gsil5LKubhq0LDtc22pxBxHpR+nE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "u9Fvsclwrs9lwIcMPV/fMZD7L3d5anSfJQVjQb9mgLg=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrContains" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrContains: { input: "$encryptedText", substring: "blah" }, + }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/QE-Text-substringPreview.json b/source/client-side-encryption/tests/unified/QE-Text-substringPreview.json new file mode 100644 index 0000000000..1df3170958 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-substringPreview.json @@ -0,0 +1,468 @@ +{ + "description": "QE-Text-substringPreview", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "maxServerVersion": "8.99.99", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "substringPreview", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "6" + }, + "strMaxLength": { + "$numberLong": "20" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE substringPreview", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrContains", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrContains": { + "input": "$encryptedText", + "substring": "oba" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "IpY3x/jjm8j/74jAdUhgxdM5hk68zR0zv/lTKm/72Vg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "G+ky260C6QiOfIxKz14FmaMbAxvui1BKJO/TnLOHlGk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "7dv3gAKe9vwJMZmpB40pRCwRTmc7ds9UkGhxH8j084E=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "o0V+Efn6x8XQdE80F1tztNaT3qxHjcsd9DOQ47BtmQk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "sJvrCjyVot7PIZFsdRehWFANKAj6fmBaj3FLbz/dZLE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "e98auxFmu02h5MfBIARk29MI7hSmvN3F9DaQ0xjqoEM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "US83krGNov/ezL6IhsY5eEOCxv1xUPDIEL/nmY0IKi0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "P2Aq5+OHZPG0CWIdmZvWq9c/18ZKVYW3vbxd+WU/TXU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "8AdPRPnSzcd5uhq4TZfNvNeF0XjLNVwAsJJMTtktw84=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "9O6u/G51I4ZHFLhL4ZLuudbr0s202A2QnPfThmOXPhI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "N7AjYVyVlv6+lVSTM+cIxRL3SMgs3G5LgxSs+jrgDkI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "RbGF7dQbPGYQFd9DDO1hPz1UlLOJ77FAC6NsjGwJeos=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "m7srHMgKm6kZwsNx8rc45pmw0/9Qro6xuQ8lZS3+RYk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "K75CNU3JyKFqZWPiIsVi4+n7DhYmcPl/nEhQ3d88mVI=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "c7bwGpUZc/7JzEnMS7qQ/TPuXZyrmMihFaAV6zIqbZc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "rDvEdUgEk8u4Srt3ETokWs2FXcnyJaRGQ+NbkFwi2rQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "VcdZj9zfveRBRlpCR2OYWau2+GokOFb73TE3gpElNiU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "eOa9o2xfA6OgkbYUxd6wQJicaeN6guhy2V66W3ALsaA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "1xGkJh+um70XiRd8lKLDtyHgDqrf7/59Mg7X0+KZh8k=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OSvllqHxycbcZN4phR6NDujY3ttA59o7nQJ6V9eJpX0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "ZTX1pyk8Vdw0BSbJx7GeJNcQf3tGKxbrrNSTqBqUWkg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "cn7V05zb5iXwYrePGMHztC+GRq+Tj8IMpRDraauPhSE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "E9bV9KyrZxHJSUmMg0HrDK4gGN+75ruelAnrM6hXQgY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "WrssTNmdgXoTGpbaF0JLRCGH6cDQuz1XEFNTy98nrb0=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "jZmyOJP35dsxQ/OY5U4ISpVRIYr8iedNfcwZiKt29Qc=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "d2mocORMbX9MX+/itAW8r1kxVw2/uii4vzXtc+2CIRQ=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "JBnJy58eRPhDo3DuZvsHbvQDiHXxdtAx1Eif66k5SfA=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "OjbDulC8s62v0pgweBSsQqtJjJBwH5JinfJpj7nVr+A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "85i7KT2GP9nSda3Gsil5LKubhq0LDtc22pxBxHpR+nE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "u9Fvsclwrs9lwIcMPV/fMZD7L3d5anSfJQVjQb9mgLg=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrContains", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrContains": { + "input": "$encryptedText", + "substring": "blah" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-substringPreview.yml b/source/client-side-encryption/tests/unified/QE-Text-substringPreview.yml new file mode 100644 index 0000000000..d033cd4f64 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-substringPreview.yml @@ -0,0 +1,375 @@ +description: QE-Text-substringPreview +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.2.0" # Server 8.2.0 adds preview support for QE text queries. + maxServerVersion: "8.99.99" # Server 9.0.0 will remove support for "substringPreview": SERVER-129158 + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.15.0 # For SPM-4158. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "substringPreview", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "6" }, + "strMaxLength": { "$numberLong": "20" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE substringPreview" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrContains" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrContains: + { input: "$encryptedText", substring: "oba" }, + }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "IpY3x/jjm8j/74jAdUhgxdM5hk68zR0zv/lTKm/72Vg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "G+ky260C6QiOfIxKz14FmaMbAxvui1BKJO/TnLOHlGk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "7dv3gAKe9vwJMZmpB40pRCwRTmc7ds9UkGhxH8j084E=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "o0V+Efn6x8XQdE80F1tztNaT3qxHjcsd9DOQ47BtmQk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "sJvrCjyVot7PIZFsdRehWFANKAj6fmBaj3FLbz/dZLE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "e98auxFmu02h5MfBIARk29MI7hSmvN3F9DaQ0xjqoEM=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "US83krGNov/ezL6IhsY5eEOCxv1xUPDIEL/nmY0IKi0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "P2Aq5+OHZPG0CWIdmZvWq9c/18ZKVYW3vbxd+WU/TXU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "8AdPRPnSzcd5uhq4TZfNvNeF0XjLNVwAsJJMTtktw84=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "9O6u/G51I4ZHFLhL4ZLuudbr0s202A2QnPfThmOXPhI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "N7AjYVyVlv6+lVSTM+cIxRL3SMgs3G5LgxSs+jrgDkI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "RbGF7dQbPGYQFd9DDO1hPz1UlLOJ77FAC6NsjGwJeos=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "m7srHMgKm6kZwsNx8rc45pmw0/9Qro6xuQ8lZS3+RYk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "K75CNU3JyKFqZWPiIsVi4+n7DhYmcPl/nEhQ3d88mVI=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "c7bwGpUZc/7JzEnMS7qQ/TPuXZyrmMihFaAV6zIqbZc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "rDvEdUgEk8u4Srt3ETokWs2FXcnyJaRGQ+NbkFwi2rQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "VcdZj9zfveRBRlpCR2OYWau2+GokOFb73TE3gpElNiU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "eOa9o2xfA6OgkbYUxd6wQJicaeN6guhy2V66W3ALsaA=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "1xGkJh+um70XiRd8lKLDtyHgDqrf7/59Mg7X0+KZh8k=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "OSvllqHxycbcZN4phR6NDujY3ttA59o7nQJ6V9eJpX0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "ZTX1pyk8Vdw0BSbJx7GeJNcQf3tGKxbrrNSTqBqUWkg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "cn7V05zb5iXwYrePGMHztC+GRq+Tj8IMpRDraauPhSE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "E9bV9KyrZxHJSUmMg0HrDK4gGN+75ruelAnrM6hXQgY=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "WrssTNmdgXoTGpbaF0JLRCGH6cDQuz1XEFNTy98nrb0=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "jZmyOJP35dsxQ/OY5U4ISpVRIYr8iedNfcwZiKt29Qc=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "d2mocORMbX9MX+/itAW8r1kxVw2/uii4vzXtc+2CIRQ=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "JBnJy58eRPhDo3DuZvsHbvQDiHXxdtAx1Eif66k5SfA=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "OjbDulC8s62v0pgweBSsQqtJjJBwH5JinfJpj7nVr+A=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "85i7KT2GP9nSda3Gsil5LKubhq0LDtc22pxBxHpR+nE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "u9Fvsclwrs9lwIcMPV/fMZD7L3d5anSfJQVjQb9mgLg=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrContains" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { + $encStrContains: { input: "$encryptedText", substring: "blah" }, + }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/QE-Text-suffix.json b/source/client-side-encryption/tests/unified/QE-Text-suffix.json new file mode 100644 index 0000000000..ad6cdc06c9 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-suffix.json @@ -0,0 +1,338 @@ +{ + "description": "QE-Text-suffix", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "9.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffix", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE suffix", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrEndsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrEndsWith": { + "input": "$encryptedText", + "suffix": "bar" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uDCWsucUsJemUP7pmeb+Kd8B9qupVzI8wnLFqX1rkiU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "W3E1x4bHZ8SEHFz4zwXM0G5Z5WSwBhnxE8x5/qdP6JM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6g/TXVDDf6z+ntResIvTKWdmIy4ajQ1rhwdNZIiEG7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hU+u/T3D6dHDpT3d/v5AlgtRoAufCXCAyO2jQlgsnCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vrPnq0AtBIURNgNGA6HJL+5/p5SBWe+qz8505TRo/dE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "W5pylBxdv2soY2NcBfPiHDVLTS6tx+0ULkI8gysBeFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oWO3xX3x0bYUJGK2S1aPAmlU3Xtfsgb9lTZ6flGAlsg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SjZGucTEUbdpd86O8yj1pyMyBOOKxvAQ9C8ngZ9C5UE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CEaMZkxVDVbnXr+To0DOyvsva04UQkIYP3KtgYVVwf8=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrEndsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrEndsWith": { + "input": "$encryptedText", + "suffix": "foo" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-suffix.yml b/source/client-side-encryption/tests/unified/QE-Text-suffix.yml new file mode 100644 index 0000000000..c98cb5e9dc --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-suffix.yml @@ -0,0 +1,221 @@ +description: QE-Text-suffix +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "9.0.0" # Server 9.0.0 adds stable support for QE text prefix and suffix queries. + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.0 # For MONGOCRYPT-870. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "suffix", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE suffix" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrEndsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { $encStrEndsWith: { input: "$encryptedText", suffix: "bar" } }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "uDCWsucUsJemUP7pmeb+Kd8B9qupVzI8wnLFqX1rkiU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "W3E1x4bHZ8SEHFz4zwXM0G5Z5WSwBhnxE8x5/qdP6JM=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "6g/TXVDDf6z+ntResIvTKWdmIy4ajQ1rhwdNZIiEG7A=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "hU+u/T3D6dHDpT3d/v5AlgtRoAufCXCAyO2jQlgsnCw=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "vrPnq0AtBIURNgNGA6HJL+5/p5SBWe+qz8505TRo/dE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "W5pylBxdv2soY2NcBfPiHDVLTS6tx+0ULkI8gysBeFY=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "oWO3xX3x0bYUJGK2S1aPAmlU3Xtfsgb9lTZ6flGAlsg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "SjZGucTEUbdpd86O8yj1pyMyBOOKxvAQ9C8ngZ9C5UE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "CEaMZkxVDVbnXr+To0DOyvsva04UQkIYP3KtgYVVwf8=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrEndsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { $encStrEndsWith: { input: "$encryptedText", suffix: "foo" } }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.json b/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.json new file mode 100644 index 0000000000..e30d0cfd58 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.json @@ -0,0 +1,339 @@ +{ + "description": "QE-Text-suffixPreview", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "maxServerVersion": "8.99.99", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.19.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + { + "queryType": "suffixPreview", + "contention": { + "$numberLong": "0" + }, + "strMinQueryLength": { + "$numberLong": "3" + }, + "strMaxQueryLength": { + "$numberLong": "30" + }, + "caseSensitive": true, + "diacriticSensitive": true + } + ] + } + ] + } + } + } + ], + "tests": [ + { + "description": "Insert QE suffixPreview", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "coll" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1, + "encryptedText": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Query with matching $encStrEndsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrEndsWith": { + "input": "$encryptedText", + "suffix": "bar" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": { + "$numberInt": "1" + }, + "encryptedText": "foobar", + "__safeContent__": [ + { + "$binary": { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "uDCWsucUsJemUP7pmeb+Kd8B9qupVzI8wnLFqX1rkiU=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "W3E1x4bHZ8SEHFz4zwXM0G5Z5WSwBhnxE8x5/qdP6JM=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "6g/TXVDDf6z+ntResIvTKWdmIy4ajQ1rhwdNZIiEG7A=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "hU+u/T3D6dHDpT3d/v5AlgtRoAufCXCAyO2jQlgsnCw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "vrPnq0AtBIURNgNGA6HJL+5/p5SBWe+qz8505TRo/dE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "W5pylBxdv2soY2NcBfPiHDVLTS6tx+0ULkI8gysBeFY=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "oWO3xX3x0bYUJGK2S1aPAmlU3Xtfsgb9lTZ6flGAlsg=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SjZGucTEUbdpd86O8yj1pyMyBOOKxvAQ9C8ngZ9C5UE=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "CEaMZkxVDVbnXr+To0DOyvsva04UQkIYP3KtgYVVwf8=", + "subType": "00" + } + } + ] + } + ] + } + ] + }, + { + "description": "Query with non-matching $encStrEndsWith", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedText": "foobar" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "$expr": { + "$encStrEndsWith": { + "input": "$encryptedText", + "suffix": "foo" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.yml b/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.yml new file mode 100644 index 0000000000..3a1414cf38 --- /dev/null +++ b/source/client-side-encryption/tests/unified/QE-Text-suffixPreview.yml @@ -0,0 +1,222 @@ +description: QE-Text-suffixPreview +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.2.0" # Server 8.2.0 adds preview support for QE text queries. + maxServerVersion: "8.99.99" # Server 9.0.0-rc0 removes support for "prefixPreview" and "suffixPreview": SERVER-123416 + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + csfle: + minLibmongocryptVersion: 1.19.1 # For MONGOCRYPT-937. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll +initialData: + # Insert data encryption key: + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": &keyid { "$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" }, + }, + ] + # Create encrypted collection: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": *keyid, + "path": "encryptedText", + "bsonType": "string", + "queries": [ + # Use zero contention for deterministic __safeContent__: + { + "queryType": "suffixPreview", + "contention": { "$numberLong": "0" }, + "strMinQueryLength": { "$numberLong": "3" }, + "strMaxQueryLength": { "$numberLong": "30" }, + "caseSensitive": true, + "diacriticSensitive": true, + }, + ], + }, + ], + } +tests: + - description: "Insert QE suffixPreview" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + expectEvents: + - client: "client" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *coll + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [ *keyid ] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *coll + documents: + - { "_id": 1, "encryptedText": { $$type: "binData" } } # Sends encrypted payload + ordered: true + commandName: insert + - description: "Query with matching $encStrEndsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { $encStrEndsWith: { input: "$encryptedText", suffix: "bar" } }, + } + object: *coll + expectResult: + [ + { + "_id": { "$numberInt": "1" }, + "encryptedText": "foobar", + "__safeContent__": + [ + { + "$binary": + { + "base64": "wpaMBVDjL4bHf9EtSP52PJFzyNn1R19+iNI/hWtvzdk=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "uDCWsucUsJemUP7pmeb+Kd8B9qupVzI8wnLFqX1rkiU=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "W3E1x4bHZ8SEHFz4zwXM0G5Z5WSwBhnxE8x5/qdP6JM=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "6g/TXVDDf6z+ntResIvTKWdmIy4ajQ1rhwdNZIiEG7A=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "hU+u/T3D6dHDpT3d/v5AlgtRoAufCXCAyO2jQlgsnCw=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "vrPnq0AtBIURNgNGA6HJL+5/p5SBWe+qz8505TRo/dE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "W5pylBxdv2soY2NcBfPiHDVLTS6tx+0ULkI8gysBeFY=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "oWO3xX3x0bYUJGK2S1aPAmlU3Xtfsgb9lTZ6flGAlsg=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "SjZGucTEUbdpd86O8yj1pyMyBOOKxvAQ9C8ngZ9C5UE=", + "subType": "00", + }, + }, + { + "$binary": + { + "base64": "CEaMZkxVDVbnXr+To0DOyvsva04UQkIYP3KtgYVVwf8=", + "subType": "00", + }, + }, + ], + }, + ] + + - description: "Query with non-matching $encStrEndsWith" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedText: "foobar" } + object: *coll + - name: find + arguments: + filter: + { + $expr: + { $encStrEndsWith: { input: "$encryptedText", suffix: "foo" } }, + } + object: *coll + expectResult: [] diff --git a/source/client-side-encryption/tests/unified/accessToken-azure.json b/source/client-side-encryption/tests/unified/accessToken-azure.json new file mode 100644 index 0000000000..510d87956f --- /dev/null +++ b/source/client-side-encryption/tests/unified/accessToken-azure.json @@ -0,0 +1,186 @@ +{ + "description": "accessToken-azure", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.6.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "accessToken": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + }, + { + "clientEncryption": { + "id": "clientEncryption", + "clientEncryptionOpts": { + "keyVaultClient": "client", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "accessToken": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "initialData": [ + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "properties": { + "secret": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": [ + "my-key" + ], + "keyMaterial": { + "$binary": { + "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "azure", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ] + } + ], + "tests": [ + { + "description": "Auto encrypt using access token Azure credentials", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "secret": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "secret": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ], + "collectionName": "coll", + "databaseName": "db" + } + ] + }, + { + "description": "Explicit encrypt using access token Azure credentials", + "operations": [ + { + "name": "encrypt", + "object": "clientEncryption", + "arguments": { + "value": "string0", + "opts": { + "keyAltName": "my-key", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "expectResult": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/accessToken-azure.yml b/source/client-side-encryption/tests/unified/accessToken-azure.yml new file mode 100644 index 0000000000..b2cc426ba4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/accessToken-azure.yml @@ -0,0 +1,107 @@ +description: accessToken-azure +schemaVersion: "1.28" # For accessToken +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.6.0" # MONGOCRYPT-473. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { accessToken: { $$placeholder: 1 } } # Use csfle/setup-secrets.sh from drivers-evergreen-tools to obtain token. + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll + - clientEncryption: + id: &clientEncryption clientEncryption + clientEncryptionOpts: + keyVaultClient: *client + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { accessToken: { $$placeholder: 1 } } # Use csfle/setup-secrets.sh from drivers-evergreen-tools to obtain token. +initialData: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + validator: + $jsonSchema: { + "properties": + { + "secret": + { + "encrypt": + { + "keyId": ["$binary": { "base64": "AZURE+AAAAAAAAAAAAAAAA==", "subType": "04" }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object" + } + - databaseName: keyvault + collectionName: datakeys + documents: + - { + "_id": { "$binary": { "base64": "AZURE+AAAAAAAAAAAAAAAA==", "subType": "04" } }, + "keyAltNames": ["my-key"], + "keyMaterial": + { + "$binary": + { + "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "azure", "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", "keyName": "key-name-csfle" } + } +tests: + - description: "Auto encrypt using access token Azure credentials" + operations: + - name: insertOne + arguments: + document: { _id: 1, secret: "string0" } + object: *coll + outcome: + - documents: + - { + _id: 1, + secret: + { + "$binary": + { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06", + }, + }, + } + collectionName: *coll + databaseName: *db + - description: "Explicit encrypt using access token Azure credentials" + operations: + - name: encrypt + object: *clientEncryption + arguments: + value: "string0" + opts: + keyAltName: "my-key" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + expectResult: + { + "$binary": + { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06", + } + } diff --git a/source/client-side-encryption/tests/unified/accessToken-gcp.json b/source/client-side-encryption/tests/unified/accessToken-gcp.json new file mode 100644 index 0000000000..f5cf8914f2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/accessToken-gcp.json @@ -0,0 +1,188 @@ +{ + "description": "accessToken-gcp", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.6.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "accessToken": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "coll" + } + }, + { + "clientEncryption": { + "id": "clientEncryption", + "clientEncryptionOpts": { + "keyVaultClient": "client", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "accessToken": { + "$$placeholder": 1 + } + } + } + } + } + } + ], + "initialData": [ + { + "databaseName": "db", + "collectionName": "coll", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "properties": { + "secret": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": [ + "my-key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ] + } + ], + "tests": [ + { + "description": "Auto encrypt using access token GCP credentials", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "secret": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "secret": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ], + "collectionName": "coll", + "databaseName": "db" + } + ] + }, + { + "description": "Explicit encrypt using access token GCP credentials", + "operations": [ + { + "name": "encrypt", + "object": "clientEncryption", + "arguments": { + "value": "string0", + "opts": { + "keyAltName": "my-key", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "expectResult": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/accessToken-gcp.yml b/source/client-side-encryption/tests/unified/accessToken-gcp.yml new file mode 100644 index 0000000000..540b4ec619 --- /dev/null +++ b/source/client-side-encryption/tests/unified/accessToken-gcp.yml @@ -0,0 +1,115 @@ +description: accessToken-gcp +schemaVersion: "1.28" # For accessToken +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.6.0" # MONGOCRYPT-461. +createEntities: + - client: + id: &client "client" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { accessToken: { $$placeholder: 1 } } # Use csfle/setup-secrets.sh from drivers-evergreen-tools to obtain token. + - database: + id: &db "db" + client: *client + databaseName: *db + - collection: + id: &coll "coll" + database: *db + collectionName: *coll + - clientEncryption: + id: &clientEncryption clientEncryption + clientEncryptionOpts: + keyVaultClient: *client + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { accessToken: { $$placeholder: 1 } } # Use csfle/setup-secrets.sh from drivers-evergreen-tools to obtain token. +initialData: + - databaseName: *db + collectionName: *coll + documents: [] + createOptions: + validator: + $jsonSchema: { + "properties": + { + "secret": + { + "encrypt": + { + "keyId": ["$binary": { "base64": "GCP+AAAAAAAAAAAAAAAAAA==", "subType": "04" }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object" + } + - databaseName: keyvault + collectionName: datakeys + documents: + - { + "_id": { "$binary": { "base64": "GCP+AAAAAAAAAAAAAAAAAA==", "subType": "04" } }, + "keyAltNames": ["my-key"], + "keyMaterial": + { + "$binary": + { + "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": + { + "provider": "gcp", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle", + }, + } + +tests: + - description: "Auto encrypt using access token GCP credentials" + operations: + - name: insertOne + arguments: + document: { _id: 1, secret: "string0" } + object: *coll + outcome: + - documents: + - { + _id: 1, + secret: + { + "$binary": + { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06", + } + } + } + collectionName: *coll + databaseName: *db + - description: "Explicit encrypt using access token GCP credentials" + operations: + - name: encrypt + object: *clientEncryption + arguments: + value: "string0" + opts: + keyAltName: "my-key" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + expectResult: + { + "$binary": + { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06", + } + } diff --git a/source/client-side-encryption/tests/unified/addKeyAltName.json b/source/client-side-encryption/tests/unified/addKeyAltName.json new file mode 100644 index 0000000000..f70bc572a8 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/addKeyAltName.yml b/source/client-side-encryption/tests/unified/addKeyAltName.yml new file mode 100644 index 0000000000..1ddd20adc0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/addKeyAltName.yml @@ -0,0 +1,201 @@ +description: addKeyAltName + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - &local_key_doc + _id: &local_key_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: + # First 3 letters of local_key_id replaced with 'A' (value: "#alkeylocalkey"). + id: &non_existent_id { $binary: { base64: AAAjYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltName: new_key_alt_name + expectResult: { $$unsetOrMatches: null } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *non_existent_id } + update: { $addToSet: { keyAltNames: new_key_alt_name } } + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *local_key_doc + + - description: add new keyAltName to data key with no keyAltNames + operations: + - name: addKeyAltName + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: local_key + expectResult: *local_key_doc + - name: find + object: *collection0 + arguments: + filter: {} + projection: { _id: 0, keyAltNames: 1 } + expectResult: + - keyAltNames: [local_key] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + 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: *local_key_id + keyAltName: local_key + expectResult: *local_key_doc + - name: addKeyAltName + # Attempting to add a duplicate keyAltName to the data key should not be an error. + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: local_key + expectResult: + _id: *local_key_id + 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: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: { $addToSet: { keyAltNames: local_key } } + writeConcern: { w: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + 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: *local_key_id + keyAltName: local_key + expectResult: *local_key_doc + - name: addKeyAltName + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: another_name + expectResult: + _id: *local_key_id + keyAltNames: [local_key] + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: 1 + masterKey: + provider: local + - name: aggregate + object: *collection0 + arguments: + pipeline: + # Ensure keyAltNames are in deterministically sorted order. + - $project: { _id: 0, keyAltNames: $keyAltNames } + - $unwind: $keyAltNames + - $sort: { keyAltNames: 1 } + expectResult: + - keyAltNames: another_name + - keyAltNames: local_key + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: { $addToSet: { keyAltNames: local_key } } + writeConcern: { w: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: { $addToSet: { keyAltNames: another_name } } + writeConcern: { w: majority } + - commandStartedEvent: { commandName: aggregate } diff --git a/source/client-side-encryption/tests/unified/aggregate.json b/source/client-side-encryption/tests/unified/aggregate.json new file mode 100644 index 0000000000..d04ce49d28 --- /dev/null +++ b/source/client-side-encryption/tests/unified/aggregate.json @@ -0,0 +1,433 @@ +{ + "description": "aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Aggregate with deterministic encryption", + "skipReason": "SERVER-39395", + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encrypted_string": "457-55-5642" + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encrypted_string": "457-55-5642" + } + } + ] + }, + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "Aggregate with empty pipeline", + "skipReason": "SERVER-40829 hides agg support behind enableTestCommands flag.", + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [], + "cursor": {} + }, + "commandName": "aggregate" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Aggregate should fail with random encryption", + "skipReason": "SERVER-39395", + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "random": "abc" + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + }, + { + "description": "Database aggregate should fail", + "operations": [ + { + "name": "aggregate", + "object": "db", + "arguments": { + "pipeline": [ + { + "$currentOp": { + "allUsers": false, + "idleConnections": false, + "localOps": true + } + }, + { + "$match": { + "command.aggregate": { + "$eq": 1 + } + } + }, + { + "$project": { + "command": 1 + } + }, + { + "$project": { + "command.lsid": 0 + } + } + ] + }, + "expectError": { + "errorContains": "non-collection command not supported for auto encryption: aggregate" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/aggregate.yml b/source/client-side-encryption/tests/unified/aggregate.yml new file mode 100644 index 0000000000..ade00a40df --- /dev/null +++ b/source/client-side-encryption/tests/unified/aggregate.yml @@ -0,0 +1,221 @@ +description: aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Aggregate with deterministic encryption" + skipReason: "SERVER-39395" + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + outcome: + - documents: + - *doc0_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $match: { encrypted_string: "457-55-5642" } } + commandName: aggregate + - description: "Aggregate with empty pipeline" + skipReason: "SERVER-40829 hides agg support behind enableTestCommands flag." + operations: + - name: aggregate + arguments: + pipeline: [] + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + outcome: + - documents: + - *doc0_encrypted + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: [] + cursor: {} + commandName: aggregate + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - description: "Aggregate should fail with random encryption" + skipReason: "SERVER-39395" + operations: + - name: aggregate + arguments: + pipeline: + - { $match: { random: "abc" } } + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "Database aggregate should fail" + operations: + - name: aggregate + object: "db" + arguments: + pipeline: + - $currentOp: { allUsers: false, idleConnections: false, localOps: true } + - $match: { command.aggregate: { $eq: 1 } } + - $project: { command: 1 } + - $project: { command.lsid: 0 } + expectError: + errorContains: "non-collection command not supported for auto encryption: aggregate" diff --git a/source/client-side-encryption/tests/unified/awsTemporary.json b/source/client-side-encryption/tests/unified/awsTemporary.json new file mode 100644 index 0000000000..24b732a5eb --- /dev/null +++ b/source/client-side-encryption/tests/unified/awsTemporary.json @@ -0,0 +1,313 @@ +{ + "description": "awsTemporary", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + }, + "sessionToken": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client1", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + }, + "sessionToken": "bad" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db1", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll1", + "database": "db1", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Insert with invalid temporary credentials", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll1", + "expectError": { + "errorContains": "security token" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/awsTemporary.yml b/source/client-side-encryption/tests/unified/awsTemporary.yml new file mode 100644 index 0000000000..5fd20f1038 --- /dev/null +++ b/source/client-side-encryption/tests/unified/awsTemporary.yml @@ -0,0 +1,180 @@ +description: awsTemporary +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: + accessKeyId: { $$placeholder: 1 } + secretAccessKey: { $$placeholder: 1 } + sessionToken: { $$placeholder: 1 } # Indicates use of temporary + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - client: + id: "client1" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: + accessKeyId: { $$placeholder: 1 } + secretAccessKey: { $$placeholder: 1 } + sessionToken: "bad" + observeEvents: + - commandStartedEvent + - database: + id: "db1" + client: "client1" + databaseName: default + - collection: + id: "coll1" + database: "db1" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + object: "coll" + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + $or: + [ + _id: { $in: ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + keyAltNames: { $in: [] }, + ], + } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - description: "Insert with invalid temporary credentials" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll1" + expectError: + errorContains: "security token" diff --git a/source/client-side-encryption/tests/unified/azureKMS.json b/source/client-side-encryption/tests/unified/azureKMS.json new file mode 100644 index 0000000000..b70959217f --- /dev/null +++ b/source/client-side-encryption/tests/unified/azureKMS.json @@ -0,0 +1,293 @@ +{ + "description": "azureKMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_azure": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AZURE+AAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_azure": { + "$binary": { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/azureKMS.yml b/source/client-side-encryption/tests/unified/azureKMS.yml new file mode 100644 index 0000000000..8c36fc7deb --- /dev/null +++ b/source/client-side-encryption/tests/unified/azureKMS.yml @@ -0,0 +1,164 @@ +description: azureKMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + }, + }, + "encrypted_string_kmip_delegated": + { + "encrypt": + { + "keyId": ["$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6"], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string_azure: "string0" } + object: "coll" + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string_azure: + { + "$binary": + { + "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + $or: + [ + _id: { $in: ["$binary": { "base64": "AZURE+AAAAAAAAAAAAAAAA==", "subType": "04" }] }, + keyAltNames: { $in: [] }, + ], + } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/badQueries.json b/source/client-side-encryption/tests/unified/badQueries.json new file mode 100644 index 0000000000..7a4f30d5b7 --- /dev/null +++ b/source/client-side-encryption/tests/unified/badQueries.json @@ -0,0 +1,1393 @@ +{ + "description": "badQueries", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "collection": { + "id": "coll_with_encrypted_id", + "database": "db", + "collectionName": "coll_with_encrypted_id" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "default", + "collectionName": "coll_with_encrypted_id", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "properties": { + "_id": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + } + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "$text unconditionally fails", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "$text": { + "$search": "search text" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Unsupported match expression operator for encryption" + } + } + ] + }, + { + "description": "$where unconditionally fails", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "$where": { + "$code": "function() { return true }" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Unsupported match expression operator for encryption" + } + } + ] + }, + { + "description": "$bit operators succeed on unencrypted, error on encrypted", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAllClear": 35 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAllClear": 35 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAllSet": 35 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAllSet": 35 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAnyClear": 35 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAnyClear": 35 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$bitsAnySet": 35 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$bitsAnySet": 35 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "geo operators succeed on unencrypted, error on encrypted", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$near": [ + 0, + 0 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "unable to find index" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$near": [ + 0, + 0 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$nearSphere": [ + 0, + 0 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "unable to find index" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$nearSphere": [ + 0, + 0 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$geoIntersects": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$geoIntersects": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$geoWithin": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$geoWithin": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 1, + 0 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ] + } + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "inequality operators succeed on unencrypted, error on encrypted", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$gt": 1 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$gt": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$lt": 1 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$lt": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$gte": 1 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$gte": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$lte": 1 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$lte": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + } + ] + }, + { + "description": "other misc operators succeed on unencrypted, error on encrypted", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$mod": [ + 3, + 1 + ] + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$mod": [ + 3, + 1 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$regex": "pattern", + "$options": "" + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$regex": "pattern", + "$options": "" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$size": 2 + } + } + }, + "object": "coll", + "expectResult": [] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$size": 2 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Invalid match expression operator on encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$eq": null + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$eq": null + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Illegal equality to null predicate for encrypted field" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "unencrypted": { + "$in": [ + null + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + null + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Illegal equality to null inside $in against an encrypted field" + } + } + ] + }, + { + "description": "$addToSet succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$addToSet": { + "unencrypted": [ + "a" + ] + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$addToSet": { + "encrypted_string": [ + "a" + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$addToSet not allowed on encrypted values" + } + } + ] + }, + { + "description": "$inc succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$inc and $mul not allowed on encrypted values" + } + } + ] + }, + { + "description": "$mul succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$mul": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$mul": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$inc and $mul not allowed on encrypted values" + } + } + ] + }, + { + "description": "$max succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$max": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$max": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$max and $min not allowed on encrypted values" + } + } + ] + }, + { + "description": "$min succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$min": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$min": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$max and $min not allowed on encrypted values" + } + } + ] + }, + { + "description": "$currentDate succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$currentDate": { + "unencrypted": true + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$currentDate": { + "encrypted_string": true + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$currentDate not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pop succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pop": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pop": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$pop not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pull succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pull": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pull": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$pull not allowed on encrypted values" + } + } + ] + }, + { + "description": "$pullAll succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pullAll": { + "unencrypted": [ + 1 + ] + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$pullAll": { + "encrypted_string": [ + 1 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$pullAll not allowed on encrypted values" + } + } + ] + }, + { + "description": "$push succeeds on unencrypted, error on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$push": { + "unencrypted": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$push": { + "encrypted_string": 1 + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$push not allowed on encrypted values" + } + } + ] + }, + { + "description": "array filters on encrypted fields does not error in mongocryptd, but errors in mongod", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_string.$[i].x": 1 + } + }, + "arrayFilters": [ + { + "i.x": 1 + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "Array update operations not allowed on encrypted values" + } + } + ] + }, + { + "description": "positional operator succeeds on unencrypted, errors on encrypted", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "unencrypted": 1 + }, + "update": { + "$set": { + "unencrypted.$": 1 + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0 + } + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encrypted_string": "abc" + }, + "update": { + "$set": { + "encrypted_string.$": "abc" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt fields below '$' positional update operator" + } + } + ] + }, + { + "description": "an update that would produce an array on an encrypted field errors", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_string": [ + 1, + 2 + ] + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element of type" + } + } + ] + }, + { + "description": "an insert with encrypted field on _id errors", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + }, + "object": "coll_with_encrypted_id", + "expectError": { + "errorContains": "Invalid schema containing the 'encrypt' keyword." + } + } + ] + }, + { + "description": "an insert with an array value for an encrypted field fails", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "encrypted_string": [ + "123", + "456" + ] + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element of type" + } + } + ] + }, + { + "description": "an insert with a Timestamp(0,0) value in the top-level fails", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "random": { + "$timestamp": { + "t": 0, + "i": 0 + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" + } + } + ] + }, + { + "description": "distinct with the key referring to a field where the keyID is a JSON Pointer errors", + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": {}, + "fieldName": "encrypted_w_altname" + }, + "object": "coll", + "expectError": { + "errorContains": "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/badQueries.yml b/source/client-side-encryption/tests/unified/badQueries.yml new file mode 100644 index 0000000000..3dda7628b4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/badQueries.yml @@ -0,0 +1,640 @@ +description: badQueries +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - collection: + id: coll_with_encrypted_id + database: db + collectionName: coll_with_encrypted_id +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: default + collectionName: coll_with_encrypted_id + documents: [] + createOptions: + validator: + $jsonSchema: + { + "properties": + { + "_id": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "$text unconditionally fails" + operations: + - name: find + arguments: + filter: { $text: { $search: "search text" } } + object: "coll" + expectError: + errorContains: "Unsupported match expression operator for encryption" + - description: "$where unconditionally fails" + operations: + - name: find + arguments: + filter: { $where: { $code: "function() { return true }" } } + object: "coll" + expectError: + errorContains: "Unsupported match expression operator for encryption" + - description: "$bit operators succeed on unencrypted, error on encrypted" + operations: + - name: find + arguments: + filter: { unencrypted: { $bitsAllClear: 35 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllClear: 35 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAllSet: 35 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAllSet: 35 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnyClear: 35 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnyClear: 35 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $bitsAnySet: 35 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $bitsAnySet: 35 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - description: "geo operators succeed on unencrypted, error on encrypted" + operations: + - name: find + arguments: + filter: { unencrypted: { $near: [0, 0] } } + object: "coll" + expectError: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $near: [0, 0] } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $nearSphere: [0, 0] } } + object: "coll" + expectError: + # Still an error because no geo index, but from mongod - not mongocryptd. + errorContains: "unable to find index" + - name: find + arguments: + filter: { encrypted_string: { $nearSphere: [0, 0] } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: + { + unencrypted: + { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] } } }, + } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: + { + encrypted_string: + { $geoIntersects: { $geometry: { type: "Polygon", coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] } } }, + } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: + { + unencrypted: + { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] } } }, + } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: + { + encrypted_string: + { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] } } }, + } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - description: "inequality operators succeed on unencrypted, error on encrypted" + operations: + - name: find + arguments: + filter: { unencrypted: { $gt: 1 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $gt: 1 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lt: 1 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $lt: 1 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $gte: 1 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $gte: 1 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $lte: 1 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $lte: 1 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - description: "other misc operators succeed on unencrypted, error on encrypted" + operations: + - name: find + arguments: + filter: { unencrypted: { $mod: [3, 1] } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $mod: [3, 1] } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $regex: "pattern", $options: "" } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $regex: "pattern", $options: "" } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $size: 2 } } + object: "coll" + expectResult: [] + - name: find + arguments: + filter: { encrypted_string: { $size: 2 } } + object: "coll" + expectError: + errorContains: "Invalid match expression operator on encrypted field" + - name: find + arguments: + filter: { unencrypted: { $eq: null } } + object: "coll" + expectResult: + - &doc0 { _id: 1, encrypted_string: "string0" } + - &doc1 { _id: 2, encrypted_string: "string1" } + - name: find + arguments: + filter: { encrypted_string: { $eq: null } } + object: "coll" + expectError: + errorContains: "Illegal equality to null predicate for encrypted field" + - name: find + arguments: + filter: { unencrypted: { $in: [null] } } + object: "coll" + expectResult: + - *doc0 + - *doc1 + - name: find + arguments: + filter: { encrypted_string: { $in: [null] } } + object: "coll" + expectError: + errorContains: "Illegal equality to null inside $in against an encrypted field" + - description: "$addToSet succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $addToSet: { "unencrypted": ["a"] } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $addToSet: { "encrypted_string": ["a"] } } + object: "coll" + expectError: + errorContains: "$addToSet not allowed on encrypted values" + - description: "$inc succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $inc: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $inc: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$mul succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $mul: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $mul: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$inc and $mul not allowed on encrypted values" + - description: "$max succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $max: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $max: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$min succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $min: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $min: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$max and $min not allowed on encrypted values" + - description: "$currentDate succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $currentDate: { "unencrypted": true } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $currentDate: { "encrypted_string": true } } + object: "coll" + expectError: + errorContains: "$currentDate not allowed on encrypted values" + - description: "$pop succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $pop: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $pop: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$pop not allowed on encrypted values" + - description: "$pull succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $pull: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $pull: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$pull not allowed on encrypted values" + - description: "$pullAll succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $pullAll: { "unencrypted": [1] } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $pullAll: { "encrypted_string": [1] } } + object: "coll" + expectError: + errorContains: "$pullAll not allowed on encrypted values" + - description: "$push succeeds on unencrypted, error on encrypted" + operations: + - name: updateOne + arguments: + filter: {} + update: { $push: { "unencrypted": 1 } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: updateOne + arguments: + filter: {} + update: { $push: { "encrypted_string": 1 } } + object: "coll" + expectError: + errorContains: "$push not allowed on encrypted values" + - description: "array filters on encrypted fields does not error in mongocryptd, but errors in mongod" + operations: + - name: updateOne + arguments: + filter: {} + update: { $set: { "encrypted_string.$[i].x": 1 } } + arrayFilters: [i.x: 1] + object: "coll" + expectError: + errorContains: "Array update operations not allowed on encrypted values" + - description: "positional operator succeeds on unencrypted, errors on encrypted" + operations: + - name: updateOne + arguments: + filter: { "unencrypted": 1 } + update: { $set: { "unencrypted.$": 1 } } + object: "coll" + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + - name: updateOne + arguments: + filter: { "encrypted_string": "abc" } + update: { $set: { "encrypted_string.$": "abc" } } + object: "coll" + expectError: + errorContains: "Cannot encrypt fields below '$' positional update operator" + - description: "an update that would produce an array on an encrypted field errors" + operations: + - name: updateOne + arguments: + filter: {} + update: { $set: { "encrypted_string": [1, 2] } } + object: "coll" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with encrypted field on _id errors" + operations: + - name: insertOne + arguments: + document: { _id: 1 } + object: "coll_with_encrypted_id" + expectError: + errorContains: "Invalid schema containing the 'encrypt' keyword." + - description: "an insert with an array value for an encrypted field fails" + operations: + - name: insertOne + arguments: + document: { encrypted_string: ["123", "456"] } + object: "coll" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot encrypt element of type array because schema requires that type is one of: [ string ]" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common prefix. + errorContains: "Cannot encrypt element of type" + - description: "an insert with a Timestamp(0,0) value in the top-level fails" + operations: + - name: insertOne + arguments: + document: { random: { "$timestamp": { "t": 0, "i": 0 } } } + object: "coll" + expectError: + errorContains: "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" + - description: "distinct with the key referring to a field where the keyID is a JSON Pointer errors" + operations: + - name: distinct + arguments: + filter: {} + fieldName: "encrypted_w_altname" + object: "coll" + expectError: + errorContains: "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" diff --git a/source/client-side-encryption/tests/unified/badSchema.json b/source/client-side-encryption/tests/unified/badSchema.json new file mode 100644 index 0000000000..af93d659d4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/badSchema.json @@ -0,0 +1,393 @@ +{ + "description": "badSchema", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + }, + "bsonType": "array" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db0", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll0", + "database": "db0", + "collectionName": "default" + } + }, + { + "client": { + "id": "client1", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "foo": { + "properties": { + "bar": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + } + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db1", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll1", + "database": "db1", + "collectionName": "default" + } + }, + { + "client": { + "id": "client2", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + }, + "bsonType": "object" + } + } + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db2", + "client": "client2", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll2", + "database": "db2", + "collectionName": "default" + } + }, + { + "client": { + "id": "client3", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "anyOf": [ + { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + } + } + } + ] + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db3", + "client": "client3", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll3", + "database": "db3", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Schema with an encrypted field in an array", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll0", + "expectError": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ] + }, + { + "description": "Schema without specifying parent object types", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll1", + "expectError": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ] + }, + { + "description": "Schema with siblings of encrypt document", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll2", + "expectError": { + "errorContains": "'encrypt' cannot be used in conjunction with 'bsonType'" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ] + }, + { + "description": "Schema with logical keywords", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll3", + "expectError": { + "errorContains": "Invalid schema" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/badSchema.yml b/source/client-side-encryption/tests/unified/badSchema.yml new file mode 100644 index 0000000000..14ee624a7e --- /dev/null +++ b/source/client-side-encryption/tests/unified/badSchema.yml @@ -0,0 +1,227 @@ +description: badSchema +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_string": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + }, + }, + }, + "bsonType": "array", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db0" + client: "client0" + databaseName: default + - collection: + id: "coll0" + database: "db0" + collectionName: default + - client: + id: "client1" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "foo": + { + "properties": + { + "bar": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + }, + }, + }, + }, + }, + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db1" + client: "client1" + databaseName: default + - collection: + id: "coll1" + database: "db1" + collectionName: default + - client: + id: "client2" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_string": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + }, + "bsonType": "object", + }, + }, + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db2" + client: "client2" + databaseName: default + - collection: + id: "coll2" + database: "db2" + collectionName: default + - client: + id: "client3" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "anyOf": + [ + { + "properties": + { + "encrypted_string": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", + }, + }, + }, + }, + ], + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db3" + client: "client3" + databaseName: default + - collection: + id: "coll3" + database: "db3" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Schema with an encrypted field in an array" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + object: "coll0" + expectError: + errorContains: "Invalid schema" + outcome: + - documents: [] + collectionName: &collection_name "default" + databaseName: &database_name "default" + - description: "Schema without specifying parent object types" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll1" + expectError: + errorContains: "Invalid schema" + outcome: + - documents: [] + collectionName: *collection_name + databaseName: *database_name + - description: "Schema with siblings of encrypt document" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll2" + expectError: + errorContains: "'encrypt' cannot be used in conjunction with 'bsonType'" + outcome: + - documents: [] + collectionName: *collection_name + databaseName: *database_name + - description: "Schema with logical keywords" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll3" + expectError: + errorContains: "Invalid schema" + outcome: + - documents: [] + collectionName: *collection_name + databaseName: *database_name diff --git a/source/client-side-encryption/tests/unified/basic.json b/source/client-side-encryption/tests/unified/basic.json new file mode 100644 index 0000000000..5522f585da --- /dev/null +++ b/source/client-side-encryption/tests/unified/basic.json @@ -0,0 +1,431 @@ +{ + "description": "basic", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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 with deterministic encryption, then find it", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Insert with randomized encryption, then find it", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "random": "123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "random": "123" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/basic.yml b/source/client-side-encryption/tests/unified/basic.yml new file mode 100644 index 0000000000..689d66a6f6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/basic.yml @@ -0,0 +1,226 @@ +description: basic +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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 with deterministic encryption, then find it" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + object: "coll" + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: [*doc0] + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "Insert with randomized encryption, then find it" + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, random: "123" } + object: "coll" + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { _id: 1, random: { $$type: "binData" } } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1, random: { $$type: "binData" } } + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find diff --git a/source/client-side-encryption/tests/unified/bulk.json b/source/client-side-encryption/tests/unified/bulk.json new file mode 100644 index 0000000000..90922b88d0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bulk.json @@ -0,0 +1,407 @@ +{ + "description": "bulk", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Bulk write with encryption", + "operations": [ + { + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "encrypted_string": "string1" + } + } + }, + { + "updateOne": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + } + }, + { + "deleteOne": { + "filter": { + "$and": [ + { + "encrypted_string": "string1" + }, + { + "_id": 2 + } + ] + } + } + } + ], + "ordered": true + }, + "object": "coll" + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + }, + "multi": false, + "upsert": false + } + ], + "ordered": true + }, + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "$and": [ + { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + }, + { + "_id": { + "$eq": 2 + } + } + ] + }, + "limit": 1 + } + ], + "ordered": true + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/bulk.yml b/source/client-side-encryption/tests/unified/bulk.yml new file mode 100644 index 0000000000..c8923a8fb2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bulk.yml @@ -0,0 +1,258 @@ +description: bulk +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Bulk write with encryption" + operations: + - name: bulkWrite + arguments: + requests: + - insertOne: + document: { _id: 1, encrypted_string: "string0", random: "abc" } + - insertOne: + document: { _id: 2, encrypted_string: "string1" } + - updateOne: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + - deleteOne: + filter: { $and: [encrypted_string: "string1", _id: 2] } + ordered: true + object: "coll" + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: &collection_name default + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + - { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + ordered: true + commandName: insert + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + u: + { + $set: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + }, + } + multi: false + upsert: false + ordered: true + commandName: update + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: + { + "$and": + [ + "encrypted_string": + { + "$eq": + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + }, + "_id": { "$eq": 2 }, + ], + } + limit: 1 + ordered: true + commandName: delete diff --git a/source/client-side-encryption/tests/unified/bypassAutoEncryption.json b/source/client-side-encryption/tests/unified/bypassAutoEncryption.json new file mode 100644 index 0000000000..3254c43781 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bypassAutoEncryption.json @@ -0,0 +1,403 @@ +{ + "description": "bypassAutoEncryption", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "bypassAutoEncryption": true, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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 with bypassAutoEncryption", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "encrypted_string": "string0" + }, + "bypassDocumentValidation": true + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {} + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": {} + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Insert with bypassAutoEncryption for local schema", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "encrypted_string": "string0" + }, + "bypassDocumentValidation": true + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {} + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 2, + "encrypted_string": "string0" + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": {} + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/bypassAutoEncryption.yml b/source/client-side-encryption/tests/unified/bypassAutoEncryption.yml new file mode 100644 index 0000000000..095a519194 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bypassAutoEncryption.yml @@ -0,0 +1,233 @@ +description: bypassAutoEncryption +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + bypassAutoEncryption: true + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + [ + { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + ] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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 with bypassAutoEncryption" + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + object: "coll" + - name: find + arguments: + filter: {} + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + outcome: + - documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { _id: 2, encrypted_string: "string0" } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: {} + commandName: find + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - description: "Insert with bypassAutoEncryption for local schema" + operations: + - name: insertOne + arguments: + document: { _id: 2, encrypted_string: "string0" } + bypassDocumentValidation: true + object: "coll" + - name: find + arguments: + filter: {} + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string0" } + outcome: + - documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { _id: 2, encrypted_string: "string0" } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + # No encryption. + - { _id: 2, encrypted_string: "string0" } + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: {} + commandName: find + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find diff --git a/source/client-side-encryption/tests/unified/bypassedCommand.json b/source/client-side-encryption/tests/unified/bypassedCommand.json new file mode 100644 index 0000000000..b0c4c56322 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bypassedCommand.json @@ -0,0 +1,147 @@ +{ + "description": "bypassedCommand", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "ping is bypassed", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ] + } + ] + }, + { + "description": "kill op is not bypassed", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "killOp": 1, + "op": 1234 + }, + "commandName": "killOp" + }, + "expectError": { + "errorContains": "command not supported for auto encryption: killOp" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/bypassedCommand.yml b/source/client-side-encryption/tests/unified/bypassedCommand.yml new file mode 100644 index 0000000000..b23e4812d0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/bypassedCommand.yml @@ -0,0 +1,81 @@ +description: bypassedCommand +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "ping is bypassed" + operations: + - name: runCommand + object: "db" + arguments: + command: + ping: 1 + commandName: ping + expectEvents: + - client: "client0" + events: + # No listCollections, no mongocryptd command, just the ping. + - commandStartedEvent: + command: + ping: 1 + commandName: ping + - description: "kill op is not bypassed" + operations: + - name: runCommand + object: "db" + arguments: + command: + killOp: 1 + op: 1234 + commandName: killOp + expectError: + errorContains: "command not supported for auto encryption: killOp" diff --git a/source/client-side-encryption/tests/unified/client-bulkWrite-qe.json b/source/client-side-encryption/tests/unified/client-bulkWrite-qe.json new file mode 100644 index 0000000000..9ed46025a6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/client-bulkWrite-qe.json @@ -0,0 +1,305 @@ +{ + "description": "client bulkWrite with queryable encryption", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid", + "csfle": { + "minLibmongocryptVersion": "1.10.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ], + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "client": { + "id": "client1", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "datakeys" + } + }, + { + "database": { + "id": "database2", + "client": "client1", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection2", + "database": "database2", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyAltNames": [ + "local_key" + ], + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + }, + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + } + ] + } + } + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite QE replaceOne", + "operations": [ + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1, + "encryptedInt": 11 + }, + { + "_id": 2, + "encryptedInt": 22 + }, + { + "_id": 3, + "encryptedInt": 33 + } + ] + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "encryptedInt": { + "$eq": 11 + } + }, + "replacement": { + "encryptedInt": 44 + } + } + } + ] + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0 + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "encryptedInt": 44 + } + }, + "expectResult": [ + { + "_id": 1, + "encryptedInt": 44 + } + ] + }, + { + "object": "collection2", + "name": "find", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": { + "$$type": "array" + } + }, + { + "_id": 2, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": { + "$$type": "array" + } + }, + { + "_id": 3, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": { + "$$type": "array" + } + } + ] + } + ] + }, + { + "description": "client bulkWrite QE with multiple replace fails", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "encryptedInt": { + "$eq": 11 + } + }, + "replacement": { + "encryptedInt": 44 + } + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "encryptedInt": { + "$eq": 22 + } + }, + "replacement": { + "encryptedInt": 44 + } + } + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Only insert is supported in BulkWrite with multiple operations and Queryable Encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/client-bulkWrite-qe.yml b/source/client-side-encryption/tests/unified/client-bulkWrite-qe.yml new file mode 100644 index 0000000000..1c7adadae0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/client-bulkWrite-qe.yml @@ -0,0 +1,130 @@ +description: client bulkWrite with queryable encryption + +schemaVersion: "1.25" + +runOnRequirements: + - minServerVersion: "8.0" + topologies: ["replicaset", "sharded", "load-balanced"] # QE does not support standalone. + serverless: forbid # Serverless does not support bulkWrite: CLOUDP-256344. + csfle: + minLibmongocryptVersion: "1.10.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - client: + id: &client1 client1 + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client0 + databaseName: &database1Name keyvault + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name datakeys + - database: + id: &database2 database2 + client: *client1 + databaseName: &database0Name crud-tests + - collection: + id: &collection2 collection2 + database: *database2 + collectionName: &collection0Name coll0 + + +initialData: + - databaseName: *database1Name + collectionName: *collection1Name + documents: + - _id: &local_key_id { $binary: { base64: EjRWeBI0mHYSNBI0VniQEg==, subType: "04" } } + keyAltNames: ["local_key"] + keyMaterial: { $binary: { base64: sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &local_masterkey + provider: local + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + createOptions: + encryptedFields: &encrypted_fields {'fields': [{'keyId': {'$binary': {'base64': 'EjRWeBI0mHYSNBI0VniQEg==', 'subType': '04'}}, 'path': 'encryptedInt', 'bsonType': 'int', 'queries': {'queryType': 'equality', 'contention': {'$numberLong': '0'}}}]} + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: client bulkWrite QE replaceOne + operations: + - object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 1, encryptedInt: 11 } + - { _id: 2, encryptedInt: 22 } + - { _id: 3, encryptedInt: 33 } + - object: *client0 + name: clientBulkWrite + arguments: + models: + - replaceOne: + namespace: *namespace + filter: { encryptedInt: { $eq: 11 } } + replacement: { encryptedInt: 44 } + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + - object: *collection0 + name: find + arguments: + filter: { encryptedInt: 44 } + expectResult: + - _id: 1 + encryptedInt: 44 + - object: *collection2 + name: find + arguments: + filter: {} + expectResult: + - { _id: 1, encryptedInt: { $$type: binData }, __safeContent__: { $$type: array} } + - { _id: 2, encryptedInt: { $$type: binData }, __safeContent__: { $$type: array} } + - { _id: 3, encryptedInt: { $$type: binData }, __safeContent__: { $$type: array} } + - description: client bulkWrite QE with multiple replace fails + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - replaceOne: + namespace: *namespace + filter: { encryptedInt: { $eq: 11 } } + replacement: { encryptedInt: 44 } + - replaceOne: + namespace: *namespace + filter: { encryptedInt: { $eq: 22 } } + replacement: { encryptedInt: 44 } + expectError: + # Expect error from mongocryptd or crypt_shared + isError: true + errorContains: "Only insert is supported in BulkWrite with multiple operations and Queryable Encryption" diff --git a/source/client-side-encryption/tests/unified/count.json b/source/client-side-encryption/tests/unified/count.json new file mode 100644 index 0000000000..d44b3e827d --- /dev/null +++ b/source/client-side-encryption/tests/unified/count.json @@ -0,0 +1,293 @@ +{ + "description": "count", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Count with deterministic encryption", + "operations": [ + { + "name": "countDocuments", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "object": "coll", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "cursor": {}, + "pipeline": [ + { + "$match": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + { + "$group": { + "_id": { + "$const": 1 + }, + "n": { + "$sum": { + "$const": 1 + } + } + } + } + ] + }, + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "Count fails when filtering on a random encrypted field", + "operations": [ + { + "name": "countDocuments", + "arguments": { + "filter": { + "random": "abc" + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/count.yml b/source/client-side-encryption/tests/unified/count.yml new file mode 100644 index 0000000000..ac66d7b1c1 --- /dev/null +++ b/source/client-side-encryption/tests/unified/count.yml @@ -0,0 +1,185 @@ +description: count +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Count with deterministic encryption" + operations: + - name: countDocuments + arguments: + filter: { encrypted_string: "string0" } + object: "coll" + expectResult: 2 + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: &collection_name "default" + + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + aggregate: default + cursor: {} + pipeline: + - $match: + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - $group: + _id: + $const: 1 + n: + $sum: + $const: 1 + commandName: aggregate + - description: "Count fails when filtering on a random encrypted field" + operations: + - name: countDocuments + arguments: + filter: { random: "abc" } + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" diff --git a/source/client-side-encryption/tests/unified/countDocuments.json b/source/client-side-encryption/tests/unified/countDocuments.json new file mode 100644 index 0000000000..c0202258b8 --- /dev/null +++ b/source/client-side-encryption/tests/unified/countDocuments.json @@ -0,0 +1,296 @@ +{ + "description": "countDocuments", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "countDocuments with deterministic encryption", + "skipReason": "waiting on SERVER-39395", + "operations": [ + { + "name": "countDocuments", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "object": "coll", + "expectResult": 1 + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/countDocuments.yml b/source/client-side-encryption/tests/unified/countDocuments.yml new file mode 100644 index 0000000000..277130dbf5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/countDocuments.yml @@ -0,0 +1,178 @@ +description: countDocuments +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "countDocuments with deterministic encryption" + skipReason: "waiting on SERVER-39395" + operations: + - name: countDocuments + arguments: + filter: { encrypted_string: "string0" } + object: "coll" + expectResult: 1 + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { + $match: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + - { $group: { _id: 1, n: { $sum: 1 } } } + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/create-and-createIndexes.json b/source/client-side-encryption/tests/unified/create-and-createIndexes.json new file mode 100644 index 0000000000..5debd15945 --- /dev/null +++ b/source/client-side-encryption/tests/unified/create-and-createIndexes.json @@ -0,0 +1,121 @@ +{ + "description": "create-and-createIndexes", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "unencryptedCollection" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "unencryptedCollection", + "documents": [] + } + ], + "tests": [ + { + "description": "create is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "unencryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "unencryptedCollection" + } + } + ] + }, + { + "description": "createIndexes is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "unencryptedCollection" + } + }, + { + "name": "createIndex", + "object": "coll", + "arguments": { + "keys": { + "x": 1 + }, + "name": "name" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "unencryptedCollection", + "indexName": "name" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/create-and-createIndexes.yml b/source/client-side-encryption/tests/unified/create-and-createIndexes.yml new file mode 100644 index 0000000000..c31f7ef970 --- /dev/null +++ b/source/client-side-encryption/tests/unified/create-and-createIndexes.yml @@ -0,0 +1,69 @@ +description: create-and-createIndexes +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: unencryptedCollection +initialData: + - databaseName: default + collectionName: unencryptedCollection + documents: [] +tests: + - description: "create is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "unencryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "unencryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: &database_name "default" + collectionName: "unencryptedCollection" + - description: "createIndexes is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "unencryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "unencryptedCollection" + - name: createIndex + object: "coll" + arguments: + keys: { x: 1 } + name: "name" + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: "unencryptedCollection" + indexName: name diff --git a/source/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json b/source/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.json new file mode 100644 index 0000000000..2344a61a95 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml b/source/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml new file mode 100644 index 0000000000..fee5a823f2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/createDataKey-kms_providers-invalid.yml @@ -0,0 +1,67 @@ +description: createDataKey-kms_providers-invalid + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - clientEncryption: + id: &clientEncryption0 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/source/client-side-encryption/tests/unified/createDataKey.json b/source/client-side-encryption/tests/unified/createDataKey.json new file mode 100644 index 0000000000..f99fa3dbcf --- /dev/null +++ b/source/client-side-encryption/tests/unified/createDataKey.json @@ -0,0 +1,775 @@ +{ + "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 KMIP delegated KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip", + "opts": { + "masterKey": { + "delegated": true + } + } + }, + "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" + }, + "delegated": true + } + } + ], + "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/source/client-side-encryption/tests/unified/createDataKey.yml b/source/client-side-encryption/tests/unified/createDataKey.yml new file mode 100644 index 0000000000..2d27827b07 --- /dev/null +++ b/source/client-side-encryption/tests/unified/createDataKey.yml @@ -0,0 +1,352 @@ +description: createDataKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 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 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: create data key with AWS KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: aws + opts: + masterKey: &new_aws_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: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: aws + <<: *new_aws_masterkey + writeConcern: { w: majority } + + - description: create datakey with Azure KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: azure + opts: + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: azure + <<: *new_azure_masterkey + writeConcern: { w: majority } + + - description: create datakey with GCP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: gcp + opts: + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: gcp + <<: *new_gcp_masterkey + 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: *database0Name + command: + insert: *collection0Name + 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 KMIP delegated KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: kmip + opts: + masterKey: &new_kmip_masterkey + delegated: true + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: kmip + keyId: { $$type: string } + delegated: true + 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: *database0Name + command: + insert: *collection0Name + 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: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + # keyAltNames field should not exist if no keyAltNames are given. + 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: *database0Name + command: + insert: *collection0Name + 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: + # Need to use pipeline to sort keyAltNames for deterministic matching + # because keyAltNames is not required to be sorted. + pipeline: + - $project: { _id: 0, keyAltNames: 1 } + - $unwind: $keyAltNames + - $sort: { keyAltNames: 1 } + expectResult: + - keyAltNames: abc + - keyAltNames: def + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + 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: + # "key_material" repeated 8 times. + keyMaterial: + &custom_key_material { + $binary: + { + base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, + subType: "00", + }, + } + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + # Cannot match exact value of encrypted key material. + 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: + # "key_material" repeated only 7 times (key material length == 84). + keyMaterial: + { + $binary: + { + base64: a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs, + subType: "00", + }, + } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] diff --git a/source/client-side-encryption/tests/unified/delete.json b/source/client-side-encryption/tests/unified/delete.json new file mode 100644 index 0000000000..242bcdba8c --- /dev/null +++ b/source/client-side-encryption/tests/unified/delete.json @@ -0,0 +1,396 @@ +{ + "description": "delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "deleteOne with deterministic encryption", + "operations": [ + { + "name": "deleteOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "limit": 1 + } + ], + "ordered": true + }, + "commandName": "delete" + } + } + ] + } + ] + }, + { + "description": "deleteMany with deterministic encryption", + "operations": [ + { + "name": "deleteMany", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 2 + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + }, + "limit": 0 + } + ], + "ordered": true + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/delete.yml b/source/client-side-encryption/tests/unified/delete.yml new file mode 100644 index 0000000000..0b2f8e782e --- /dev/null +++ b/source/client-side-encryption/tests/unified/delete.yml @@ -0,0 +1,241 @@ +description: delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "deleteOne with deterministic encryption" + operations: + - name: deleteOne + arguments: + filter: { encrypted_string: "string0" } + object: "coll" + expectResult: + deletedCount: 1 + outcome: + - documents: + - *doc1_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + limit: 1 + ordered: true + commandName: delete + - description: "deleteMany with deterministic encryption" + operations: + - name: deleteMany + arguments: + filter: { encrypted_string: { $in: ["string0", "string1"] } } + object: "coll" + expectResult: + deletedCount: 2 + outcome: + - documents: [] + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: + { + encrypted_string: + { + $in: + [ + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + ], + }, + } + limit: 0 + ordered: true + commandName: delete diff --git a/source/client-side-encryption/tests/unified/deleteKey.json b/source/client-side-encryption/tests/unified/deleteKey.json new file mode 100644 index 0000000000..3a10fb082f --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/deleteKey.yml b/source/client-side-encryption/tests/unified/deleteKey.yml new file mode 100644 index 0000000000..b212eb563c --- /dev/null +++ b/source/client-side-encryption/tests/unified/deleteKey.yml @@ -0,0 +1,173 @@ +description: deleteKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - &aws_key_doc + _id: &aws_key_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 + - &local_key_doc + _id: &local_key_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: + # *aws_key_id with first three letters replaced with 'A' (value: "3awsawsawsawsa"). + id: &non_existent_id { $binary: { base64: AAAzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + expectResult: + deletedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + delete: *collection0Name + deletes: [{ q: { _id: *non_existent_id }, limit: 1 }] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *aws_key_doc + - *local_key_doc + + - description: delete existing AWS data key + operations: + - name: deleteKey + object: *clientEncryption0 + arguments: + id: *aws_key_id + expectResult: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + delete: *collection0Name + deletes: [{ q: { _id: *aws_key_id }, limit: 1 }] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *local_key_doc + + - description: delete existing local data key + operations: + - name: deleteKey + object: *clientEncryption0 + arguments: + id: *local_key_id + expectResult: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + delete: *collection0Name + deletes: [{ q: { _id: *local_key_id }, limit: 1 }] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *aws_key_doc + + - description: delete existing data key twice + operations: + - name: deleteKey + object: *clientEncryption0 + arguments: + id: *aws_key_id + expectResult: + deletedCount: 1 + - name: deleteKey + object: *clientEncryption0 + arguments: + id: *aws_key_id + expectResult: + deletedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + delete: *collection0Name + deletes: [{ q: { _id: *aws_key_id }, limit: 1 }] + writeConcern: { w: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + delete: *collection0Name + deletes: [{ q: { _id: *aws_key_id }, limit: 1 }] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *local_key_doc diff --git a/source/client-side-encryption/tests/unified/distinct.json b/source/client-side-encryption/tests/unified/distinct.json new file mode 100644 index 0000000000..a7ac0fc7f1 --- /dev/null +++ b/source/client-side-encryption/tests/unified/distinct.json @@ -0,0 +1,325 @@ +{ + "description": "distinct", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "distinct with deterministic encryption", + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "fieldName": "encrypted_string" + }, + "object": "coll", + "expectResult": [ + "string0" + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "default", + "key": "encrypted_string", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "Distinct fails when filtering on a random encrypted field", + "operations": [ + { + "name": "distinct", + "arguments": { + "filter": { + "random": "abc" + }, + "fieldName": "encrypted_string" + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/distinct.yml b/source/client-side-encryption/tests/unified/distinct.yml new file mode 100644 index 0000000000..8e814f32d5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/distinct.yml @@ -0,0 +1,200 @@ +description: distinct +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc2_encrypted { + _id: 3, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "distinct with deterministic encryption" + operations: + - name: distinct + arguments: + filter: { encrypted_string: "string0" } + fieldName: "encrypted_string" + object: "coll" + expectResult: + - "string0" + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + distinct: *collection_name + key: encrypted_string + query: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + commandName: distinct + - description: "Distinct fails when filtering on a random encrypted field" + operations: + - name: distinct + arguments: + filter: { random: "abc" } + fieldName: "encrypted_string" + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" diff --git a/source/client-side-encryption/tests/unified/explain.json b/source/client-side-encryption/tests/unified/explain.json new file mode 100644 index 0000000000..667f921165 --- /dev/null +++ b/source/client-side-encryption/tests/unified/explain.json @@ -0,0 +1,293 @@ +{ + "description": "explain", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Explain a find with deterministic encryption", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "explain": { + "find": "default", + "filter": { + "encrypted_string": "string1" + } + } + }, + "commandName": "explain" + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "explain": { + "find": "default", + "filter": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + } + }, + "verbosity": "allPlansExecution" + }, + "commandName": "explain" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/explain.yml b/source/client-side-encryption/tests/unified/explain.yml new file mode 100644 index 0000000000..c28970b340 --- /dev/null +++ b/source/client-side-encryption/tests/unified/explain.yml @@ -0,0 +1,182 @@ +description: explain +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Explain a find with deterministic encryption" + operations: + - name: runCommand + object: "db" + arguments: + command: + explain: + find: &collection_name "default" + + filter: { encrypted_string: "string1" } + commandName: explain + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + collectionName: *collection_name + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + explain: + find: *collection_name + filter: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + }, + } + verbosity: "allPlansExecution" + commandName: explain diff --git a/source/client-side-encryption/tests/unified/find.json b/source/client-side-encryption/tests/unified/find.json new file mode 100644 index 0000000000..7f358d9c08 --- /dev/null +++ b/source/client-side-encryption/tests/unified/find.json @@ -0,0 +1,458 @@ +{ + "description": "find", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Find with deterministic encryption", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Find with $in with deterministic encryption", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1", + "random": "abc" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$binary": { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Find fails when filtering on a random encrypted field", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "random": "abc" + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/find.yml b/source/client-side-encryption/tests/unified/find.yml new file mode 100644 index 0000000000..a1960c8aed --- /dev/null +++ b/source/client-side-encryption/tests/unified/find.yml @@ -0,0 +1,256 @@ +description: find +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: + { + "$binary": + { + "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Find with deterministic encryption" + operations: + - name: find + arguments: + filter: { encrypted_string: "string0" } + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + find: *collection_name + filter: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + commandName: find + - description: "Find with $in with deterministic encryption" + operations: + - name: find + arguments: + filter: { encrypted_string: { $in: ["string0", "string1"] } } + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string1", random: "abc" } + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + find: *collection_name + filter: + # Note, the values are re-ordered, but this is logically equivalent. + { + encrypted_string: + { + $in: + [ + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + ], + }, + } + commandName: find + - description: "Find fails when filtering on a random encrypted field" + operations: + - name: find + arguments: + filter: { random: "abc" } + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" diff --git a/source/client-side-encryption/tests/unified/findOneAndDelete.json b/source/client-side-encryption/tests/unified/findOneAndDelete.json new file mode 100644 index 0000000000..ff1103cb9b --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndDelete.json @@ -0,0 +1,276 @@ +{ + "description": "findOneAndDelete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "findOneAndDelete with deterministic encryption", + "operations": [ + { + "name": "findOneAndDelete", + "arguments": { + "filter": { + "encrypted_string": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "remove": true + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/findOneAndDelete.yml b/source/client-side-encryption/tests/unified/findOneAndDelete.yml new file mode 100644 index 0000000000..cff57ad2d4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndDelete.yml @@ -0,0 +1,175 @@ +description: findOneAndDelete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "findOneAndDelete with deterministic encryption" + operations: + - name: findOneAndDelete + arguments: + filter: { encrypted_string: "string0" } + object: "coll" + outcome: + - documents: + - *doc1_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + remove: true + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/findOneAndReplace.json b/source/client-side-encryption/tests/unified/findOneAndReplace.json new file mode 100644 index 0000000000..c1a89fd2f6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndReplace.json @@ -0,0 +1,282 @@ +{ + "description": "findOneAndReplace", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "findOneAndReplace with deterministic encryption", + "operations": [ + { + "name": "findOneAndReplace", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "replacement": { + "encrypted_string": "string1" + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encrypted_string": "string0" + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "update": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/findOneAndReplace.yml b/source/client-side-encryption/tests/unified/findOneAndReplace.yml new file mode 100644 index 0000000000..2fe5440a6c --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndReplace.yml @@ -0,0 +1,187 @@ +description: findOneAndReplace +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "findOneAndReplace with deterministic encryption" + operations: + - name: findOneAndReplace + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1" } + returnDocument: Before + object: "coll" + expectResult: { _id: 1, encrypted_string: "string0" } + outcome: + - documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + update: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/findOneAndUpdate.json b/source/client-side-encryption/tests/unified/findOneAndUpdate.json new file mode 100644 index 0000000000..ffcb0e79e4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndUpdate.json @@ -0,0 +1,286 @@ +{ + "description": "findOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "findOneAndUpdate with deterministic encryption", + "operations": [ + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encrypted_string": "string0" + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "update": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/findOneAndUpdate.yml b/source/client-side-encryption/tests/unified/findOneAndUpdate.yml new file mode 100644 index 0000000000..da6b2dd8c4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,190 @@ +description: findOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "findOneAndUpdate with deterministic encryption" + operations: + - name: findOneAndUpdate + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1" } } + returnDocument: Before + object: "coll" + expectResult: { _id: 1, encrypted_string: "string0" } + outcome: + - documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + update: + { + $set: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + }, + } + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.json b/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.json new file mode 100644 index 0000000000..671413b83f --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.json @@ -0,0 +1,324 @@ +{ + "description": "fle2v2-BypassQueryAnalysis", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "serverless": "forbid", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "keyVaultNamespace": "keyvault.datakeys", + "bypassQueryAnalysis": true + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "encryptedDB", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "encryptedColl", + "database": "encryptedDB", + "collectionName": "default" + } + }, + { + "client": { + "id": "client1" + } + }, + { + "database": { + "id": "unencryptedDB", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "unencryptedColl", + "database": "unencryptedDB", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + } + ], + "tests": [ + { + "description": "BypassQueryAnalysis decrypts", + "operations": [ + { + "object": "encryptedColl", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": { + "$binary": { + "base64": "C18BAAAFZAAgAAAAANnt+eLTkv4GdDPl8IAfJOvTzArOgFJQ2S/DcLza4W0DBXMAIAAAAAD2u+omZme3P2gBPehMQyQHQ153tPN1+z7bksYA9jKTpAVwADAAAAAAUnCOQqIvmR65YKyYnsiVfVrg9hwUVO3RhhKExo3RWOzgaS0QdsBL5xKFS0JhZSoWBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAEjRWeBI0mHYSNBI0VniQEpQbp/ZJpWBKeDtKLiXb0P2E9wvc0g3f373jnYQYlJquOrlPOoEy3ngsHPJuSUijvWDsrQzqYa349K7G/66qaXEFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAACkm0o9bj6j0HuADKc0svbqO2UHj6GrlNdF6yKNxh63xRJrAAAAAAAAAAAAAA==", + "subType": "06" + } + } + } + } + }, + { + "object": "encryptedColl", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + }, + { + "object": "unencryptedColl", + "name": "find", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.yml b/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.yml new file mode 100644 index 0000000000..b601d2ba13 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-BypassQueryAnalysis.yml @@ -0,0 +1,154 @@ +description: fle2v2-BypassQueryAnalysis + +schemaVersion: "1.25" + +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Test has not run on Serverless. + # Serverless tests are planned for removal: DRIVERS-3115 + serverless: forbid + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: &client0 client0 + autoEncryptOpts: + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + keyVaultNamespace: keyvault.datakeys + bypassQueryAnalysis: true + observeEvents: [commandStartedEvent] + - database: + id: &encryptedDB encryptedDB + client: *client0 + databaseName: &encryptedDBName default + - collection: + id: &encryptedColl encryptedColl + database: *encryptedDB + collectionName: &encryptedCollName default + - client: + id: &client1 client1 + - database: + id: &unencryptedDB unencryptedDB + client: *client1 + databaseName: *encryptedDBName + - collection: + id: &unencryptedColl unencryptedColl + database: *unencryptedDB + collectionName: *encryptedCollName + +initialData: + - databaseName: &keyvaultDBName keyvault + collectionName: &datakeysCollName datakeys + documents: + - { + "_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" }, + } + - databaseName: *encryptedDBName + collectionName: *encryptedCollName + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + +tests: + - description: "BypassQueryAnalysis decrypts" + operations: + - object: *encryptedColl + name: insertOne + arguments: + document: &doc0_encrypted { "_id": 1, "encryptedIndexed": { "$binary": { + # Payload has an IndexKey of key1 and UserKey of key1. + "base64": "C18BAAAFZAAgAAAAANnt+eLTkv4GdDPl8IAfJOvTzArOgFJQ2S/DcLza4W0DBXMAIAAAAAD2u+omZme3P2gBPehMQyQHQ153tPN1+z7bksYA9jKTpAVwADAAAAAAUnCOQqIvmR65YKyYnsiVfVrg9hwUVO3RhhKExo3RWOzgaS0QdsBL5xKFS0JhZSoWBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAEjRWeBI0mHYSNBI0VniQEpQbp/ZJpWBKeDtKLiXb0P2E9wvc0g3f373jnYQYlJquOrlPOoEy3ngsHPJuSUijvWDsrQzqYa349K7G/66qaXEFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAACkm0o9bj6j0HuADKc0svbqO2UHj6GrlNdF6yKNxh63xRJrAAAAAAAAAAAAAA==", + "subType": "06", + } } } + - object: *encryptedColl + name: find + arguments: + filter: { "_id": 1 } + expectResult: [{ "_id": 1, "encryptedIndexed": "123" }] + - object: *unencryptedColl + name: find + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { "$$type": "binData" }, + "__safeContent__": + [{ "$binary": { "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType": "00" } }], + } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *encryptedCollName + commandName: listCollections + - commandStartedEvent: + command: + insert: *encryptedCollName + documents: + - *doc0_encrypted + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *encryptedCollName + filter: { "_id": 1 } + commandName: find + - commandStartedEvent: + command: + find: *datakeysCollName + filter: + { + "$or": + [ + { + "_id": { "$in": [{ "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }] }, + }, + { "keyAltNames": { "$in": [] } }, + ], + } + $db: *keyvaultDBName + readConcern: { level: "majority" } + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Compact.json b/source/client-side-encryption/tests/unified/fle2v2-Compact.json new file mode 100644 index 0000000000..07ebf4351b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Compact.json @@ -0,0 +1,312 @@ +{ + "description": "fle2v2-Compact", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "client": { + "id": "client1" + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "database": { + "id": "db1", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll1", + "database": "db1", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + }, + "commandName": "compactStructuredEncryptionData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections", + "reply": { + "ok": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find", + "reply": { + "ok": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "compactStructuredEncryptionData": "default", + "compactionTokens": { + "encryptedIndexed": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "encryptedUnindexed": { + "$binary": { + "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", + "subType": "00" + } + } + } + }, + "commandName": "compactStructuredEncryptionData" + } + }, + { + "commandSucceededEvent": { + "commandName": "compactStructuredEncryptionData", + "reply": { + "ok": 1 + } + } + } + ] + } + ] + }, + { + "description": "Compact errors on an unencrypted client", + "operations": [ + { + "name": "runCommand", + "object": "db1", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + }, + "commandName": "compactStructuredEncryptionData" + }, + "expectError": { + "errorContains": "'compactStructuredEncryptionData.compactionTokens' is missing" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Compact.yml b/source/client-side-encryption/tests/unified/fle2v2-Compact.yml new file mode 100644 index 0000000000..53de7766fb --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Compact.yml @@ -0,0 +1,167 @@ +description: fle2v2-Compact +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - client: + id: "client1" + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - database: + id: "db1" + client: "client1" + databaseName: default + - collection: + id: "coll1" + database: "db1" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: runCommand + object: "db" + arguments: + command: + compactStructuredEncryptionData: &collection_name "default" + commandName: compactStructuredEncryptionData + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + reply: + ok: 1 + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": + { + "$in": + [ + "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }, + "$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" }, + ], + }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandSucceededEvent: + commandName: find + reply: + ok: 1 + - commandStartedEvent: + command: + compactStructuredEncryptionData: *collection_name + compactionTokens: + { + "encryptedIndexed": + { "$binary": { "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", "subType": "00" } }, + "encryptedUnindexed": + { "$binary": { "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", "subType": "00" } }, + } + commandName: compactStructuredEncryptionData + - commandSucceededEvent: + commandName: compactStructuredEncryptionData + reply: + ok: 1 + - description: "Compact errors on an unencrypted client" + operations: + - name: runCommand + object: "db1" + arguments: + command: + compactStructuredEncryptionData: *collection_name + commandName: compactStructuredEncryptionData + expectError: + errorContains: "'compactStructuredEncryptionData.compactionTokens' is missing" diff --git a/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.json b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.json new file mode 100644 index 0000000000..fc069d55b2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.json @@ -0,0 +1,127 @@ +{ + "description": "fle2v2-CreateCollection-OldServer", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "6.0.0", + "maxServerVersion": "6.3.99", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + } + ] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "tests": [ + { + "description": "driver returns an error if creating a QEv2 collection on unsupported server", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + }, + "expectError": { + "errorContains": "Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption." + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.yml b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.yml new file mode 100644 index 0000000000..778fe336ae --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection-OldServer.yml @@ -0,0 +1,75 @@ +description: fle2v2-CreateCollection-OldServer +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "6.0.0" + maxServerVersion: "6.3.99" + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + encryptedFieldsMap: + default.encryptedCollection: + { + "fields": + [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }, + }, + ], + } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +tests: + - description: "driver returns an error if creating a QEv2 collection on unsupported server" + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + expectError: + errorContains: "Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption." + # Assert no collections were created. + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: &database_name "default" + collectionName: &esc_collection_name "enxcol_.encryptedCollection.esc" + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: &ecc_collection_name "enxcol_.encryptedCollection.ecc" + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: &ecoc_collection_name "enxcol_.encryptedCollection.ecoc" + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: encryptedCollection diff --git a/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.json b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.json new file mode 100644 index 0000000000..81a4c61731 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.json @@ -0,0 +1,1748 @@ +{ + "description": "fle2v2-CreateCollection", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client1", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + }, + "encryptedFieldsMap": { + "default.encryptedCollection": { + "escCollection": "invalid_esc_name", + "ecocCollection": "invalid_ecoc_name", + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db1", + "client": "client1", + "databaseName": "default" + } + }, + { + "client": { + "id": "client2", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + }, + "encryptedFieldsMap": {} + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db2", + "client": "client2", + "databaseName": "default" + } + } + ], + "tests": [ + { + "description": "state collections and index are created", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "default state collection names are applied", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "drop removes all state collections", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + } + ] + } + ] + }, + { + "description": "CreateCollection without encryptedFields.", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "plaintextCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "plaintextCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "plaintextCollection" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "plaintextCollection" + } + }, + "databaseName": "default", + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "plaintextCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "plaintextCollection" + }, + "databaseName": "default", + "commandName": "create" + } + } + ] + } + ] + }, + { + "description": "CreateCollection from encryptedFieldsMap.", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "CreateCollection from encryptedFields.", + "operations": [ + { + "name": "dropCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + } + ], + "expectEvents": [ + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "databaseName": "default", + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + } + ] + } + ] + }, + { + "description": "DropCollection from encryptedFieldsMap", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + } + ] + } + ] + }, + { + "description": "DropCollection from encryptedFields", + "operations": [ + { + "name": "dropCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + } + ], + "expectEvents": [ + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "databaseName": "default", + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + } + ] + } + ] + }, + { + "description": "DropCollection from remote encryptedFields", + "operations": [ + { + "name": "dropCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "createCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "__safeContent___1" + } + }, + { + "name": "dropCollection", + "object": "db2", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.esc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "enxcol_.encryptedCollection.ecoc" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + } + ], + "expectEvents": [ + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.encryptedCollection.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "encryptedCollection", + "encryptedFields": { + "fields": [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { + "$binary": { + "subType": "04", + "base64": "AAAAAAAAAAAAAAAAAAAAAA==" + } + } + } + ] + } + }, + "databaseName": "default", + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "databaseName": "default", + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "databaseName": "default", + "commandName": "createIndexes" + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "encryptedCollection" + } + }, + "databaseName": "default", + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.esc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.encryptedCollection.ecoc" + }, + "databaseName": "default", + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "encryptedCollection" + }, + "databaseName": "default", + "commandName": "drop" + } + } + ] + } + ] + }, + { + "description": "encryptedFields are consulted for metadata collection names", + "operations": [ + { + "name": "dropCollection", + "object": "db1", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db1", + "arguments": { + "collection": "encryptedCollection" + }, + "expectError": { + "errorContains": "Encrypted State Collection name should follow" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.yml b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.yml new file mode 100644 index 0000000000..4c39fe797d --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-CreateCollection.yml @@ -0,0 +1,950 @@ +description: fle2v2-CreateCollection +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + encryptedFieldsMap: + default.encryptedCollection: + &encrypted_fields { + "fields": + [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" } }, + }, + ], + } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - client: + id: "client1" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + encryptedFieldsMap: + default.encryptedCollection: + { + "escCollection": "invalid_esc_name", + "ecocCollection": "invalid_ecoc_name", + "fields": + [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" } }, + }, + ], + } + observeEvents: + - commandStartedEvent + - database: + id: "db1" + client: "client1" + databaseName: default + - client: + id: "client2" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + encryptedFieldsMap: {} # this forces the driver to ask the server for encryptedFields + observeEvents: + - commandStartedEvent + - database: + id: "db2" + client: "client2" + databaseName: default +tests: + - description: "state collections and index are created" + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: &encrypted_collection_name "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: &database_name "default" + collectionName: &esc_collection_name "enxcol_.encryptedCollection.esc" + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: &ecc_collection_name "enxcol_.encryptedCollection.ecc" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: &ecoc_collection_name "enxcol_.encryptedCollection.ecoc" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + # Data collection is created after. + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: + &encrypted_fields_expectation { + "fields": + [ + { + "path": "firstName", + "bsonType": "string", + "keyId": { "$binary": { "subType": "04", "base64": "AAAAAAAAAAAAAAAAAAAAAA==" } }, + }, + ], + } + databaseName: *database_name + commandName: create + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + - description: "default state collection names are applied" + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + # Data collection is created after. + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + databaseName: *database_name + commandName: create + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + - description: "drop removes all state collections" + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + - name: dropCollection + object: "db" + arguments: + collection: *encrypted_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + # Data collection is created after. + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields + databaseName: *database_name + commandName: create + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + - description: "CreateCollection without encryptedFields." + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "plaintextCollection" + - name: createCollection + object: "db" + arguments: + collection: "plaintextCollection" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: "plaintextCollection" + + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + # expect listCollections to be sent on drop to check for remote encryptedFields. + - commandStartedEvent: + command: + listCollections: 1 + filter: { name: "plaintextCollection" } + databaseName: *database_name + commandName: listCollections + - commandStartedEvent: + command: + drop: "plaintextCollection" + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + - commandStartedEvent: + command: + create: "plaintextCollection" + databaseName: *database_name + commandName: create + - description: "CreateCollection from encryptedFieldsMap." + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: "db" + arguments: + collection: *encrypted_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + # Data collection is created after. + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + databaseName: *database_name + commandName: create + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + - description: "CreateCollection from encryptedFields." + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + + expectEvents: + - client: "client2" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + # State collections are created first. + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + # Data collection is created after. + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + databaseName: *database_name + commandName: create + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - commandStartedEvent: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + databaseName: *database_name + commandName: listCollections + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + + - description: "DropCollection from encryptedFieldsMap" + operations: + - name: dropCollection + object: "db" + arguments: + collection: *encrypted_collection_name + expectEvents: + - client: "client0" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + - description: "DropCollection from encryptedFields" + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + - name: dropCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + expectEvents: + - client: "client2" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + databaseName: *database_name + commandName: create + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - commandStartedEvent: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + databaseName: *database_name + commandName: listCollections + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + + - description: "DropCollection from remote encryptedFields" + + operations: + # Do initial drops to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: createCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + encryptedFields: *encrypted_fields + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + indexName: __safeContent___1 + - name: dropCollection + object: "db2" + arguments: + collection: *encrypted_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *esc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *ecoc_collection_name + # ecc collection is no longer created for QEv2 + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: *encrypted_collection_name + + expectEvents: + - client: "client2" + events: + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + # events from createCollection ... begin + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: { key: { _id: 1 }, unique: true } + databaseName: *database_name + commandName: create + - commandStartedEvent: + command: + create: *encrypted_collection_name + encryptedFields: *encrypted_fields_expectation + databaseName: *database_name + commandName: create + # libmongocrypt requests listCollections to get a schema for the "createIndexes" command. + - commandStartedEvent: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + databaseName: *database_name + commandName: listCollections + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *encrypted_collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + databaseName: *database_name + commandName: createIndexes + # events from createCollection ... end + # events from dropCollection ... begin + - commandStartedEvent: + command: + listCollections: 1 + filter: { name: *encrypted_collection_name } + databaseName: *database_name + commandName: listCollections + - commandStartedEvent: + command: + drop: *esc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *ecoc_collection_name + databaseName: *database_name + commandName: drop + - commandStartedEvent: + command: + drop: *encrypted_collection_name + databaseName: *database_name + commandName: drop + # events from dropCollection ... end + - description: "encryptedFields are consulted for metadata collection names" + + operations: + # Do an initial drop to remove collections that may exist from previous test runs. + - name: dropCollection + object: "db1" + arguments: + collection: *encrypted_collection_name + - name: createCollection + object: "db1" + arguments: + collection: *encrypted_collection_name + expectError: + # Expect error due to server constraints added in SERVER-74069 + errorContains: "Encrypted State Collection name should follow" diff --git a/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.json b/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.json new file mode 100644 index 0000000000..b171c78c00 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.json @@ -0,0 +1,186 @@ +{ + "description": "fle2v2-DecryptExistingData", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ] + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.yml b/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.yml new file mode 100644 index 0000000000..eac18cdb94 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-DecryptExistingData.yml @@ -0,0 +1,100 @@ +description: fle2v2-DecryptExistingData +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + [ + { + "_id": 1, + "encryptedUnindexed": + { + "$binary": + { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06", + }, + }, + }, + ] + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: [{ "_id": 1, "encryptedUnindexed": "value123" }] + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: &collection_name "default" + commandName: listCollections + - commandStartedEvent: + command: + find: *collection_name + filter: { "_id": 1 } + commandName: find + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Delete.json new file mode 100644 index 0000000000..305f642ae1 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Delete.json @@ -0,0 +1,326 @@ +{ + "description": "fle2v2-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Delete.yml new file mode 100644 index 0000000000..243d19de44 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Delete.yml @@ -0,0 +1,155 @@ +description: fle2v2-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: { "_id": 1, "encryptedIndexed": "value123" } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedIndexed": "value123" } + object: "coll" + expectResult: + deletedCount: 1 + outcome: + - documents: [] + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - { + "q": + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json new file mode 100644 index 0000000000..7a6957db0a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.json @@ -0,0 +1,258 @@ +{ + "description": "fle2v2-EncryptedFields-vs-EncryptedFieldsMap", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "serverless": "forbid", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "encryptedFieldsMap": { + "default.default": { + "fields": [] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "encryptedDB", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "encryptedColl", + "database": "encryptedDB", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + } + } + ] + }, + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + } + ], + "tests": [ + { + "description": "encryptedFieldsMap is preferred over remote encryptedFields", + "operations": [ + { + "object": "encryptedColl", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + } + }, + { + "object": "encryptedColl", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "default", + "commandName": "insert", + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ], + "ordered": true + } + } + }, + { + "commandStartedEvent": { + "databaseName": "default", + "commandName": "find", + "command": { + "find": "default", + "filter": { + "_id": 1 + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "commandName": "find", + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "default", + "databaseName": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml new file mode 100644 index 0000000000..bf5ad3da85 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml @@ -0,0 +1,137 @@ +description: fle2v2-EncryptedFields-vs-EncryptedFieldsMap + +schemaVersion: "1.25" + +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Test has not run on Serverless. + # Serverless tests are planned for removal: DRIVERS-3115 + serverless: forbid + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: &client0 client0 + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + encryptedFieldsMap: { "default.default": { "fields": [] } } + observeEvents: [commandStartedEvent] + - database: + id: &encryptedDB encryptedDB + client: *client0 + databaseName: &encryptedDBName default + - collection: + id: &encryptedColl encryptedColl + database: *encryptedDB + collectionName: &encryptedCollName default + +initialData: + - databaseName: &keyvaultDBName keyvault + collectionName: &datakeysCollName datakeys + documents: + - { + "_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" }, + } + - databaseName: *encryptedDBName + collectionName: *encryptedCollName + documents: [] + createOptions: + encryptedFields: + { + "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", + }, + ], + } + +tests: + - description: "encryptedFieldsMap is preferred over remote encryptedFields" + operations: + # EncryptedFieldsMap overrides remote encryptedFields. + # Automatic encryption does not occur on encryptedUnindexed. The value is validated on the server. + - object: *encryptedColl + name: insertOne + arguments: + document: + &doc0 { + _id: 1, + encryptedUnindexed: + { + "$binary": + { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06", + }, + }, + } + - object: *encryptedColl + name: find + arguments: + filter: { "_id": 1 } + expectResult: + - { "_id": 1, "encryptedUnindexed": "value123" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *encryptedDBName + commandName: insert + command: + insert: *encryptedCollName + documents: + - *doc0 + ordered: true + - commandStartedEvent: + databaseName: *encryptedDBName + commandName: find + command: + find: *encryptedCollName + filter: { "_id": 1 } + - commandStartedEvent: + databaseName: *keyvaultDBName + commandName: find + command: + find: *datakeysCollName + filter: + { + "$or": + [ + { + "_id": { "$in": [{ "$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" } }] }, + }, + { "keyAltNames": { "$in": [] } }, + ], + } + $db: *keyvaultDBName + readConcern: { level: "majority" } + outcome: + - collectionName: *encryptedCollName + databaseName: *encryptedDBName + documents: + - *doc0 diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.json b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.json new file mode 100644 index 0000000000..af24e9b369 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.json @@ -0,0 +1,367 @@ +{ + "description": "fle2v2-EncryptedFields-vs-jsonSchema", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + }, + "validator": { + "$jsonSchema": { + "properties": {}, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedIndexed": "123" + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.yml b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.yml new file mode 100644 index 0000000000..600f8c4723 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFields-vs-jsonSchema.yml @@ -0,0 +1,170 @@ +description: fle2v2-EncryptedFields-vs-jsonSchema +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + validator: + $jsonSchema: { "properties": {}, "bsonType": "object" } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedIndexed: "123" } + object: "coll" + - name: find + arguments: + filter: { encryptedIndexed: "123" } + object: "coll" + expectResult: [*doc0] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { $$type: "binData" }, + "__safeContent__": + ["$binary": { "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: &collection_name default + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.json b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.json new file mode 100644 index 0000000000..3727e43147 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.json @@ -0,0 +1,139 @@ +{ + "description": "fle2v2-EncryptedFieldsMap-defaults", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "encryptedFieldsMap": { + "default.default": { + "fields": [] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + } + ], + "tests": [ + { + "description": "default state collections are applied to encryptionInformation", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "foo": { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "foo": { + "$binary": { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "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 + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.yml b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.yml new file mode 100644 index 0000000000..05b01b8a32 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-EncryptedFieldsMap-defaults.yml @@ -0,0 +1,78 @@ +description: fle2v2-EncryptedFieldsMap-defaults +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + encryptedFieldsMap: { "default.default": { "fields": [] } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] +tests: + - description: "default state collections are applied to encryptionInformation" + operations: + - name: insertOne + arguments: + document: + &doc0 { + _id: 1, + foo: + { + "$binary": + { + "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + } + object: "coll" + outcome: + - documents: + - *doc0 + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0 + encryptionInformation: + { + "type": { "$numberInt": "1" }, + "schema": + { + "default.default": + { + "escCollection": "enxcol_.default.esc", + "ecocCollection": "enxcol_.default.ecoc", + "fields": [], + }, + }, + } + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.json new file mode 100644 index 0000000000..5131dc9fef --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.json @@ -0,0 +1,622 @@ +{ + "description": "fle2v2-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "foo": "bar" + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "foo": "bar", + "__safeContent__": [ + { + "$binary": { + "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate can modify an FLE2 indexed field", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "encryptedIndexed": "value456" + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "encryptedIndexed": "value456" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.yml new file mode 100644 index 0000000000..b2183bc2a7 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-FindOneAndUpdate.yml @@ -0,0 +1,271 @@ +description: fle2v2-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: { "_id": 1, "encryptedIndexed": "value123" } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { "encryptedIndexed": "value123" } + update: { "$set": { "foo": "bar" } } + returnDocument: Before + object: "coll" + expectResult: { "_id": 1, "encryptedIndexed": "value123" } + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { "$$type": "binData" }, + "foo": "bar", + "__safeContent__": + ["$binary": { "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "foo": "bar" } } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify + - description: "findOneAndUpdate can modify an FLE2 indexed field" + operations: + - name: insertOne + arguments: + document: { "_id": 1, "encryptedIndexed": "value123" } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { "encryptedIndexed": "value123" } + update: { "$set": { "encryptedIndexed": "value456" } } + returnDocument: Before + object: "coll" + expectResult: { "_id": 1, "encryptedIndexed": "value123" } + - name: find + arguments: + filter: { "_id": 1 } + object: "coll" + expectResult: ["encryptedIndexed": "value456"] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { "$$type": "binData" }, + "__safeContent__": + ["$binary": { "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedIndexed": { "$$type": "binData" } } } + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify + - commandStartedEvent: + command: + find: *collection_name + filter: { "_id": { "$eq": 1 } } + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.json b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.json new file mode 100644 index 0000000000..8155797583 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.json @@ -0,0 +1,361 @@ +{ + "description": "fle2v2-InsertFind-Indexed", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedIndexed": "123" + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.yml b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.yml new file mode 100644 index 0000000000..f5dedf4ffc --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Indexed.yml @@ -0,0 +1,168 @@ +description: fle2v2-InsertFind-Indexed +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedIndexed: "123" } + object: "coll" + - name: find + arguments: + filter: { encryptedIndexed: "123" } + object: "coll" + expectResult: [*doc0] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { $$type: "binData" }, + "__safeContent__": + ["$binary": { "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.json b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.json new file mode 100644 index 0000000000..a6410bb9d8 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.json @@ -0,0 +1,301 @@ +{ + "description": "fle2v2-InsertFind-Unindexed", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedUnindexed": "value123" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedUnindexed": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "q83vqxI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encryptedUnindexed": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Query with an unindexed field fails", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedUnindexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedUnindexed": "value123" + } + }, + "object": "coll", + "expectError": { + "errorContains": "encrypt" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.yml b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.yml new file mode 100644 index 0000000000..f84eece455 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-Unindexed.yml @@ -0,0 +1,150 @@ +description: fle2v2-InsertFind-Unindexed +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedUnindexed: "value123" } + object: "coll" + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: [*doc0] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { "_id": 1, "encryptedUnindexed": { $$type: "binData" } } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedUnindexed": { $$type: "binData" } } + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { "_id": { "$eq": 1 } } + commandName: find + - description: "Query with an unindexed field fails" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedUnindexed: "value123" } + object: "coll" + - name: find + arguments: + filter: { encryptedUnindexed: "value123" } + object: "coll" + expectError: + # Expected error message changed in https://github.com/10gen/mongo-enterprise-modules/commit/212b584d4f7a44bed41c826a180a4aff00923d7a#diff-5f12b55e8d5c52c2f62853ec595dc2c1e2e5cb4fdbf7a32739a8e3acb3c6f818 + # Before the message was "cannot query non-indexed fields with the randomized encryption algorithm" + # After: "can only execute encrypted equality queries with an encrypted equality index" + # Use a small common substring. + errorContains: "encrypt" diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.json b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.json new file mode 100644 index 0000000000..ef13a3b2db --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.json @@ -0,0 +1,485 @@ +{ + "description": "fle2v2-InsertFind-keyAltName", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.18.0" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "encryptedFieldsMap": { + "default.default": { + "fields": [ + { + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + }, + "keyAltName": "altname" + } + ] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + }, + "keyAltNames": [ + "altname" + ] + } + ] + } + ], + "tests": [ + { + "description": "Insert and find FLE2 indexed field", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedIndexed": "123" + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": "123" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [] + } + }, + { + "keyAltNames": { + "$in": [ + "altname" + ] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "Create translates keyAltName", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "default" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "default" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.default.esc" + }, + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "enxcol_.default.ecoc" + }, + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "drop": "default" + }, + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.default.esc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "enxcol_.default.ecoc", + "clusteredIndex": { + "key": { + "_id": 1 + }, + "unique": true + } + }, + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [] + } + }, + { + "keyAltNames": { + "$in": [ + "altname" + ] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "default", + "encryptedFields": { + "fields": [ + { + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + }, + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + } + ] + } + }, + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "default", + "indexes": [ + { + "name": "__safeContent___1", + "key": { + "__safeContent__": 1 + } + } + ] + }, + "commandName": "createIndexes" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.yml b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.yml new file mode 100644 index 0000000000..72fb3b9b2d --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-InsertFind-keyAltName.yml @@ -0,0 +1,249 @@ +description: fle2v2-InsertFind-keyAltName +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.18.0" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + encryptedFieldsMap: + default.default: + { + "fields": + [ + { + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { "queryType": "equality", "contention": { "$numberLong": "0" } }, + "keyAltName": "altname" + }, + ], + } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { "queryType": "equality", "contention": { "$numberLong": "0" } }, + } + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" }, + "keyAltNames": ["altname"], + }, + ] +tests: + - description: "Insert and find FLE2 indexed field" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encryptedIndexed: "123" } + object: "coll" + - name: find + arguments: + filter: { encryptedIndexed: "123" } + object: "coll" + expectResult: [*doc0] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { $$type: "binData" }, + "__safeContent__": + ["$binary": { "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": [] }, + "keyAltNames": { "$in": ["altname"] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVsACAAAAAApJtKPW4+o9B7gAynNLL26jtlB4+hq5TXResijcYet8USY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find + - description: "Create translates keyAltName" + operations: + - name: dropCollection # Drop collection if already exists + object: "db" + arguments: + collection: *collection_name + - name: createCollection # Uses encrypted client with encryptedFieldsMap. keyAltName translated to keyId. + object: "db" + arguments: + collection: *collection_name + expectEvents: + - client: "client0" + events: + + # events from dropCollection ... begin + - commandStartedEvent: + command: + drop: &esc_collection_name enxcol_.default.esc + commandName: drop + - commandStartedEvent: + command: + drop: &ecoc_collection_name enxcol_.default.ecoc + commandName: drop + - commandStartedEvent: + command: + drop: *collection_name + commandName: drop + # events from dropCollection ... end + + # events from createCollection ... begin + - commandStartedEvent: + command: + create: *esc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + commandName: create + - commandStartedEvent: + command: + create: *ecoc_collection_name + clusteredIndex: {key: {_id: 1}, unique: true} + commandName: create + - commandStartedEvent: + command: + find: datakeys # To translate keyAltName to keyId. + filter: + { + "$or": + [ + "_id": { "$in": [] }, + "keyAltNames": { "$in": ["altname"] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + create: *collection_name + encryptedFields: &encryptedFields_translated { + "fields": + [ + { + "path": "encryptedIndexed", + "bsonType": "string", + "queries": { "queryType": "equality", "contention": { "$numberLong": "0" } }, + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } } + } + ] + } + commandName: create + # Index on __safeContents__ is then created. + - commandStartedEvent: + command: + createIndexes: *collection_name + indexes: + - name: __safeContent___1 + key: { __safeContent__: 1 } + commandName: createIndexes + # events from createCollection ... end \ No newline at end of file diff --git a/source/client-side-encryption/tests/unified/fle2v2-MissingKey.json b/source/client-side-encryption/tests/unified/fle2v2-MissingKey.json new file mode 100644 index 0000000000..dc8ffc57b2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-MissingKey.json @@ -0,0 +1,137 @@ +{ + "description": "fle2v2-MissingKey", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "encryptedUnindexed": { + "$binary": { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06" + } + } + } + ], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "FLE2 encrypt fails with missing key", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "123" + } + }, + "object": "coll", + "expectError": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + }, + { + "description": "FLE2 decrypt fails with missing key", + "operations": [ + { + "name": "find", + "arguments": { + "filter": {} + }, + "object": "coll", + "expectError": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-MissingKey.yml b/source/client-side-encryption/tests/unified/fle2v2-MissingKey.yml new file mode 100644 index 0000000000..005337d687 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-MissingKey.yml @@ -0,0 +1,79 @@ +description: fle2v2-MissingKey +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + [ + "encryptedUnindexed": + { + "$binary": + { + "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", + "subType": "06", + }, + }, + ] + createOptions: + encryptedFields: + { + "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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: [] +tests: + - description: "FLE2 encrypt fails with missing key" + operations: + - name: insertOne + arguments: + document: { _id: 1, encryptedIndexed: "123" } + object: "coll" + expectError: + errorContains: "not all keys requested were satisfied" + - description: "FLE2 decrypt fails with missing key" + operations: + - name: find + arguments: + filter: {} + object: "coll" + expectError: + errorContains: "not all keys requested were satisfied" diff --git a/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.json b/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.json new file mode 100644 index 0000000000..4036fe5edd --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.json @@ -0,0 +1,123 @@ +{ + "description": "fle2v2-NoEncryption", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [] + } + } + } + ], + "tests": [ + { + "description": "insert with no encryption succeeds", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "foo": "bar" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "foo": "bar" + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "foo": "bar" + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.yml b/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.yml new file mode 100644 index 0000000000..cdf10a05a5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-NoEncryption.yml @@ -0,0 +1,61 @@ +description: fle2v2-NoEncryption +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: { "fields": [] } +tests: + - description: "insert with no encryption succeeds" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, foo: "bar" } + object: "coll" + outcome: + - documents: + - *doc0 + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.json new file mode 100644 index 0000000000..8ccbcafc24 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.json @@ -0,0 +1,358 @@ +{ + "description": "fle2v2-Rangev2-Compact", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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": "Compact works with 'range' fields", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "command": { + "compactStructuredEncryptionData": "default" + }, + "commandName": "compactStructuredEncryptionData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections", + "reply": { + "ok": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find", + "reply": { + "ok": 1 + } + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert", + "reply": { + "ok": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "compactStructuredEncryptionData": "default", + "compactionTokens": { + "encryptedInt": { + "ecoc": { + "$binary": { + "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", + "subType": "00" + } + }, + "anchorPaddingToken": { + "$binary": { + "base64": "QxKJD2If48p0l8NAXf2Kr0aleMd/dATSjBK6hTpNMyc=", + "subType": "00" + } + } + } + }, + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "compactStructuredEncryptionData" + } + }, + { + "commandSucceededEvent": { + "commandName": "compactStructuredEncryptionData", + "reply": { + "ok": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.yml new file mode 100644 index 0000000000..b6253084e0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Compact.yml @@ -0,0 +1,153 @@ +description: fle2v2-Rangev2-Compact +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Require range v2 support on server. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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: "Compact works with 'range' fields" + operations: + # Insert before running compact to ensure the server errors if `encryptionInformation` is incorrectly omitted. + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: runCommand + object: "db" + arguments: + command: + compactStructuredEncryptionData: &collection_name "default" + commandName: compactStructuredEncryptionData + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + reply: + ok: 1 + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandSucceededEvent: + commandName: find + reply: + ok: 1 + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandSucceededEvent: + commandName: insert + reply: + ok: 1 + - commandStartedEvent: + command: + compactStructuredEncryptionData: *collection_name + compactionTokens: + "encryptedInt": + { + "ecoc": + { "$binary": { "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", "subType": "00" } }, + "anchorPaddingToken": + { "$binary": { "base64": "QxKJD2If48p0l8NAXf2Kr0aleMd/dATSjBK6hTpNMyc=", "subType": "00" } }, + } + encryptionInformation: *encryptionInformation + commandName: compactStructuredEncryptionData + - commandSucceededEvent: + commandName: compactStructuredEncryptionData + reply: + ok: 1 diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.json new file mode 100644 index 0000000000..7933cc5600 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.json @@ -0,0 +1,574 @@ +{ + "description": "fle2v2-Rangev2-Date-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.yml new file mode 100644 index 0000000000..4c25be7a85 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Aggregate.yml @@ -0,0 +1,218 @@ +description: fle2v2-Rangev2-Date-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + observeEvents: + - commandStartedEvent + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedDate": { $gt: { $date: { $numberLong: "0" } } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedDate": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.json new file mode 100644 index 0000000000..9ed541fa8e --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.json @@ -0,0 +1,1610 @@ +{ + "description": "fle2v2-Rangev2-Date-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "-1" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 200, + "encryptedDate": { + "$date": { + "$numberLong": "200" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "1" + } + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lte": { + "$date": { + "$numberLong": "1" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$lt": { + "$date": { + "$numberLong": "0" + } + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "200" + } + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + }, + "$lt": { + "$date": { + "$numberLong": "2" + } + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$gte": { + "$date": { + "$numberLong": "0" + } + }, + "$lte": { + "$date": { + "$numberLong": "200" + } + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + }, + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDate": { + "$in": [ + { + "$date": { + "$numberLong": "0" + } + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$numberDouble": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value type is a date" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.yml new file mode 100644 index 0000000000..a34dea8ebe --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Correctness.yml @@ -0,0 +1,457 @@ +description: fle2v2-Rangev2-Date-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: { $numberLong: "2" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gte: { $date: { $numberLong: "0" } }, $lte: { $date: { $numberLong: "200" } } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $in: [$date: { $numberLong: "0" }] } } + object: "coll" + expectResult: [*doc0] + + - description: "Insert out of range" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "-1" } } } + object: "coll" + expectError: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll" + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDate: { $date: { $numberLong: "200" } } } + object: "coll" + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc200] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gte: { $date: { $numberLong: "0" } } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "1" } } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "1" } } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lte: { $date: { $numberLong: "1" } } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $lt: { $date: { $numberLong: "0" } } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $gt: { $date: { $numberLong: "200" } } } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDate: { $gt: { $date: { $numberLong: "0" } }, $lt: { $date: { $numberLong: "2" } } } }, + } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "0" } } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $date: { $numberLong: "1" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDate: { $gte: { $date: { $numberLong: "0" } }, $lte: { $date: { $numberLong: "200" } } } }, + } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDate: { $in: [$date: { $numberLong: "0" }] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Double" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDate: { $numberDouble: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Double" + operations: + - name: find + arguments: + filter: { encryptedDate: { $gte: { $numberDouble: "0" } } } + object: "coll" + expectError: + # expect an error mongocryptd. + errorContains: "value type is a date" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.json new file mode 100644 index 0000000000..ad05dd4e17 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.json @@ -0,0 +1,505 @@ +{ + "description": "fle2v2-Rangev2-Date-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.yml new file mode 100644 index 0000000000..3be32f2bc0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Delete.yml @@ -0,0 +1,205 @@ +description: fle2v2-Rangev2-Date-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedDate": { $gt: { $date: { $numberLong: "0" } } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedDate": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.json new file mode 100644 index 0000000000..55db0279c2 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.json @@ -0,0 +1,577 @@ +{ + "description": "fle2v2-Rangev2-Date-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.yml new file mode 100644 index 0000000000..81898d8d1f --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-FindOneAndUpdate.yml @@ -0,0 +1,215 @@ +description: fle2v2-Rangev2-Date-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } } } + update: { "$set": { "encryptedDate": { $date: { $numberLong: "2" } } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedDate": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedDate": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.json new file mode 100644 index 0000000000..1fd1edf191 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.json @@ -0,0 +1,562 @@ +{ + "description": "fle2v2-Rangev2-Date-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.yml new file mode 100644 index 0000000000..5bd0fc039e --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-InsertFind.yml @@ -0,0 +1,210 @@ +description: fle2v2-Rangev2-Date-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: find + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedDate": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.json new file mode 100644 index 0000000000..d5153270d5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.json @@ -0,0 +1,581 @@ +{ + "description": "fle2v2-Rangev2-Date-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDate", + "bsonType": "date", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDate": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDate": { + "$date": { + "$numberLong": "1" + } + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDate": { + "$gt": { + "$date": { + "$numberLong": "0" + } + } + } + }, + "update": { + "$set": { + "encryptedDate": { + "$date": { + "$numberLong": "2" + } + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDate": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDate": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDate": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$date": { + "$numberLong": "0" + } + }, + "max": { + "$date": { + "$numberLong": "200" + } + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.yml new file mode 100644 index 0000000000..a793d1d603 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Date-Update.yml @@ -0,0 +1,223 @@ +description: fle2v2-Rangev2-Date-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDate", + "bsonType": "date", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$date": { "$numberLong": "0" } }, + "max": { "$date": { "$numberLong": "200" } }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDate: { $date: { $numberLong: "0" } } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDate: { $date: { $numberLong: "1" } } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedDate: { $gt: { $date: { $numberLong: "0" } } } } + update: { "$set": { "encryptedDate": { $date: { $numberLong: "2" } } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDate": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDate": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedDate": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAJbW4AAAAAAAAAAAAJbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedDate": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.json new file mode 100644 index 0000000000..712a68be32 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.json @@ -0,0 +1,1965 @@ +{ + "description": "fle2v2-Rangev2-Decimal-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "DR1jAAADcGF5bG9hZACxYgAABGcAnWIAAAMwAH0AAAAFZAAgAAAAAJu2KgiI8vM+kz9qD3ZQzFQY5qbgYqCqHG5R4jAlnlwXBXMAIAAAAAAAUXxFXsz764T79sGCdhxvNd5b6E/9p61FonsHyEIhogVsACAAAAAAt19RL3Oo5ni5L8kcvgOJYLgVYyXJExwP8pkuzLG7f/kAAzEAfQAAAAVkACAAAAAAPQPvL0ARjujSv2Rkm8r7spVsgeC1K3FWcskGGZ3OdDIFcwAgAAAAACgNn660GmefR8jLqzgR1u5O+Uocx9GyEHiBqVGko5FZBWwAIAAAAADflr+fsnZngm6KRWYgHa9JzK+bXogWl9evBU9sQUHPHQADMgB9AAAABWQAIAAAAAD2Zi6kcxmaD2mY3VWrP+wYJMPg6cSBIYPapxaFQxYFdQVzACAAAAAAM/cV36BLBY3xFBXsXJY8M9EHHOc/qrmdc2CJmj3M89gFbAAgAAAAAOpydOrKxx6m2gquSDV2Vv3w10GocmNCFeOo/fRhRH9JAAMzAH0AAAAFZAAgAAAAAOaNqI9srQ/mI9gwbk+VkizGBBH/PPWOVusgnfPk3tY1BXMAIAAAAAAc96O/pwKCmHCagT6T/QV/wz4vqO+R22GsZ1dse2Vg6QVsACAAAAAAgzIak+Q3UFLTHXPmJ+MuEklFtR3eLtvM+jdKkmGCV/YAAzQAfQAAAAVkACAAAAAA0XlQgy/Yu97EQOjronl9b3dcR1DFn3deuVhtTLbJZHkFcwAgAAAAACoMnpVl6EFJak8A+t5N4RFnQhkQEBnNAx8wDqmq5U/dBWwAIAAAAACR26FJif673qpwF1J1FEkQGJ1Ywcr/ZW6JQ7meGqzt1QADNQB9AAAABWQAIAAAAAAOtpNexRxfv0yRFvZO9DhlkpU4mDuAb8ykdLnE5Vf1VAVzACAAAAAAeblFKm/30orP16uQpZslvsoS8s0xfNPIBlw3VkHeekYFbAAgAAAAAPEoHj87sYE+nBut52/LPvleWQBzB/uaJFnosxp4NRO2AAM2AH0AAAAFZAAgAAAAAIr8xAFm1zPmrvW4Vy5Ct0W8FxMmyPmFzdWVzesBhAJFBXMAIAAAAABYeeXjJEzTHwxab6pUiCRiZjxgtN59a1y8Szy3hfkg+gVsACAAAAAAJuoY4rF8mbI+nKb+5XbZShJ8191o/e8ZCRHE0O4Ey8MAAzcAfQAAAAVkACAAAAAAl+ibLk0/+EwoqeC8S8cGgAtjtpQWGEZDsybMPnrrkwEFcwAgAAAAAHPPBudWgQ+HUorLDpJMqhS9VBF2VF5aLcxgrM1s+yU7BWwAIAAAAAAcCcBR2Vyv5pAFbaOU97yovuOi1+ATDnLLcAUqHecXcAADOAB9AAAABWQAIAAAAACR9erwLTb+tcWFZgJ2MEfM0PKI9uuwIjDTHADRFgD+SQVzACAAAAAAcOop8TXsGUVQoKhzUllMYWxL93xCOkwtIpV8Q6hiSYYFbAAgAAAAAKXKmh4V8veYwob1H03Q3p3PN8SRAaQwDT34KlNVUjiDAAM5AH0AAAAFZAAgAAAAALv0vCPgh7QpmM8Ug6ad5ioZJCh7pLMdT8FYyQioBQ6KBXMAIAAAAADsCPyIG8t6ApQkRk1fX/sfc1kpuWCWP8gAEpnYoBSHrQVsACAAAAAAJe/r67N6d8uTiogvfoR9rEXbIDjyLb9EVdqkayFFGaYAAzEwAH0AAAAFZAAgAAAAAIW4AxJgYoM0pcNTwk1RSbyjZGIqgKL1hcTJmNrnZmoPBXMAIAAAAAAZpfx3EFO0vY0f1eHnE0PazgqeNDTaj+pPJMUNW8lFrAVsACAAAAAAP+Um2vwW6Bj6vuz9DKz6+6aWkoKoEmFNoiz/xXm7lOsAAzExAH0AAAAFZAAgAAAAAKliO6L9zgeuufjj174hvmQGNRbmYYs9yAirL7OxwEW3BXMAIAAAAAAqU7vs3DWUQ95Eq8OejwWnD0GuXd+ASi/uD6S0l8MM1QVsACAAAAAAb9legYzsfctBPpHyl7YWpPmLr5QiNZFND/50N1vv2MUAAzEyAH0AAAAFZAAgAAAAAOGQcCBkk+j/Kzjt/Cs6g3BZPJG81wIHBS8JewHGpgk+BXMAIAAAAABjrxZXWCkdzrExwCgyHaafuPSQ4V4x2k9kUCAqUaYKDQVsACAAAAAADBU6KefT0v8zSmseaMNmQxKjJar72y7MojLFhkEHqrUAAzEzAH0AAAAFZAAgAAAAAPmCNEt4t97waOSd5hNi2fNCdWEkmcFJ37LI9k4Az4/5BXMAIAAAAABX7DuDPNg+duvELf3NbLWkPMFw2HGLgWGHyVWcPvSNCAVsACAAAAAAS7El1FtZ5STh8Q1FguvieyYX9b2DF1DFVsb9hzxXYRsAAzE0AH0AAAAFZAAgAAAAAD4vtVUYRNB+FD9yoQ2FVJH3nMeJeKbi6eZfth638YqbBXMAIAAAAAANCuUB4OdmuD6LaDK2f3vaqfgYYvg40wDXOBbcFjTqLwVsACAAAAAA9hqC2VoJBjwR7hcQ45xO8ZVojwC83jiRacCaDj6Px2gAAzE1AH0AAAAFZAAgAAAAAJPIRzjmTjbdIvshG6UslbEOd797ZSIdjGAhGWxVQvK1BXMAIAAAAABgmJ0Jh8WLs9IYs/a7DBjDWd8J3thW/AGJK7zDnMeYOAVsACAAAAAAi9zAsyAuou2oiCUHGc6QefLUkACa9IgeBhGu9W/r0X8AAzE2AH0AAAAFZAAgAAAAAABQyKQPoW8wGPIqnsTv69+DzIdRkohRhOhDmyVHkw9WBXMAIAAAAAAqWA2X4tB/h3O1Xlawtz6ndI6WaTwgU1QYflL35opu5gVsACAAAAAAWI/Gj5aZMwDIxztqmVL0g5LBcI8EdKEc2UA28pnekQoAAzE3AH0AAAAFZAAgAAAAACB7NOyGQ1Id3MYnxtBXqyZ5Ul/lHH6p1b10U63DfT6bBXMAIAAAAADpOryIcndxztkHSfLN3Kzq29sD8djS0PspDSqERMqokQVsACAAAAAADatsMW4ezgnyi1PiP7xk+gA4AFIN/fb5uJqfVkjg4UoAAzE4AH0AAAAFZAAgAAAAAKVfXLfs8XA14CRTB56oZwV+bFJN5BHraTXbqEXZDmTkBXMAIAAAAAASRWTsfGOpqdffiOodoqIgBzG/yzFyjR5CfUsIUIWGpgVsACAAAAAAkgCHbCwyX640/0Ni8+MoYxeHUiC+FSU4Mn9jTLYtgZgAAzE5AH0AAAAFZAAgAAAAAH/aZr4EuS0/noQR9rcF8vwoaxnxrwgOsSJ0ys8PkHhGBXMAIAAAAACd7ObGQW7qfddcvyxRTkPuvq/PHu7+6I5dxwS1Lzy5XAVsACAAAAAA3q0eKdV7KeU3pc+CtfypKR7BPxwaf30yu0j9FXeOOboAAzIwAH0AAAAFZAAgAAAAAKvlcpFFNq0oA+urq3w6d80PK1HHHw0H0yVWvU9aHijXBXMAIAAAAADWnAHQ5Fhlcjawki7kWzdqjM2f6IdGJblojrYElWjsZgVsACAAAAAAO0wvY66l24gx8nRxyVGC0QcTztIi81Kx3ndRhuZr6W4AAzIxAH0AAAAFZAAgAAAAAH/2aMezEOddrq+dNOkDrdqf13h2ttOnexZsJxG1G6PNBXMAIAAAAABNtgnibjC4VKy5poYjvdsBBnVvDTF/4mmEAxsXVgZVKgVsACAAAAAAqvadzJFLqQbs8WxgZ2D2X+XnaPSDMLCVVgWxx5jnLcYAAzIyAH0AAAAFZAAgAAAAAF2wZoDL6/V59QqO8vdRZWDpXpkV4h4KOCSn5e7x7nmzBXMAIAAAAADLZBu7LCYjbThaVUqMK14H/elrVOYIKJQCx4C9Yjw37gVsACAAAAAAEh6Vs81jLU204aGpL90fmYTm5i5R8/RT1uIbg6VU3HwAAzIzAH0AAAAFZAAgAAAAAH27yYaLn9zh2CpvaoomUPercSfJRUmBY6XFqmhcXi9QBXMAIAAAAAAUwumVlIYIs9JhDhSj0R0+59psCMsFk94E62VxkPt42QVsACAAAAAAT5x2hCCd2bpmpnyWaxas8nSxTc8e4C9DfKaqr0ABEysAAzI0AH0AAAAFZAAgAAAAALMg2kNAO4AFFs/mW3In04yFeN4AP6Vo0klyUoT06RquBXMAIAAAAAAgGWJbeIdwlpqXCyVIYSs0dt54Rfc8JF4b8uYc+YUj0AVsACAAAAAAWHeWxIkyvXTOWvfZzqtPXjfGaWWKjGSIQENTU3zBCrsAAzI1AH0AAAAFZAAgAAAAALas/i1T2DFCEmrrLEi7O2ngJZyFHialOoedVXS+OjenBXMAIAAAAAA1kK0QxY4REcGxHeMkgumyF7iwlsRFtw9MlbSSoQY7uAVsACAAAAAAUNlpMJZs1p4HfsD4Q4WZ4TBEi6Oc2fX34rzyynqWCdwAAzI2AH0AAAAFZAAgAAAAAP1TejmWg1CEuNSMt6NUgeQ5lT+oBoeyF7d2l5xQrbXWBXMAIAAAAABPX0kj6obggdJShmqtVfueKHplH4ZrXusiwrRDHMOKeQVsACAAAAAAIYOsNwC3DA7fLcOzqdr0bOFdHCfmK8tLwPoaE9uKOosAAzI3AH0AAAAFZAAgAAAAAMrKn+QPa/NxYezNhlOX9nyEkN1kE/gW7EuZkVqYl0b8BXMAIAAAAABUoZMSPUywRGfX2EEencJEKH5x/P9ySUVrhStAwgR/LgVsACAAAAAAMgZFH6lQIIDrgHnFeslv3ld20ynwQjQJt3cAp4GgrFkAAzI4AH0AAAAFZAAgAAAAAMmD1+a+oVbiUZd1HuZqdgtdVsVKwuWAn3/M1B6QGBM3BXMAIAAAAACLyytOYuZ9WEsIrrtJbXUx4QgipbaAbmlJvSZVkGi0CAVsACAAAAAA4v1lSp5H9BB+HYJ4bH43tC8aeuPZMf78Ng1JOhJh190AAzI5AH0AAAAFZAAgAAAAAOVKV7IuFwmYP1qVv8h0NvJmfPICu8yQhzjG7oJdTLDoBXMAIAAAAABL70XLfQLKRsw1deJ2MUvxSWKxpF/Ez73jqtbLvqbuogVsACAAAAAAvfgzIorXxE91dDt4nQxYfntTsx0M8Gzdsao5naQqcRUAAzMwAH0AAAAFZAAgAAAAAKS/1RSAQma+xV9rz04IcdzmavtrBDjOKPM+Z2NEyYfPBXMAIAAAAAAOJDWGORDgfRv8+w5nunh41wXb2hCA0MRzwnLnQtIqPgVsACAAAAAAf42C1+T7xdHEFF83+c2mF5S8PuuL22ogXXELnRAZ4boAAzMxAH0AAAAFZAAgAAAAAFeq8o82uNY1X8cH6OhdTzHNBUnCChsEDs5tm0kPBz3qBXMAIAAAAABaxMBbsaeEj/EDtr8nZfrhhhirBRPJwVamDo5WwbgvTQVsACAAAAAAMbH453A+BYAaDOTo5kdhV1VdND1avNwvshEG/4MIJjQAAzMyAH0AAAAFZAAgAAAAAI8IKIfDrohHh2cjspJHCovqroSr5N3QyVtNzFvT5+FzBXMAIAAAAABXHXteKG0DoOMmECKp6ro1MZNQvXGzqTDdZ0DUc8QfFAVsACAAAAAA/w5s++XYmO+9TWTbtGc3n3ndV4T9JUribIbF4jmDLSMAAzMzAH0AAAAFZAAgAAAAAJkHvm15kIu1OtAiaByj5ieWqzxiu/epK6c/9+KYIrB0BXMAIAAAAACzg5TcyANk0nes/wCJudd1BwlkWWF6zw3nGclq5v3SJQVsACAAAAAAvruXHTT3irPJLyWpI1j/Xwf2FeIE/IV+6Z49pqRzISoAAzM0AH0AAAAFZAAgAAAAAAYSOvEWWuSg1Aym7EssNLR+xsY7e9BcwsX4JKlnSHJcBXMAIAAAAABT48eY3PXVDOjw7JpNjOe1j2JyI3LjDnQoqZ8Je5B2KgVsACAAAAAAU2815RR57TQ9uDg0XjWjBkAKvf8yssxDMzrM4+FqP6AAAzM1AH0AAAAFZAAgAAAAAGQxC9L1e9DfO5XZvX1yvc3hTLtQEdKO9FPMkyg0Y9ZABXMAIAAAAADtmcMNJwdWLxQEArMGZQyzpnu+Z5yMmPAkvgq4eAKwNQVsACAAAAAAJ88zt4Y/Hoqh+zrf6KCOiUwHbOzCxSfp6k/qsZaYGEgAAzM2AH0AAAAFZAAgAAAAADLHK2LNCNRO0pv8n4fAsxwtUqCNnVK8rRgNiQfXpHSdBXMAIAAAAACf16EBIHRKD3SzjRW+LMOl+47QXA3CJhMzlcqyFRW22AVsACAAAAAAMGz4fAOa0EoVv90fUffwLjBrQhHATf+NdlgCR65vujAAAzM3AH0AAAAFZAAgAAAAAHiZJiXKNF8bbukQGsdYkEi95I+FSBHy1I5/hK2uEZruBXMAIAAAAADE+lZBa8HDUJPN+bF6xI9x4N7GF9pj3vBR7y0BcfFhBAVsACAAAAAAGIEN6sfqq30nyxW4dxDgXr/jz5HmvA9T1jx/pKCn4zgAAzM4AH0AAAAFZAAgAAAAAI1oa2OIw5TvhT14tYCGmhanUoYcCZtNbrVbeoMldHNZBXMAIAAAAAAx2nS0Ipblf2XOgBiUOuJFBupBhe7nb6QPLZlA4aMPCgVsACAAAAAA9xu828hugIgo0E3de9dZD+gTpVUGlwtDba+tw/WcbUoAAzM5AH0AAAAFZAAgAAAAABgTWS3Yap7Q59hii/uPPimHWXsr+DUmsqfwt/X73qsOBXMAIAAAAACKK05liW5KrmEAvtpCB1WUltruzUylDDpjea//UlWoOAVsACAAAAAAcgN4P/wakJ5aJK5c1bvJBqpVGND221dli2YicPFfuAYAAzQwAH0AAAAFZAAgAAAAABOAnBPXDp6i9TISQXvcNKwGDLepZTu3cKrB4vKnSCjBBXMAIAAAAADjjzZO7UowAAvpwyG8BNOVqLCccMFk3aDK4unUeft5ywVsACAAAAAA4zkCd4k9gvfXoD1C7vwTjNcdVJwEARh8h/cxZ4PNMfgAAzQxAH0AAAAFZAAgAAAAAHN8hyvT1lYrAsdiV5GBdd5jhtrAYE/KnSjw2Ka9hjz9BXMAIAAAAAD794JK7EeXBs+D7yOVK7nWF8SbZ/7U8gZ7nnT9JFNwTAVsACAAAAAAg8Wt1HO3NhByq2ggux2a4Lo6Gryr24rEFIqh2acrwWMAAzQyAH0AAAAFZAAgAAAAAO93bPrq8bsnp1AtNd9ETnXIz0lH/2HYN/vuw9wA3fyFBXMAIAAAAABHlls5fbaF2oAGqptC481XQ4eYxInTC29aElfmVZgDUgVsACAAAAAANoQXEWpXJpgrSNK/cKi/m7oYhuSRlp1IZBF0bqTEATcAAzQzAH0AAAAFZAAgAAAAAL1YsAZm1SA0ztU6ySIrQgCCA74V6rr0/4iIygCcaJL6BXMAIAAAAADTXWTHWovGmUR1Zg9l/Aqq9H5mOCJQQrb/Dfae7e3wKAVsACAAAAAA5dunyJK6/SVfDD0t9QlNBcFqoZnf9legRjHaLSKAoQMAAzQ0AH0AAAAFZAAgAAAAAEoFAeHk0RZ9kD+cJRD3j7PcE5gzWKnyBrF1I/MDNp5mBXMAIAAAAACgHtc2hMBRSZjKw8RAdDHK+Pi1HeyjiBuAslGVNcW5tAVsACAAAAAAXzBLfq+GxRtX4Wa9fazA49DBLG6AjZm2XODStJKH8D0AAzQ1AH0AAAAFZAAgAAAAAAW+7DmSN/LX+/0uBVJDHIc2dhxAGz4+ehyyz8fAnNGoBXMAIAAAAAA6Ilw42EvvfLJ3Eq8Afd+FjPoPcQutZO6ltmCLEr8kxQVsACAAAAAAbbZalyo07BbFjPFlYmbmv0z023eT9eLkHqeVUnfUAUAAAzQ2AH0AAAAFZAAgAAAAANBdV7M7kuYO3EMoQItAbXv4t2cIhfaT9V6+s4cg9djlBXMAIAAAAABvz4MIvZWxxrcJCL5qxLfFhXiUYB1OLHdKEjco94SgDgVsACAAAAAAK2GVGvyPIKolF/ECcmfmkVcf1/IZNcaTv96N92yGrkEAAzQ3AH0AAAAFZAAgAAAAAMoAoiAn1kc79j5oPZtlMWHMhhgwNhLUnvqkqIFvcH1NBXMAIAAAAADcJTW7WiCyW0Z9YDUYwppXhLj4Ac1povpJvcAq+i48MQVsACAAAAAAIGxGDzoeB3PTmudl4+j6piQB++e33EEzuzAiXcqGxvUAAzQ4AH0AAAAFZAAgAAAAACI3j5QP7dWHpcT6WO/OhsWwRJNASBYqIBDNzW8IorEyBXMAIAAAAABxUpBSjXwCKDdGP9hYU+RvyR+96kChfvyyRC4jZmztqAVsACAAAAAAvBCHguWswb4X0xdcAryCvZgQuthXzt7597bJ5VxAMdgAAzQ5AH0AAAAFZAAgAAAAAKsbycEuQSeNrF8Qnxqw3x3og8JmQabwGqnDbqzFRVrrBXMAIAAAAACno/3ef2JZJS93SVVzmOZSN+jjJHT8s0XYq2M46d2sLAVsACAAAAAAAt5zLJG+/j4K8rnkFtAn8IvdUVNefe6utJ3rdzgwudIAAzUwAH0AAAAFZAAgAAAAAPXIcoO8TiULqlxzb74NFg+I8kWX5uXIDUPnh2DobIoMBXMAIAAAAADR6/drkdTpnr9g1XNvKDwtBRBdKn7c2c4ZNUVK5CThdQVsACAAAAAAJqOA1c6KVog3F4Hb/GfDb3jCxXDRTqpXWSbMH4ePIJsAAzUxAH0AAAAFZAAgAAAAAEa03ZOJmfHT6/nVadvIw71jVxEuIloyvxXraYEW7u7pBXMAIAAAAADzRlBJK75FLiKjz3djqcgjCLo/e3yntI3MnPS48OORhgVsACAAAAAAnQhx4Rnyj081XrLRLD5NLpWmRWCsd0M9Hl7Jl19R0h8AAzUyAH0AAAAFZAAgAAAAAKx8NLSZUU04pSSGmHa5fh2oLHsEN5mmNMNHL95/tuC9BXMAIAAAAAA59hcXVaN3MNdHoo11OcH1aPRzHCwpVjO9mGfMz4xh3QVsACAAAAAAYIPdjV2XbPj7dBeHPwnwhVU7zMuJ+xtMUW5mIOYtmdAAAzUzAH0AAAAFZAAgAAAAAHNKAUxUqBFNS9Ea9NgCZoXMWgwhP4x0/OvoaPRWMquXBXMAIAAAAABUZ551mnP4ZjX+PXU9ttomzuOpo427MVynpkyq+nsYCQVsACAAAAAALnVK5p2tTTeZEh1zYt4iqKIQT9Z0si//Hy1L85oF+5IAAzU0AH0AAAAFZAAgAAAAALfGXDlyDVcGaqtyHkLT0qpuRhJQLgCxtznazhFtuyn/BXMAIAAAAABipxlXDq14C62pXhwAeen5+syA+/C6bN4rtZYcO4zKwAVsACAAAAAAXUf0pzUq0NhLYagWDap4uEiwq5rLpcx29rWbt1NYMsMAAzU1AH0AAAAFZAAgAAAAANoEr8sheJjg4UCfBkuUzarU9NFoy1xwbXjs5ifVDeA9BXMAIAAAAABPoyTf6M+xeZVGES4aNzVlq7LgjqZXJ/QunjYVusGUEAVsACAAAAAA1hA2gMeZZPUNytk9K+lB1RCqWRudRr7GtadJlExJf8oAAzU2AH0AAAAFZAAgAAAAAKvDiK+xjlBe1uQ3SZTNQl2lClIIvpP/5CHwY6Kb3WlgBXMAIAAAAAANnxImq5MFbWaRBHdJp+yD09bVlcFtiFDYsy1eDZj+iQVsACAAAAAAWtsyO+FxMPSIezwsV1TJD8ZrXAdRnQM6DJ+f+1V3qEkAAzU3AH0AAAAFZAAgAAAAAF49IlFH9RmSUSvUQpEPUedEksrQUcjsOv44nMkwXhjzBXMAIAAAAADJtWGbk0bZzmk20obz+mNsp86UCu/nLLlbg7ppxYn7PgVsACAAAAAA3k0Tj/XgPQtcYijH8cIlQoe/VXf15q1nrZNmg7yWYEgAAzU4AH0AAAAFZAAgAAAAAOuSJyuvz50lp3BzXlFKnq62QkN2quNU1Gq1IDsnFoJCBXMAIAAAAAAqavH1d93XV3IzshWlMnzznucadBF0ND092/2ApI1AcAVsACAAAAAAzUrK4kpoKCmcpdZlZNI13fddjdoAseVe67jaX1LobIIAAzU5AH0AAAAFZAAgAAAAALtgC4Whb4ZdkCiI30zY6fwlsxSa7lEaOAU3SfUXr02XBXMAIAAAAACgdZ6U1ZVgUaZZwbIaCdlANpCw6TZV0bwg3DS1NC/mnAVsACAAAAAAzI49hdpp0PbO7S2KexISxC16sE73EUAEyuqUFAC/J48AAzYwAH0AAAAFZAAgAAAAAF6PfplcGp6vek1ThwenMHVkbZgrc/dHgdsgx1VdPqZ5BXMAIAAAAACha3qhWkqmuwJSEXPozDO8y1ZdRLyzt9Crt2vjGnT7AAVsACAAAAAA7nvcU59+LwxGupSF21jAeAE0x7JE94tjRkJfgM1yKU8AAzYxAH0AAAAFZAAgAAAAAKoLEhLvLjKc7lhOJfx+VrGJCx9tXlOSa9bxQzGR6rfbBXMAIAAAAAAIDK5wNnjRMBzET7x/KAMExL/zi1IumJM92XTgXfoPoAVsACAAAAAAFkUYWFwNr815dEdFqp+TiIozDcq5IBNVkyMoDjharDQAAzYyAH0AAAAFZAAgAAAAADoQv6lutRmh5scQFvIW6K5JBquLxszuygM1tzBiGknIBXMAIAAAAADAD+JjW7FoBQ76/rsECmmcL76bmyfXpUU/awqIsZdO+wVsACAAAAAAPFHdLw3jssmEXsgtvl/RBNaUCRA1kgSwsofG364VOvQAAzYzAH0AAAAFZAAgAAAAAJNHUGAgn56KekghO19d11nai3lAh0JAlWfeP+6w4lJBBXMAIAAAAAD9XGJlvz59msJvA6St9fKW9CG4JoHV61rlWWnkdBRLzwVsACAAAAAAxwP/X/InJJHmrjznvahIMgj6pQR30B62UtHCthSjrP0AAzY0AH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzY1AH0AAAAFZAAgAAAAANpIljbxHOM7pydY877gpRQvYY2TGK7igqgGsavqGPBABXMAIAAAAAAqHyEu9gpurPOulApPnr0x9wrygY/7mXe9rAC+tPK80wVsACAAAAAA7gkPzNsS3gCxdFBWbSW9tkBjoR5ib+saDvpGSB3A3ogAAzY2AH0AAAAFZAAgAAAAAGR+gEaZTeGNgG9BuM1bX2R9ed4FCxBA9F9QvdQDAjZwBXMAIAAAAABSkrYFQ6pf8MZ1flgmeIRkxaSh/Eep4Btdx4QYnGGnwAVsACAAAAAApRovMiV00hm/pEcT4XBsyPNw0eo8RLAX/fuabjdU+uwAAzY3AH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzY4AH0AAAAFZAAgAAAAADgyPqQdqQrgfmJjRFAILTHzXbdw5kpKyfeoEcy6YYG/BXMAIAAAAAAE+3XsBQ8VAxAkN81au+f3FDeCD/s7KoZD+fnM1MJSSAVsACAAAAAAhRnjrXecwV0yeCWKJ5J/x12Xx4qVJahsCEVHB/1U2rcAAzY5AH0AAAAFZAAgAAAAAI0CT7JNngTCTUSei1Arw7eHWCD0jumv2rb7imjWIlWABXMAIAAAAABSP8t6ya0SyCphXMwnru6ZUDXWElN0NfBvEOhDvW9bJQVsACAAAAAAGWeGmBNDRaMtvm7Rv+8TJ2sJ4WNXKcp3tqpv5Se9Ut4AAzcwAH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcxAH0AAAAFZAAgAAAAAHIkVuNDkSS1cHIThKc/O0r2/ubaABTOi8Q1r/dvBAsEBXMAIAAAAADdHYqchEiJLM340c3Q4vJABmmth3+MKzwLYlsG6GS7sQVsACAAAAAADa+KP/pdTiG22l+ZWd30P1iHjnBF4zSNRdFm0oEK82kAAzcyAH0AAAAFZAAgAAAAAJmoDILNhC6kn3masElfnjIjP1VjsjRavGk1gSUIjh1NBXMAIAAAAAD97Ilvp3XF8T6MmVVcxMPcdL80RgQ09UoC6PnoOvZ1IQVsACAAAAAA2RK3Xng6v8kpvfVW9tkVXjpE+BSnx9/+Fw85Evs+kUEAAzczAH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzc0AH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzc1AH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzc2AH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzc3AH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzc4AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzc5AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzgwAH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzgxAH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzgyAH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzgzAH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzg0AH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzg1AH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzg2AH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzg3AH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzg4AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzg5AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzkwAH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzkxAH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzkyAH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzkzAH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzk0AH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzk1AH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzk2AH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzk3AH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzk4AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzk5AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzEwMAB9AAAABWQAIAAAAADJDdC9aEFl4Y8J/awHbnXGHjfP+VXQilPHJg7ewaJI7AVzACAAAAAAE+tqRl6EcBMXvbr4GDiNIYObTsYpa1n6BJk9EjIJVicFbAAgAAAAAJVc+HYYqa0m1Hq6OiRX8c0iRnJYOt6AJAJoG0sG3GMSAAMxMDEAfQAAAAVkACAAAAAA3F9rjEKhpoHuTULVGgfUsGGwJs3bISrXkFP1v6KoQLgFcwAgAAAAAIBf0tXw96Z/Ds0XSIHX/zk3MzUR/7WZR/J6FpxRWChtBWwAIAAAAABWrjGlvKYuTS2s8L9rYy8Hf0juFGJfwQmxVIjkTmFIGQADMTAyAH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzEwMwB9AAAABWQAIAAAAACMtPm12YtdEAvqu6Eji1yuRXnu1RJP6h0l7pH3lSH4MwVzACAAAAAAENyCFfyUAh1veQBGx+cxiB7Sasrj41jzCGflZkB5cRMFbAAgAAAAAKdI2LMqISr/T5vuJPg6ZRBm5fVi2aQCc4ra3A4+AjbDAAMxMDQAfQAAAAVkACAAAAAAvlI4lDcs6GB1cnm/Tzo014CXWqidCdyE5t2lknWQd4QFcwAgAAAAAD60SpNc4O2KT7J0llKdSpcX1/Xxs97N715a1HsTFkmBBWwAIAAAAABuuRkJWAH1CynggBt1/5sPh9PoGiqTlS24D/OE2uHXLQADMTA1AH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzEwNgB9AAAABWQAIAAAAABb6LXDWqCp1beQgQjj8I3sRTtFhlrmiBi+h/+ikmrvugVzACAAAAAA9stpgTecT7uTyaGNs3K9Bp0A7R0QaIAOfscyMXHBPX8FbAAgAAAAAHUt+McyXrJ1H8SwnHNVO181Ki8vDAM1f7XI26mg95ZDAAMxMDcAfQAAAAVkACAAAAAA97NTT+81PhDhgptNtp4epzA0tP4iNb9j1AWkiiiKGM8FcwAgAAAAAKPbHg7ise16vxmdPCzksA/2Mn/qST0L9Xe8vnQugVkcBWwAIAAAAABB0EMXfvju4JU/mUH/OvxWbPEl9NJkcEp4iCbkXI41fAADMTA4AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzEwOQB9AAAABWQAIAAAAADQnslvt6Hm2kJPmqsTVYQHE/wWeZ4bE1XSkt7TKy0r1gVzACAAAAAA8URTA4ZMrhHPvlp53TH6FDCzS+0+61qHm5XK6UiOrKEFbAAgAAAAAHQbgTCdZcbdA0avaTmZXUKnIS7Nwf1tNrcXDCw+PdBRAAMxMTAAfQAAAAVkACAAAAAAhujlgFPFczsdCGXtQ/002Ck8YWQHHzvWvUHrkbjv4rwFcwAgAAAAALbV0lLGcSGfE7mDM3n/fgEvi+ifjl7WZ5b3aqjDNvx9BWwAIAAAAACbceTZy8E3QA1pHmPN5kTlOx3EO8kJM5PUjTVftw1VpgADMTExAH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzExMgB9AAAABWQAIAAAAACfw9/te4GkHZAapC9sDMHHHZgmlTrccyJDPFciOMSOcwVzACAAAAAAIIC1ZpHObvmMwUfqDRPl4C1aeuHwujM1G/yJbvybMNAFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5AAMxMTMAfQAAAAVkACAAAAAAkxHJRbnShpPOylLoDdNShfILeA1hChKFQY9qQyZ5VmsFcwAgAAAAAKidrY+rC3hTY+YWu2a7fuMH2RD/XaiTIBW1hrxNCQOJBWwAIAAAAACW0kkqMIzIFMn7g+R0MI8l15fr3k/w/mHtY5n6SYTEwAADMTE0AH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzExNQB9AAAABWQAIAAAAABxMy7X5hf7AXGDz3Y/POu1ZpkMlNcSvSP92NOO/Gs7wAVzACAAAAAAHJshWo2T5wU2zvqCyJzcJQKQaHFHpCpMc9oWBXkpUPoFbAAgAAAAAGeiJKzlUXAvL0gOlW+Hz1mSa2HsV4RGmyLmCHlzbAkoAAMxMTYAfQAAAAVkACAAAAAAlqbslixl7Zw3bRlibZbe/WmKw23k8uKeIzPKYEtbIy0FcwAgAAAAAHEKwpUxkxOfef5HYvulXPmdbzTivwdwrSYIHDeNRcpcBWwAIAAAAADuPckac21Hrg/h0kt5ShJwVEZ9rx6SOHd2+HDjqxEWTQADMTE3AH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzExOAB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFbAAgAAAAAIgbpyYtJj5513Z5XYqviH/HXG/5+mqR52iBbfqMmDtZAAMxMTkAfQAAAAVkACAAAAAAJRzYK0PUwr9RPG2/7yID0WgcTJPB2Xjccp5LAPDYunkFcwAgAAAAAIIh24h3DrltAzNFhF+MEmPrZtzr1PhCofhChZqfCW+jBWwAIAAAAAAzRNXtL5o9VXMk5D5ylI0odPDJDSZZry1wfN+TedH70gADMTIwAH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzEyMQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FbAAgAAAAAJci0w1ZB8W2spJQ+kMpod6HSCtSR2jrabOH+B0fj3A4AAMxMjIAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWwAIAAAAAAlswzAl81+0DteibwHD+CG5mZJrfHXa9NnEFRtXybzzwADMTIzAH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzEyNAB9AAAABWQAIAAAAAAfPUoy7QyZKhIIURso+mkP9qr1izbjETqF5s22GwjCjAVzACAAAAAAvLMsIDQ/go4VUxeh50UHmsvMvfx51cwyONnRD2odvC0FbAAgAAAAAKMb+1CodEalAFnDrEL1Ndt8ztamZ+9134m9Kp3GQgd+AAMxMjUAfQAAAAVkACAAAAAAE3ZqUar0Bq2zWbARE0bAv98jBlK9UJ73/xcwdMWWlSkFcwAgAAAAAK4M+MmC+9sFiFsumMyJZQKxWmmJiuG9H7IzKw083xxkBWwAIAAAAAAqkAONzhvMhkyL1D/6h7QQxEkdhC3p2WjXH+VGq5qCqQADMTI2AH0AAAAFZAAgAAAAAMo8FJiOq63cAmyk2O7eI7GcbQh/1j4RrMTqly3rexftBXMAIAAAAADjVmpd0WiRGTw/gAqEgGolt2EI7Csv14vKdmYoMD0aAgVsACAAAAAA07XQBzBUQMNw7F2/YxJjZNuPVpHTTgbLd1oGk77+bygAAzEyNwB9AAAABWQAIAAAAACu5IGaIx7A3Jvly/kzlCsSA4s3iJwuIl8jEdRH0k93NwVzACAAAAAA9NRUyxYE+t0Xyosyt6vIfMFW/vBoYg6sR+jBNs4JAxIFbAAgAAAAAAzyZ91dx+0oMlOVAjRGiMrPySikY/U9eMEB4WJb3uWtAAMxMjgAfQAAAAVkACAAAAAALkRy0GJInXYLA+cgjs6Myb0a+Gu9hgXhHvhLNoGWfckFcwAgAAAAANbALyt9zCSvwnLaWCd2/y2eoB7qkWTvv1Ldu8r40JPuBWwAIAAAAAD4Fl5bV5sz4isIE9bX+lmAp+aAKaZgVYVZeVfrItkCZAADMTI5AH0AAAAFZAAgAAAAAGoUK/DSWhT8LZhszSUqDbTrp8cSA7rdqmADKL+MILtTBXMAIAAAAABHnEE9bVa6lvhfhEMkkV2kzSSxH/sMW/FIJuw3CzWs6wVsACAAAAAAanavcBdqZxgRGKvEK95wTmeL1K1CeDSXZsXUAs81uOgAAzEzMAB9AAAABWQAIAAAAAC922ZDQE3h2fQKibGMZ9hV0WNlmrPYYSdtaSyYxsWYqgVzACAAAAAAagMovciKK6WVjIc2cCj8nK5O/gVOFFVeVAJpRp89tmQFbAAgAAAAAKcTFfPQzaFiAtSFhqbN02sCE1BKWJSrRfGN5L6oZwzkAAMxMzEAfQAAAAVkACAAAAAAtK+JqX3K/z2txjAU15DgX4y90DS2YLfIJFolCOkJJJwFcwAgAAAAAMnR5V7gfX7MNqqUdL5AkWlkhyFXaBRVNej+Rcn8lrQkBWwAIAAAAAA2cDNRXZuiC241TGRvdFyctJnrNcdbZOP9zHio81tkngADMTMyAH0AAAAFZAAgAAAAAAeGrIMK/bac6kPczxbvRYqKMkcpeI2FjdMpD91FDWIvBXMAIAAAAAAix62z1LeS8yvSXCl5gHSIomjyx76fF3S1lp9k900hygVsACAAAAAAiYwzf2m71aWFD5ajcXyW2JX2EzQOkBroTGMg29nLPYIAAzEzMwB9AAAABWQAIAAAAACphf298InM0Us4HT8o1W1MGw0D/02vd7Jh+U0h7qaFaQVzACAAAAAAFXtk7YpqsOJxsqGWSIL+YcBE96G3Zz9D31gPqDW94y8FbAAgAAAAAAOrS1KVA94rjB1jZ1pPocpCeBG+B14RzWoHqVDpp7JbAAMxMzQAfQAAAAVkACAAAAAATLDS2cuDVM3yDMuWNgk2iGKBTzPpfJMbvxVOSY39ZfcFcwAgAAAAAPT5wRi2cLHIUflXzm6EQB/m7xdThP80ir1VV/JBBqvxBWwAIAAAAAB9lEtZS0aXCFbCtSbhnis27S5IPcfWGygHW8AHn3QqzwADMTM1AH0AAAAFZAAgAAAAAJNjExiZVX7jfFGfYpQu16qxLN0YPqVU/5CQ/Y67YSinBXMAIAAAAABMpm2+6KrkRUlXzQoMPHrQmIO6dkQz66tYdfTeA3dKqQVsACAAAAAAFXobHiMLvNZuEPr8jtewCX2J93EZG3JNeyVg92fue6YAAzEzNgB9AAAABWQAIAAAAABlFkYtLCx901X6QVVMkSn6Z7k30UF4xHaA0OZJJ9bdyQVzACAAAAAATez+F9GHcGzTp7jjv4feboUNb8JCkIp4EqcPFisnq7MFbAAgAAAAACE7JvOpBgMoZ7kRd4QbxIhxukPTUxXpzhjnBHiR7XoRAAMxMzcAfQAAAAVkACAAAAAA8NJKN0IxZnruhswGQkiruv8Ih0EMwDcSZx/Xasup9dkFcwAgAAAAAKaJZRxzA+Igeydvuk6cSwUHXcrmT4PjhuPu//FslpdnBWwAIAAAAAD53Rok1Vq/PMAnXmarqoHJ0PEyYUBmVESa9hIpCv/G9QADMTM4AH0AAAAFZAAgAAAAABHxHdEClz7hbSSgE58+dWLlSMJnoPz+jFxp4bB1GmLQBXMAIAAAAAD3nSvT6aGD+A110J/NwEfp0nPutlmuB5B+wA3CC3noGAVsACAAAAAA3Apjd+TapONB7k5wBVwTWgn8t+Sq2oyyU5/+as109RcAAzEzOQB9AAAABWQAIAAAAAC/o8qW/ifk3KuJ01VFkyNLgQafxB5/bGs2G5VyyVafOwVzACAAAAAA1bMqAFGDHSl6BYNLbxApvkAv2K1/oafywiX0MDz1dGUFbAAgAAAAAHJXLlId3edFoniLD/9K2A5973MeP2Ro31flDyqm3l5QAAMxNDAAfQAAAAVkACAAAAAAY2V8I1bz3a1AxTtmED6UhdhA09huFkuuEX8R+d/WDPUFcwAgAAAAAPTVoNRiI76tcRKqd+JBBVyy4+YcKST42p0QX2BtmQ2VBWwAIAAAAACcxt9hg14WqPNiDv1MkqVljM2e2KJEv53lA17LhV6ZigADMTQxAH0AAAAFZAAgAAAAAO2kSsW0WGN9AOtK4xK2SHrGhWiaAbMEKT4iZkRpaDN/BXMAIAAAAABKGzQcPM8LT2dwOggxoWjv/1imYWabbG/G4kBw8OWaxAVsACAAAAAAC9hLK1dScQTAqg+YAG3ObdPzg2Xet57HmOFpGmyUR9UAAzE0MgB9AAAABWQAIAAAAAAiCwzNEEaH/mDam68IdDftnhthyUFdb+ZCNSBQ91WlHQVzACAAAAAA7tHyHcxCzmbJeFYZyPm4mEgkTGKOvwY4MX82OvH0Jn8FbAAgAAAAAAb5IAbZ1hXCNegQ+S+C9i/Z8y6sS8KeU04V6hXa2ml6AAMxNDMAfQAAAAVkACAAAAAAGuCHVNJSuoVkpPOnS5s89GuA+BLi2IPBUr2Bg1sWEPIFcwAgAAAAAEl1gncS5/xO7bQ/KQSstRV3rOT2SW6nV92ZANeG2SR6BWwAIAAAAAA9LOcKmhek8F2wAh8yvT/vjp2gaouuO+Hmv10lwAeWPAADMTQ0AH0AAAAFZAAgAAAAAMfxz7gEaoCdPvXrubDhCZUS0ARLZc1svgbXgMDlVBPgBXMAIAAAAAB6a5dDA3fuT5Vz2KvAcbUEFX/+B7Nw2p1QqbPoQ5TTuAVsACAAAAAAcf/y75UOuI62A6vWH7bYr/5Jz+nirZVYK/81trN6XOQAAzE0NQB9AAAABWQAIAAAAACnYsqF/VzmjIImC9+dqrHO1TM6lJ6fRwM0mM6Wf6paOwVzACAAAAAA5tgZzch8uDCR1ky3SllVaKVpxAlbrhvlNDTazZZRZOAFbAAgAAAAALeGiLJS4z2zhgVpxzyPdRYyACP9QzQBOob34YrIZumCAAMxNDYAfQAAAAVkACAAAAAAEC0sIVmadtW4YMuRXH7RpAhXclsd+3bmqGXCMeaT014FcwAgAAAAABPpXh0uzpsJJB+IRUNajmMB9WGwswfpw5T9xk3Xj6ANBWwAIAAAAAAmf+NYh9TZ/QRu3w/GQz66n7DtfbJijN3G7KzeL8lstAADMTQ3AH0AAAAFZAAgAAAAABaIB3n49Xm9cOafSrQsE0WCcYp8rMIO/qVwIlMF5YLRBXMAIAAAAAC9EyWJV3xOu9bzgdJ/yX+ko7qLf1u3AxNMataW2C9EzQVsACAAAAAAvVbDkLxXx2DcMLifIQ3K0IIJcLcAG9DUrNfI6aoUjNcAAzE0OAB9AAAABWQAIAAAAAA5rZItA/cocRnngYqcJ3nBXQ+l688aKz3EQyLbYYunPAVzACAAAAAAwKyA+L7TgxztPClLrIMk2JXR+w7c04N3ZOqPgjvrIvsFbAAgAAAAACzvZ33h6aWEe8hmo+1f6OXJ72FY5hvWaUuha64ZV3KFAAMxNDkAfQAAAAVkACAAAAAA3htn7oHJ0YYpIrs+Mzyh85Ys67HwAdv5LQl1mCdoMWkFcwAgAAAAAEHjCtNNLenHuSIYux6ezAHsXDaj2DlTF67ToDhDDe6HBWwAIAAAAAD+P4H0sk9jOd+7vOANt2/1Ectb+4ZRGPE8GkHWNXW3MgADMTUwAH0AAAAFZAAgAAAAAEnt18Km/nqggfIJWxzTr9r3hnXNaueG6XO9A5G11LnGBXMAIAAAAAD7QxzGMN/ard5TfFLecE6uusMmXG2+RBsBR+/NCQHUwAVsACAAAAAAQEZ1ZZ8GC8rdbg7s87OM5Gr9qkTXS9+P5DuAZxj5Gl4AAzE1MQB9AAAABWQAIAAAAAAVAKK/GoY8AACu/hyMpO4hdLq6JnEyWNzkyci9sbaD/wVzACAAAAAA2HmeqpMlvvBpV2zQTYIRmsc4MFlfHRwLof0ycJgMg/MFbAAgAAAAACdltCeWi5E/q1Li1eXLChpM2D9QQSGLBZ82NklQSc0oAAMxNTIAfQAAAAVkACAAAAAAhHyq1GQC/GiMwpYjcsfkNxolJ10ARKjIjfkW1Wipzi0FcwAgAAAAAD/uaGWxTDq87F8XZ6CrFI+RNa8yMqfSZdqK00Kj833BBWwAIAAAAAD6aEdOO0CsQGagioOCvANPCEHSpJ8BSixlPBq5ERhB7AADMTUzAH0AAAAFZAAgAAAAABAJJxHoZD+MQBWqm9UM9Dd3z5ZohIZGWRaRVRsMptKQBXMAIAAAAADrE/ca+gqj/SH4oao4wE4qn2ovoTydzcMbDbrfnUs3zAVsACAAAAAAeNCIQN6hVnGJinytQRFGlQ2ocoprXNqpia+BSxzl+uwAAzE1NAB9AAAABWQAIAAAAAAv01wz7VG9mTepjXQi6Zma+7b/OVBaKVkWNbgDLr1mFgVzACAAAAAA0I5sxz8r6wkCp5Tgvr+iL4p6MxSOq5d3e1kZG+0b7NkFbAAgAAAAAIA32v6oGkAOS96HexGouNTex+tLahtx9QF2dgGClk6WAAMxNTUAfQAAAAVkACAAAAAAWXecRwxSon68xaa9THXnRDw5ZfzARKnvvjTjtbae6T0FcwAgAAAAAPh0UfUMEo7eILCMv2tiJQe1bF9qtXq7GJtC6H5Va4fIBWwAIAAAAADqFr1ThRrTXNgIOrJWScO9mk86Ufi95IDu5gi4vP+HWQADMTU2AH0AAAAFZAAgAAAAAEY5WL8/LpX36iAB1wlQrMO/xHVjoO9BePVzbUlBYo+bBXMAIAAAAABoKcpadDXUARedDvTmzUzWPe1jTuvD0z9oIcZmKuiSXwVsACAAAAAAJuJbwuaMrAFoI+jU/IYr+k4RzAqITrOjAd3HWCpJHqEAAzE1NwB9AAAABWQAIAAAAADnJnWqsfx0xqNnqfFGCxIplVu8mXjaHTViJT9+y2RuTgVzACAAAAAAWAaSCwIXDwdYxWf2NZTly/iKVfG/KDjHUcA1BokN5sMFbAAgAAAAAJVxavipE0H4/JQvhagdytXBZ8qGooeXpkbPQ1RfYMVHAAMxNTgAfQAAAAVkACAAAAAAsPG7LaIpJvcwqcbtfFUpIjj+vpNj70Zjaw3eV9T+QYsFcwAgAAAAAJQ71zi0NlCyY8ZQs3IasJ4gB1PmWx57HpnlCf3+hmhqBWwAIAAAAACD58TO6d+71GaOoS+r73rAxliAO9GMs4Uc8JbOTmC0OwADMTU5AH0AAAAFZAAgAAAAAAGiSqKaQDakMi1W87rFAhkogfRAevnwQ41onWNUJKtuBXMAIAAAAAASgiDpXfGh7E47KkOD8MAcX8+BnDShlnU5JAGdnPdqOAVsACAAAAAAI+2TTQIgbFq4Yr3lkzGwhG/tqChP7hRAx2W0fNaH6jcAAzE2MAB9AAAABWQAIAAAAAB7L4EnhjKA5xJD3ORhH2wOA1BvpnQ+7IjRYi+jjVEaJAVzACAAAAAAuhBIm0nL3FJnVJId+7CKDASEo+l2E89Z9/5aWSITK4AFbAAgAAAAALtSICOzQDfV9d+gZuYxpEj6cCeHnKTT+2G3ceP2H65kAAMxNjEAfQAAAAVkACAAAAAAaROn1NaDZFOGEWw724dsXBAm6bgmL5i0cki6QZQNrOoFcwAgAAAAANVT8R6UvhrAlyqYlxtmnvkR4uYK/hlvyQmBu/LP6/3ZBWwAIAAAAAD+aHNMP/X+jcRHyUtrCNkk1KfMtoD3GTmShS8pWGLt+AADMTYyAH0AAAAFZAAgAAAAADqSR5e0/Th59LrauDA7OnGD1Xr3H3NokfVxzDWOFaN7BXMAIAAAAACt30faNwTWRbvmykDpiDYUOCwA6QDbBBYBFWS7rdOB4AVsACAAAAAAF7SvnjjRk5v2flFOKaBAEDvjXaL1cpjsQLtK2fv9zdQAAzE2MwB9AAAABWQAIAAAAADmtb1ZgpZjSeodPG/hIVlsnS8hoRRwRbrTVx89VwL62AVzACAAAAAAi38e1g6sEyVfSDkzZbaZXGxKI/zKNbMasOl2LYoWrq8FbAAgAAAAAALACk0KcCDN/Kv8WuazY8ORtUGkOZ5Dsm0ys1oOppp/AAMxNjQAfQAAAAVkACAAAAAAf/f7AWVgBxoKjr7YsEQ4w/fqSvuQWV2HMiA3rQ7ur0sFcwAgAAAAADkkeJozP6FFhUdRIN74H4UhIHue+eVbOs1NvbdWYFQrBWwAIAAAAAB55FlHAkmTzAYj/TWrGkRJw2EhrVWUnZXDoMYjyfB/ZwADMTY1AH0AAAAFZAAgAAAAAI2WEOymtuFpdKi4ctanPLnlQud+yMKKb8p/nfKmIy56BXMAIAAAAADVKrJmhjr1rfF3p+T+tl7UFd1B7+BfJRk0e7a4im7ozgVsACAAAAAA5E7Ti3PnFiBQoCcb/DN7V1uM3Xd6VKiexPKntssFL7kAAzE2NgB9AAAABWQAIAAAAAAuHU9Qd79hjyvKOujGanSGDIQlxzsql8JytTZhEnPw+AVzACAAAAAAjF2gV/4+sOHVgDd/oR5wDi9zL7NGpGD+NsEpGXy/a4QFbAAgAAAAAJzMoyojYV6Ed/LpVN5zge93Odv3U7JgP7wxeRaJZGTdAAMxNjcAfQAAAAVkACAAAAAA7dQDkt3iyWYCT94d7yqUtPPwp4qkC0ddu+HFdHgVKEkFcwAgAAAAANuYvtvZBTEq4Rm9+5eb7VuFopowkrAuv86PGP8Q8/QvBWwAIAAAAACeqXoAOQOE4j0zRMlkVd8plaW0RX1npsFvB38Xmzv7sAADMTY4AH0AAAAFZAAgAAAAAAwnZSDhL4tNGYxlHPhKYB8s28dY5ScSwiKZm3UhT8U3BXMAIAAAAABDoY6dhivufTURQExyC9Gx3ocpl09bgbbQLChj3qVGbgVsACAAAAAAF+1nS7O0v85s3CCy+9HkdeoEfm2C6ZiNbPMMnSfsMHUAAzE2OQB9AAAABWQAIAAAAAC2VuRdaC4ZJmLdNOvD6R2tnvkyARteqXouJmI46V306QVzACAAAAAAMn1Z6B35wFTX9mEYAPM+IiJ5hauEwfD0CyIvBrxHg7IFbAAgAAAAAOG6DvDZkT9B/xZWmjao2AevN7MMbs3Oh9YJeSd/hZ+hAAMxNzAAfQAAAAVkACAAAAAAVerb7qVNy457rNOHOgDSKyWl5ojun7iWrv1uHPXrIZQFcwAgAAAAAIDcYS9j5z+gx0xdJj09L7876r/vjvKTi/d3bXDE3PhyBWwAIAAAAADuhVLqb1Bkrx8aNymS+bx2cL8GvLFNH4SAi690DUgnWQADMTcxAH0AAAAFZAAgAAAAAH/E44yLxKCJjuSmU9A8SEhbmkDOx1PqqtYcZtgOzJdrBXMAIAAAAABgLh9v2HjBbogrRoQ82LS6KjZQnzjxyJH4PH+F3jupSAVsACAAAAAAIlO46ehXp4TqpDV0t6op++KO+uWBFh8iFORZjmx2IjkAAzE3MgB9AAAABWQAIAAAAAAlNUdDL+f/SSQ5074mrq0JNh7CTXwTbbhsQyDwWeDVMwVzACAAAAAANIH2IlSNG0kUw4qz0budjcWn8mNR9cJlYUqPYdonucAFbAAgAAAAAJMrOUOyiu5Y3sV76zwEFct8L7+i8WGlQI2+8z2W2kzaAAMxNzMAfQAAAAVkACAAAAAASZ+CvUDtlk/R4HAQ3a+PHrKeY/8ifAfh0oXYFqliu80FcwAgAAAAAJelpzPgM65OZFt/mvGGpwibclQ49wH+1gbUGzd9OindBWwAIAAAAAD9qeDchteEpVXWcycmD9kl9449C1dOw0r60TBm5jK+cQADMTc0AH0AAAAFZAAgAAAAAN9fkoUVbvFV2vMNMAkak4gYfEnzwKI3eDM3pnDK5q3lBXMAIAAAAACnDkgVNVNUlbQ9RhR6Aot2nVy+U4km6+GHPkLr631jEAVsACAAAAAANzg/BnkvkmvOr8nS4omF+q9EG/4oisB+ul4YHi938hwAAzE3NQB9AAAABWQAIAAAAAASyK3b1nmNCMptVEGOjwoxYLLS9fYWm/Zxilqea0jpEQVzACAAAAAADDHsGrbqlKGEpxlvfyqOJKQJjwJrzsrB7k3HG0AUJbkFbAAgAAAAAKwx3S4XfDZh4+LuI9jf7XgUh5qiefNv87JD4qvVRfPSAAMxNzYAfQAAAAVkACAAAAAAlSP9iK31GlcG9MKGbLmq+VXMslURr+As736rrVNXcsUFcwAgAAAAAAvbj0zfq9zzi8XReheKFbCB+h9IsOLgXPPpI5vrEJNZBWwAIAAAAABXvoZhaQE7ogWjeBjceVkp03N20cKYP3TA8vuNsgpfAgADMTc3AH0AAAAFZAAgAAAAAOJNORH8Bev97gVU7y6bznOxJ+E6Qoykur1QP76hG1/7BXMAIAAAAAC+C1PtOOrSZgzBAGhr+dPe/kR0JUw9GTwLVNr61xC1aAVsACAAAAAAeA/L8MQIXkamaObtMPLpoDoi5FypA5WAPtMeMrgi0eQAAzE3OAB9AAAABWQAIAAAAAAKcHzLUomavInN6upPkyWhAqYQACP/vdVCIYpiy6U6HgVzACAAAAAATsR4KItY6R2+U7Gg6sJdaEcf58gjd1OulyWovIqfxKcFbAAgAAAAAFbm10ko67ahboAejQdAV0U2uA5OhZYdb8XUFJ8OL46LAAMxNzkAfQAAAAVkACAAAAAAqTOLiMpCdR59tLZzzIPqJvbCNvz2XQL9ust0qYaehtcFcwAgAAAAAArefox/3k5xGOeiw2m6NUdzuGxmPwcu5IFcj+jMwHgHBWwAIAAAAADLZGFJ7MQd5JXMgMXjqZO5LDLxcFClcXPlnRMWRn+1oAADMTgwAH0AAAAFZAAgAAAAAIPSqSeVzSRgNVNmrPYHmUMgykCY27NbdDUNhE5kx/SgBXMAIAAAAAAhX90nNfxyXmZe/+btZ7q6xMX4PFyj0paM1ccJ/5IUUQVsACAAAAAA419oHmD2W0SYoOMwhrhrp8jf68fg9hTkaRdCuVd3CN0AAzE4MQB9AAAABWQAIAAAAACLn5DxiqAosHGXIAY96FwFKjeqrzXWf3VJIQMwx1fl4gVzACAAAAAAindvU27nveutopdvuHmzdENBbeGFtI3Qcsr07jxmvm8FbAAgAAAAAPvl9pBStQvP4OGkN5v0MghUY6djm9n7XdKKfrW0l1sMAAMxODIAfQAAAAVkACAAAAAA7i2S6rHRSPBwZEn59yxaS7HiYBOmObIkeyCcFU42kf8FcwAgAAAAAGb3RSEyBmgarkTvyLWtOLJcPwCKbCRkESG4RZjVmY4iBWwAIAAAAADB2/wo5CSHR4ANtifY6ZRXNTO5+O8qP82DfAiAeanpZwADMTgzAH0AAAAFZAAgAAAAAFz+M+H/Z94mdPW5oP51B4HWptp1rxcMWAjnlHvWJDWrBXMAIAAAAACBFEOQyL7ZHu4Cq33QvXkmKuH5ibG/Md3RaED9CtG5HwVsACAAAAAAfggtJTprQ/yZzj7y5z9KvXsdeXMWP0yUXMMJqpOwI88AAzE4NAB9AAAABWQAIAAAAAAE7c2x3Z3aM1XGfLNk/XQ9jCazNRbGhVm7H8c2NjS5ywVzACAAAAAARJ9h8fdcwA19velF3L/Wcvi2rCzewlKZ2nA0p8bT9uwFbAAgAAAAAJtWe6b4wK2Hae2dZm/OEpYQnvoZjz4Sz5IgJC2wInecAAMxODUAfQAAAAVkACAAAAAAVoRt9B9dNVvIMGN+ea5TzRzQC+lqSZ8dd/170zU5o9cFcwAgAAAAAEwM95XZin5mv2yhCI8+ugtKuvRVmNgzzIQN0yi1+9aIBWwAIAAAAAAMGBq72n00rox3uqhxSB98mkenTGCdbbUF1gXrgottzgADMTg2AH0AAAAFZAAgAAAAAKRDkjyWv/etlYT4GyoXrmBED2FgZHnhc+l9Wsl06cH2BXMAIAAAAABohlpm3K850Vndf3NmNE0hHqDlNbSR8/IvMidQ3LnIZAVsACAAAAAAW42nGHa6q2MCAaaPVwaIDfr8QLyQwjKq23onZJYsqVsAAzE4NwB9AAAABWQAIAAAAAC3DFh5oklLCNLY90bgWm68dFXz65JpAZSp1K99MBTPAQVzACAAAAAAQgZecmxEUZVHoptEQClDwAf8smI3WynQ/i+JBP0g+kQFbAAgAAAAAEUSQGVnAPISD6voD0DiBUqyWKgt2rta0tjmoe+LNt6IAAMxODgAfQAAAAVkACAAAAAAQ5WKvWSB503qeNlOI2Tpjd5blheNr6OBO8pfJfPNstcFcwAgAAAAAKwHgQLSDJ5NwLBQbY5OnblQIsVDpGV7q3RCbFLD1U4/BWwAIAAAAACQ5nED99LnpbqXZuUOUjnO2HTphEAFBjLD4OZeDEYybgADMTg5AH0AAAAFZAAgAAAAAGfhFY3RGRm5ZgWRQef1tXxHBq5Y6fXaLAR4yJhrTBplBXMAIAAAAACKEF0ApLoB6lP2UqTFsTQYNc9OdDrs/vziPGzttGVLKQVsACAAAAAArOO6FyfNRyBi0sPT5iye7M8d16MTLcwRfodZq4uCYKEAAzE5MAB9AAAABWQAIAAAAAAIM73gPcgzgotYHLeMa2zAU4mFsr7CbILUZWfnuKSwagVzACAAAAAAJCSu98uV8xv88f2BIOWzt6p+6EjQStMBdkGPUkgN79cFbAAgAAAAAMGqPGMPxXbmYbVfSa/japvUljht1zZT33TY7ZjAiuPfAAMxOTEAfQAAAAVkACAAAAAAkWmHCUsiMy1pwZTHxVPBzPTrWFBUDqHNrVqcyyt7nO8FcwAgAAAAAMv2CebFRG/br7USELR98sIdgE9OQCRBGV5JZCO+uPMgBWwAIAAAAABt7qSmn3gxJu7aswsbUiwvO+G6lXj/Xhx+J/zQyZxzLAADMTkyAH0AAAAFZAAgAAAAAGInUYv0lP/rK7McM8taEHXRefk8Q2AunrvWqdfSV7UaBXMAIAAAAACE+WPxJ3gan7iRTbIxXXx+bKVcaf8kP4JD8DcwU0aL7wVsACAAAAAAUC4eTprX4DUZn2X+UXYU6QjtiXk+u57yoOPBbPQUmDkAAzE5MwB9AAAABWQAIAAAAACmHlg2ud3cplXlTsNTpvNnY6Qm1Fce0m899COamoDjaQVzACAAAAAArtJQeJIlepBWRU2aYar7+YGYVQ7dfDc1oxgTmA8r9q0FbAAgAAAAAOk45vg5VqZHAFCO3i0Z52SZi5RADf8NXwf68T5yad/DAAMxOTQAfQAAAAVkACAAAAAApzcWSAbZWV/Rq+ylRNqqlJqNVR4fhXrz4633/MQOQgcFcwAgAAAAAN/jz/bsEleiuCl+li83EWlG6UMHA8CyaOMRKCkXkSCPBWwAIAAAAAC3Sd+Qg+uFDKpGZHbrQgokXHQ1az1aFl4YK343OB6hcQAAEmNtAAAAAAAAAAAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAEAAAASc3AAAQAAAAAAAAAQdGYAAQAAABNtbgD/////Y46NN8CHrb4J7f/fE214AP////9jjo03wIetvgnt/18A", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.yml new file mode 100644 index 0000000000..d9aecf4646 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Aggregate.yml @@ -0,0 +1,455 @@ +description: fle2v2-Rangev2-Decimal-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedDecimalNoPrecision": { $gt: { $numberDecimal: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedDecimalNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.json new file mode 100644 index 0000000000..edca7724a7 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.json @@ -0,0 +1,1016 @@ +{ + "description": "fle2v2-Rangev2-Decimal-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalNoPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberInt": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.yml new file mode 100644 index 0000000000..0ea8a7d08b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Correctness.yml @@ -0,0 +1,333 @@ +description: fle2v2-Rangev2-Decimal-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $in: [$numberDecimal: "0.0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gte: { $numberDecimal: "0.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "1.0" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lt: { $numberDecimal: "1.0" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $lte: { $numberDecimal: "1.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0" } } }, + } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "0.0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalNoPrecision: { $in: [$numberDecimal: "0.0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Int" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDecimalNoPrecision: { $numberInt: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Int" + operations: + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gte: { $numberInt: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.json new file mode 100644 index 0000000000..4b0121ac22 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.json @@ -0,0 +1,1179 @@ +{ + "description": "fle2v2-Rangev2-Decimal-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.yml new file mode 100644 index 0000000000..b2dd342460 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Delete.yml @@ -0,0 +1,324 @@ +description: fle2v2-Rangev2-Decimal-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedDecimalNoPrecision": { $gt: { $numberDecimal: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedDecimalNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json new file mode 100644 index 0000000000..2697549f6a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.json @@ -0,0 +1,1969 @@ +{ + "description": "fle2v2-Rangev2-Decimal-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml new file mode 100644 index 0000000000..5aeccd18ba --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-FindOneAndUpdate.yml @@ -0,0 +1,454 @@ +description: fle2v2-Rangev2-Decimal-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimalNoPrecision": { $numberDecimal: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedDecimalNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "DR1jAAADcGF5bG9hZACxYgAABGcAnWIAAAMwAH0AAAAFZAAgAAAAAJu2KgiI8vM+kz9qD3ZQzFQY5qbgYqCqHG5R4jAlnlwXBXMAIAAAAAAAUXxFXsz764T79sGCdhxvNd5b6E/9p61FonsHyEIhogVsACAAAAAAt19RL3Oo5ni5L8kcvgOJYLgVYyXJExwP8pkuzLG7f/kAAzEAfQAAAAVkACAAAAAAPQPvL0ARjujSv2Rkm8r7spVsgeC1K3FWcskGGZ3OdDIFcwAgAAAAACgNn660GmefR8jLqzgR1u5O+Uocx9GyEHiBqVGko5FZBWwAIAAAAADflr+fsnZngm6KRWYgHa9JzK+bXogWl9evBU9sQUHPHQADMgB9AAAABWQAIAAAAAD2Zi6kcxmaD2mY3VWrP+wYJMPg6cSBIYPapxaFQxYFdQVzACAAAAAAM/cV36BLBY3xFBXsXJY8M9EHHOc/qrmdc2CJmj3M89gFbAAgAAAAAOpydOrKxx6m2gquSDV2Vv3w10GocmNCFeOo/fRhRH9JAAMzAH0AAAAFZAAgAAAAAOaNqI9srQ/mI9gwbk+VkizGBBH/PPWOVusgnfPk3tY1BXMAIAAAAAAc96O/pwKCmHCagT6T/QV/wz4vqO+R22GsZ1dse2Vg6QVsACAAAAAAgzIak+Q3UFLTHXPmJ+MuEklFtR3eLtvM+jdKkmGCV/YAAzQAfQAAAAVkACAAAAAA0XlQgy/Yu97EQOjronl9b3dcR1DFn3deuVhtTLbJZHkFcwAgAAAAACoMnpVl6EFJak8A+t5N4RFnQhkQEBnNAx8wDqmq5U/dBWwAIAAAAACR26FJif673qpwF1J1FEkQGJ1Ywcr/ZW6JQ7meGqzt1QADNQB9AAAABWQAIAAAAAAOtpNexRxfv0yRFvZO9DhlkpU4mDuAb8ykdLnE5Vf1VAVzACAAAAAAeblFKm/30orP16uQpZslvsoS8s0xfNPIBlw3VkHeekYFbAAgAAAAAPEoHj87sYE+nBut52/LPvleWQBzB/uaJFnosxp4NRO2AAM2AH0AAAAFZAAgAAAAAIr8xAFm1zPmrvW4Vy5Ct0W8FxMmyPmFzdWVzesBhAJFBXMAIAAAAABYeeXjJEzTHwxab6pUiCRiZjxgtN59a1y8Szy3hfkg+gVsACAAAAAAJuoY4rF8mbI+nKb+5XbZShJ8191o/e8ZCRHE0O4Ey8MAAzcAfQAAAAVkACAAAAAAl+ibLk0/+EwoqeC8S8cGgAtjtpQWGEZDsybMPnrrkwEFcwAgAAAAAHPPBudWgQ+HUorLDpJMqhS9VBF2VF5aLcxgrM1s+yU7BWwAIAAAAAAcCcBR2Vyv5pAFbaOU97yovuOi1+ATDnLLcAUqHecXcAADOAB9AAAABWQAIAAAAACR9erwLTb+tcWFZgJ2MEfM0PKI9uuwIjDTHADRFgD+SQVzACAAAAAAcOop8TXsGUVQoKhzUllMYWxL93xCOkwtIpV8Q6hiSYYFbAAgAAAAAKXKmh4V8veYwob1H03Q3p3PN8SRAaQwDT34KlNVUjiDAAM5AH0AAAAFZAAgAAAAALv0vCPgh7QpmM8Ug6ad5ioZJCh7pLMdT8FYyQioBQ6KBXMAIAAAAADsCPyIG8t6ApQkRk1fX/sfc1kpuWCWP8gAEpnYoBSHrQVsACAAAAAAJe/r67N6d8uTiogvfoR9rEXbIDjyLb9EVdqkayFFGaYAAzEwAH0AAAAFZAAgAAAAAIW4AxJgYoM0pcNTwk1RSbyjZGIqgKL1hcTJmNrnZmoPBXMAIAAAAAAZpfx3EFO0vY0f1eHnE0PazgqeNDTaj+pPJMUNW8lFrAVsACAAAAAAP+Um2vwW6Bj6vuz9DKz6+6aWkoKoEmFNoiz/xXm7lOsAAzExAH0AAAAFZAAgAAAAAKliO6L9zgeuufjj174hvmQGNRbmYYs9yAirL7OxwEW3BXMAIAAAAAAqU7vs3DWUQ95Eq8OejwWnD0GuXd+ASi/uD6S0l8MM1QVsACAAAAAAb9legYzsfctBPpHyl7YWpPmLr5QiNZFND/50N1vv2MUAAzEyAH0AAAAFZAAgAAAAAOGQcCBkk+j/Kzjt/Cs6g3BZPJG81wIHBS8JewHGpgk+BXMAIAAAAABjrxZXWCkdzrExwCgyHaafuPSQ4V4x2k9kUCAqUaYKDQVsACAAAAAADBU6KefT0v8zSmseaMNmQxKjJar72y7MojLFhkEHqrUAAzEzAH0AAAAFZAAgAAAAAPmCNEt4t97waOSd5hNi2fNCdWEkmcFJ37LI9k4Az4/5BXMAIAAAAABX7DuDPNg+duvELf3NbLWkPMFw2HGLgWGHyVWcPvSNCAVsACAAAAAAS7El1FtZ5STh8Q1FguvieyYX9b2DF1DFVsb9hzxXYRsAAzE0AH0AAAAFZAAgAAAAAD4vtVUYRNB+FD9yoQ2FVJH3nMeJeKbi6eZfth638YqbBXMAIAAAAAANCuUB4OdmuD6LaDK2f3vaqfgYYvg40wDXOBbcFjTqLwVsACAAAAAA9hqC2VoJBjwR7hcQ45xO8ZVojwC83jiRacCaDj6Px2gAAzE1AH0AAAAFZAAgAAAAAJPIRzjmTjbdIvshG6UslbEOd797ZSIdjGAhGWxVQvK1BXMAIAAAAABgmJ0Jh8WLs9IYs/a7DBjDWd8J3thW/AGJK7zDnMeYOAVsACAAAAAAi9zAsyAuou2oiCUHGc6QefLUkACa9IgeBhGu9W/r0X8AAzE2AH0AAAAFZAAgAAAAAABQyKQPoW8wGPIqnsTv69+DzIdRkohRhOhDmyVHkw9WBXMAIAAAAAAqWA2X4tB/h3O1Xlawtz6ndI6WaTwgU1QYflL35opu5gVsACAAAAAAWI/Gj5aZMwDIxztqmVL0g5LBcI8EdKEc2UA28pnekQoAAzE3AH0AAAAFZAAgAAAAACB7NOyGQ1Id3MYnxtBXqyZ5Ul/lHH6p1b10U63DfT6bBXMAIAAAAADpOryIcndxztkHSfLN3Kzq29sD8djS0PspDSqERMqokQVsACAAAAAADatsMW4ezgnyi1PiP7xk+gA4AFIN/fb5uJqfVkjg4UoAAzE4AH0AAAAFZAAgAAAAAKVfXLfs8XA14CRTB56oZwV+bFJN5BHraTXbqEXZDmTkBXMAIAAAAAASRWTsfGOpqdffiOodoqIgBzG/yzFyjR5CfUsIUIWGpgVsACAAAAAAkgCHbCwyX640/0Ni8+MoYxeHUiC+FSU4Mn9jTLYtgZgAAzE5AH0AAAAFZAAgAAAAAH/aZr4EuS0/noQR9rcF8vwoaxnxrwgOsSJ0ys8PkHhGBXMAIAAAAACd7ObGQW7qfddcvyxRTkPuvq/PHu7+6I5dxwS1Lzy5XAVsACAAAAAA3q0eKdV7KeU3pc+CtfypKR7BPxwaf30yu0j9FXeOOboAAzIwAH0AAAAFZAAgAAAAAKvlcpFFNq0oA+urq3w6d80PK1HHHw0H0yVWvU9aHijXBXMAIAAAAADWnAHQ5Fhlcjawki7kWzdqjM2f6IdGJblojrYElWjsZgVsACAAAAAAO0wvY66l24gx8nRxyVGC0QcTztIi81Kx3ndRhuZr6W4AAzIxAH0AAAAFZAAgAAAAAH/2aMezEOddrq+dNOkDrdqf13h2ttOnexZsJxG1G6PNBXMAIAAAAABNtgnibjC4VKy5poYjvdsBBnVvDTF/4mmEAxsXVgZVKgVsACAAAAAAqvadzJFLqQbs8WxgZ2D2X+XnaPSDMLCVVgWxx5jnLcYAAzIyAH0AAAAFZAAgAAAAAF2wZoDL6/V59QqO8vdRZWDpXpkV4h4KOCSn5e7x7nmzBXMAIAAAAADLZBu7LCYjbThaVUqMK14H/elrVOYIKJQCx4C9Yjw37gVsACAAAAAAEh6Vs81jLU204aGpL90fmYTm5i5R8/RT1uIbg6VU3HwAAzIzAH0AAAAFZAAgAAAAAH27yYaLn9zh2CpvaoomUPercSfJRUmBY6XFqmhcXi9QBXMAIAAAAAAUwumVlIYIs9JhDhSj0R0+59psCMsFk94E62VxkPt42QVsACAAAAAAT5x2hCCd2bpmpnyWaxas8nSxTc8e4C9DfKaqr0ABEysAAzI0AH0AAAAFZAAgAAAAALMg2kNAO4AFFs/mW3In04yFeN4AP6Vo0klyUoT06RquBXMAIAAAAAAgGWJbeIdwlpqXCyVIYSs0dt54Rfc8JF4b8uYc+YUj0AVsACAAAAAAWHeWxIkyvXTOWvfZzqtPXjfGaWWKjGSIQENTU3zBCrsAAzI1AH0AAAAFZAAgAAAAALas/i1T2DFCEmrrLEi7O2ngJZyFHialOoedVXS+OjenBXMAIAAAAAA1kK0QxY4REcGxHeMkgumyF7iwlsRFtw9MlbSSoQY7uAVsACAAAAAAUNlpMJZs1p4HfsD4Q4WZ4TBEi6Oc2fX34rzyynqWCdwAAzI2AH0AAAAFZAAgAAAAAP1TejmWg1CEuNSMt6NUgeQ5lT+oBoeyF7d2l5xQrbXWBXMAIAAAAABPX0kj6obggdJShmqtVfueKHplH4ZrXusiwrRDHMOKeQVsACAAAAAAIYOsNwC3DA7fLcOzqdr0bOFdHCfmK8tLwPoaE9uKOosAAzI3AH0AAAAFZAAgAAAAAMrKn+QPa/NxYezNhlOX9nyEkN1kE/gW7EuZkVqYl0b8BXMAIAAAAABUoZMSPUywRGfX2EEencJEKH5x/P9ySUVrhStAwgR/LgVsACAAAAAAMgZFH6lQIIDrgHnFeslv3ld20ynwQjQJt3cAp4GgrFkAAzI4AH0AAAAFZAAgAAAAAMmD1+a+oVbiUZd1HuZqdgtdVsVKwuWAn3/M1B6QGBM3BXMAIAAAAACLyytOYuZ9WEsIrrtJbXUx4QgipbaAbmlJvSZVkGi0CAVsACAAAAAA4v1lSp5H9BB+HYJ4bH43tC8aeuPZMf78Ng1JOhJh190AAzI5AH0AAAAFZAAgAAAAAOVKV7IuFwmYP1qVv8h0NvJmfPICu8yQhzjG7oJdTLDoBXMAIAAAAABL70XLfQLKRsw1deJ2MUvxSWKxpF/Ez73jqtbLvqbuogVsACAAAAAAvfgzIorXxE91dDt4nQxYfntTsx0M8Gzdsao5naQqcRUAAzMwAH0AAAAFZAAgAAAAAKS/1RSAQma+xV9rz04IcdzmavtrBDjOKPM+Z2NEyYfPBXMAIAAAAAAOJDWGORDgfRv8+w5nunh41wXb2hCA0MRzwnLnQtIqPgVsACAAAAAAf42C1+T7xdHEFF83+c2mF5S8PuuL22ogXXELnRAZ4boAAzMxAH0AAAAFZAAgAAAAAFeq8o82uNY1X8cH6OhdTzHNBUnCChsEDs5tm0kPBz3qBXMAIAAAAABaxMBbsaeEj/EDtr8nZfrhhhirBRPJwVamDo5WwbgvTQVsACAAAAAAMbH453A+BYAaDOTo5kdhV1VdND1avNwvshEG/4MIJjQAAzMyAH0AAAAFZAAgAAAAAI8IKIfDrohHh2cjspJHCovqroSr5N3QyVtNzFvT5+FzBXMAIAAAAABXHXteKG0DoOMmECKp6ro1MZNQvXGzqTDdZ0DUc8QfFAVsACAAAAAA/w5s++XYmO+9TWTbtGc3n3ndV4T9JUribIbF4jmDLSMAAzMzAH0AAAAFZAAgAAAAAJkHvm15kIu1OtAiaByj5ieWqzxiu/epK6c/9+KYIrB0BXMAIAAAAACzg5TcyANk0nes/wCJudd1BwlkWWF6zw3nGclq5v3SJQVsACAAAAAAvruXHTT3irPJLyWpI1j/Xwf2FeIE/IV+6Z49pqRzISoAAzM0AH0AAAAFZAAgAAAAAAYSOvEWWuSg1Aym7EssNLR+xsY7e9BcwsX4JKlnSHJcBXMAIAAAAABT48eY3PXVDOjw7JpNjOe1j2JyI3LjDnQoqZ8Je5B2KgVsACAAAAAAU2815RR57TQ9uDg0XjWjBkAKvf8yssxDMzrM4+FqP6AAAzM1AH0AAAAFZAAgAAAAAGQxC9L1e9DfO5XZvX1yvc3hTLtQEdKO9FPMkyg0Y9ZABXMAIAAAAADtmcMNJwdWLxQEArMGZQyzpnu+Z5yMmPAkvgq4eAKwNQVsACAAAAAAJ88zt4Y/Hoqh+zrf6KCOiUwHbOzCxSfp6k/qsZaYGEgAAzM2AH0AAAAFZAAgAAAAADLHK2LNCNRO0pv8n4fAsxwtUqCNnVK8rRgNiQfXpHSdBXMAIAAAAACf16EBIHRKD3SzjRW+LMOl+47QXA3CJhMzlcqyFRW22AVsACAAAAAAMGz4fAOa0EoVv90fUffwLjBrQhHATf+NdlgCR65vujAAAzM3AH0AAAAFZAAgAAAAAHiZJiXKNF8bbukQGsdYkEi95I+FSBHy1I5/hK2uEZruBXMAIAAAAADE+lZBa8HDUJPN+bF6xI9x4N7GF9pj3vBR7y0BcfFhBAVsACAAAAAAGIEN6sfqq30nyxW4dxDgXr/jz5HmvA9T1jx/pKCn4zgAAzM4AH0AAAAFZAAgAAAAAI1oa2OIw5TvhT14tYCGmhanUoYcCZtNbrVbeoMldHNZBXMAIAAAAAAx2nS0Ipblf2XOgBiUOuJFBupBhe7nb6QPLZlA4aMPCgVsACAAAAAA9xu828hugIgo0E3de9dZD+gTpVUGlwtDba+tw/WcbUoAAzM5AH0AAAAFZAAgAAAAABgTWS3Yap7Q59hii/uPPimHWXsr+DUmsqfwt/X73qsOBXMAIAAAAACKK05liW5KrmEAvtpCB1WUltruzUylDDpjea//UlWoOAVsACAAAAAAcgN4P/wakJ5aJK5c1bvJBqpVGND221dli2YicPFfuAYAAzQwAH0AAAAFZAAgAAAAABOAnBPXDp6i9TISQXvcNKwGDLepZTu3cKrB4vKnSCjBBXMAIAAAAADjjzZO7UowAAvpwyG8BNOVqLCccMFk3aDK4unUeft5ywVsACAAAAAA4zkCd4k9gvfXoD1C7vwTjNcdVJwEARh8h/cxZ4PNMfgAAzQxAH0AAAAFZAAgAAAAAHN8hyvT1lYrAsdiV5GBdd5jhtrAYE/KnSjw2Ka9hjz9BXMAIAAAAAD794JK7EeXBs+D7yOVK7nWF8SbZ/7U8gZ7nnT9JFNwTAVsACAAAAAAg8Wt1HO3NhByq2ggux2a4Lo6Gryr24rEFIqh2acrwWMAAzQyAH0AAAAFZAAgAAAAAO93bPrq8bsnp1AtNd9ETnXIz0lH/2HYN/vuw9wA3fyFBXMAIAAAAABHlls5fbaF2oAGqptC481XQ4eYxInTC29aElfmVZgDUgVsACAAAAAANoQXEWpXJpgrSNK/cKi/m7oYhuSRlp1IZBF0bqTEATcAAzQzAH0AAAAFZAAgAAAAAL1YsAZm1SA0ztU6ySIrQgCCA74V6rr0/4iIygCcaJL6BXMAIAAAAADTXWTHWovGmUR1Zg9l/Aqq9H5mOCJQQrb/Dfae7e3wKAVsACAAAAAA5dunyJK6/SVfDD0t9QlNBcFqoZnf9legRjHaLSKAoQMAAzQ0AH0AAAAFZAAgAAAAAEoFAeHk0RZ9kD+cJRD3j7PcE5gzWKnyBrF1I/MDNp5mBXMAIAAAAACgHtc2hMBRSZjKw8RAdDHK+Pi1HeyjiBuAslGVNcW5tAVsACAAAAAAXzBLfq+GxRtX4Wa9fazA49DBLG6AjZm2XODStJKH8D0AAzQ1AH0AAAAFZAAgAAAAAAW+7DmSN/LX+/0uBVJDHIc2dhxAGz4+ehyyz8fAnNGoBXMAIAAAAAA6Ilw42EvvfLJ3Eq8Afd+FjPoPcQutZO6ltmCLEr8kxQVsACAAAAAAbbZalyo07BbFjPFlYmbmv0z023eT9eLkHqeVUnfUAUAAAzQ2AH0AAAAFZAAgAAAAANBdV7M7kuYO3EMoQItAbXv4t2cIhfaT9V6+s4cg9djlBXMAIAAAAABvz4MIvZWxxrcJCL5qxLfFhXiUYB1OLHdKEjco94SgDgVsACAAAAAAK2GVGvyPIKolF/ECcmfmkVcf1/IZNcaTv96N92yGrkEAAzQ3AH0AAAAFZAAgAAAAAMoAoiAn1kc79j5oPZtlMWHMhhgwNhLUnvqkqIFvcH1NBXMAIAAAAADcJTW7WiCyW0Z9YDUYwppXhLj4Ac1povpJvcAq+i48MQVsACAAAAAAIGxGDzoeB3PTmudl4+j6piQB++e33EEzuzAiXcqGxvUAAzQ4AH0AAAAFZAAgAAAAACI3j5QP7dWHpcT6WO/OhsWwRJNASBYqIBDNzW8IorEyBXMAIAAAAABxUpBSjXwCKDdGP9hYU+RvyR+96kChfvyyRC4jZmztqAVsACAAAAAAvBCHguWswb4X0xdcAryCvZgQuthXzt7597bJ5VxAMdgAAzQ5AH0AAAAFZAAgAAAAAKsbycEuQSeNrF8Qnxqw3x3og8JmQabwGqnDbqzFRVrrBXMAIAAAAACno/3ef2JZJS93SVVzmOZSN+jjJHT8s0XYq2M46d2sLAVsACAAAAAAAt5zLJG+/j4K8rnkFtAn8IvdUVNefe6utJ3rdzgwudIAAzUwAH0AAAAFZAAgAAAAAPXIcoO8TiULqlxzb74NFg+I8kWX5uXIDUPnh2DobIoMBXMAIAAAAADR6/drkdTpnr9g1XNvKDwtBRBdKn7c2c4ZNUVK5CThdQVsACAAAAAAJqOA1c6KVog3F4Hb/GfDb3jCxXDRTqpXWSbMH4ePIJsAAzUxAH0AAAAFZAAgAAAAAEa03ZOJmfHT6/nVadvIw71jVxEuIloyvxXraYEW7u7pBXMAIAAAAADzRlBJK75FLiKjz3djqcgjCLo/e3yntI3MnPS48OORhgVsACAAAAAAnQhx4Rnyj081XrLRLD5NLpWmRWCsd0M9Hl7Jl19R0h8AAzUyAH0AAAAFZAAgAAAAAKx8NLSZUU04pSSGmHa5fh2oLHsEN5mmNMNHL95/tuC9BXMAIAAAAAA59hcXVaN3MNdHoo11OcH1aPRzHCwpVjO9mGfMz4xh3QVsACAAAAAAYIPdjV2XbPj7dBeHPwnwhVU7zMuJ+xtMUW5mIOYtmdAAAzUzAH0AAAAFZAAgAAAAAHNKAUxUqBFNS9Ea9NgCZoXMWgwhP4x0/OvoaPRWMquXBXMAIAAAAABUZ551mnP4ZjX+PXU9ttomzuOpo427MVynpkyq+nsYCQVsACAAAAAALnVK5p2tTTeZEh1zYt4iqKIQT9Z0si//Hy1L85oF+5IAAzU0AH0AAAAFZAAgAAAAALfGXDlyDVcGaqtyHkLT0qpuRhJQLgCxtznazhFtuyn/BXMAIAAAAABipxlXDq14C62pXhwAeen5+syA+/C6bN4rtZYcO4zKwAVsACAAAAAAXUf0pzUq0NhLYagWDap4uEiwq5rLpcx29rWbt1NYMsMAAzU1AH0AAAAFZAAgAAAAANoEr8sheJjg4UCfBkuUzarU9NFoy1xwbXjs5ifVDeA9BXMAIAAAAABPoyTf6M+xeZVGES4aNzVlq7LgjqZXJ/QunjYVusGUEAVsACAAAAAA1hA2gMeZZPUNytk9K+lB1RCqWRudRr7GtadJlExJf8oAAzU2AH0AAAAFZAAgAAAAAKvDiK+xjlBe1uQ3SZTNQl2lClIIvpP/5CHwY6Kb3WlgBXMAIAAAAAANnxImq5MFbWaRBHdJp+yD09bVlcFtiFDYsy1eDZj+iQVsACAAAAAAWtsyO+FxMPSIezwsV1TJD8ZrXAdRnQM6DJ+f+1V3qEkAAzU3AH0AAAAFZAAgAAAAAF49IlFH9RmSUSvUQpEPUedEksrQUcjsOv44nMkwXhjzBXMAIAAAAADJtWGbk0bZzmk20obz+mNsp86UCu/nLLlbg7ppxYn7PgVsACAAAAAA3k0Tj/XgPQtcYijH8cIlQoe/VXf15q1nrZNmg7yWYEgAAzU4AH0AAAAFZAAgAAAAAOuSJyuvz50lp3BzXlFKnq62QkN2quNU1Gq1IDsnFoJCBXMAIAAAAAAqavH1d93XV3IzshWlMnzznucadBF0ND092/2ApI1AcAVsACAAAAAAzUrK4kpoKCmcpdZlZNI13fddjdoAseVe67jaX1LobIIAAzU5AH0AAAAFZAAgAAAAALtgC4Whb4ZdkCiI30zY6fwlsxSa7lEaOAU3SfUXr02XBXMAIAAAAACgdZ6U1ZVgUaZZwbIaCdlANpCw6TZV0bwg3DS1NC/mnAVsACAAAAAAzI49hdpp0PbO7S2KexISxC16sE73EUAEyuqUFAC/J48AAzYwAH0AAAAFZAAgAAAAAF6PfplcGp6vek1ThwenMHVkbZgrc/dHgdsgx1VdPqZ5BXMAIAAAAACha3qhWkqmuwJSEXPozDO8y1ZdRLyzt9Crt2vjGnT7AAVsACAAAAAA7nvcU59+LwxGupSF21jAeAE0x7JE94tjRkJfgM1yKU8AAzYxAH0AAAAFZAAgAAAAAKoLEhLvLjKc7lhOJfx+VrGJCx9tXlOSa9bxQzGR6rfbBXMAIAAAAAAIDK5wNnjRMBzET7x/KAMExL/zi1IumJM92XTgXfoPoAVsACAAAAAAFkUYWFwNr815dEdFqp+TiIozDcq5IBNVkyMoDjharDQAAzYyAH0AAAAFZAAgAAAAADoQv6lutRmh5scQFvIW6K5JBquLxszuygM1tzBiGknIBXMAIAAAAADAD+JjW7FoBQ76/rsECmmcL76bmyfXpUU/awqIsZdO+wVsACAAAAAAPFHdLw3jssmEXsgtvl/RBNaUCRA1kgSwsofG364VOvQAAzYzAH0AAAAFZAAgAAAAAJNHUGAgn56KekghO19d11nai3lAh0JAlWfeP+6w4lJBBXMAIAAAAAD9XGJlvz59msJvA6St9fKW9CG4JoHV61rlWWnkdBRLzwVsACAAAAAAxwP/X/InJJHmrjznvahIMgj6pQR30B62UtHCthSjrP0AAzY0AH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzY1AH0AAAAFZAAgAAAAANpIljbxHOM7pydY877gpRQvYY2TGK7igqgGsavqGPBABXMAIAAAAAAqHyEu9gpurPOulApPnr0x9wrygY/7mXe9rAC+tPK80wVsACAAAAAA7gkPzNsS3gCxdFBWbSW9tkBjoR5ib+saDvpGSB3A3ogAAzY2AH0AAAAFZAAgAAAAAGR+gEaZTeGNgG9BuM1bX2R9ed4FCxBA9F9QvdQDAjZwBXMAIAAAAABSkrYFQ6pf8MZ1flgmeIRkxaSh/Eep4Btdx4QYnGGnwAVsACAAAAAApRovMiV00hm/pEcT4XBsyPNw0eo8RLAX/fuabjdU+uwAAzY3AH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzY4AH0AAAAFZAAgAAAAADgyPqQdqQrgfmJjRFAILTHzXbdw5kpKyfeoEcy6YYG/BXMAIAAAAAAE+3XsBQ8VAxAkN81au+f3FDeCD/s7KoZD+fnM1MJSSAVsACAAAAAAhRnjrXecwV0yeCWKJ5J/x12Xx4qVJahsCEVHB/1U2rcAAzY5AH0AAAAFZAAgAAAAAI0CT7JNngTCTUSei1Arw7eHWCD0jumv2rb7imjWIlWABXMAIAAAAABSP8t6ya0SyCphXMwnru6ZUDXWElN0NfBvEOhDvW9bJQVsACAAAAAAGWeGmBNDRaMtvm7Rv+8TJ2sJ4WNXKcp3tqpv5Se9Ut4AAzcwAH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcxAH0AAAAFZAAgAAAAAHIkVuNDkSS1cHIThKc/O0r2/ubaABTOi8Q1r/dvBAsEBXMAIAAAAADdHYqchEiJLM340c3Q4vJABmmth3+MKzwLYlsG6GS7sQVsACAAAAAADa+KP/pdTiG22l+ZWd30P1iHjnBF4zSNRdFm0oEK82kAAzcyAH0AAAAFZAAgAAAAAJmoDILNhC6kn3masElfnjIjP1VjsjRavGk1gSUIjh1NBXMAIAAAAAD97Ilvp3XF8T6MmVVcxMPcdL80RgQ09UoC6PnoOvZ1IQVsACAAAAAA2RK3Xng6v8kpvfVW9tkVXjpE+BSnx9/+Fw85Evs+kUEAAzczAH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzc0AH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzc1AH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzc2AH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzc3AH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzc4AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzc5AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzgwAH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzgxAH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzgyAH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzgzAH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzg0AH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzg1AH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzg2AH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzg3AH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzg4AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzg5AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzkwAH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzkxAH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzkyAH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzkzAH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzk0AH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzk1AH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzk2AH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzk3AH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzk4AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzk5AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzEwMAB9AAAABWQAIAAAAADJDdC9aEFl4Y8J/awHbnXGHjfP+VXQilPHJg7ewaJI7AVzACAAAAAAE+tqRl6EcBMXvbr4GDiNIYObTsYpa1n6BJk9EjIJVicFbAAgAAAAAJVc+HYYqa0m1Hq6OiRX8c0iRnJYOt6AJAJoG0sG3GMSAAMxMDEAfQAAAAVkACAAAAAA3F9rjEKhpoHuTULVGgfUsGGwJs3bISrXkFP1v6KoQLgFcwAgAAAAAIBf0tXw96Z/Ds0XSIHX/zk3MzUR/7WZR/J6FpxRWChtBWwAIAAAAABWrjGlvKYuTS2s8L9rYy8Hf0juFGJfwQmxVIjkTmFIGQADMTAyAH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzEwMwB9AAAABWQAIAAAAACMtPm12YtdEAvqu6Eji1yuRXnu1RJP6h0l7pH3lSH4MwVzACAAAAAAENyCFfyUAh1veQBGx+cxiB7Sasrj41jzCGflZkB5cRMFbAAgAAAAAKdI2LMqISr/T5vuJPg6ZRBm5fVi2aQCc4ra3A4+AjbDAAMxMDQAfQAAAAVkACAAAAAAvlI4lDcs6GB1cnm/Tzo014CXWqidCdyE5t2lknWQd4QFcwAgAAAAAD60SpNc4O2KT7J0llKdSpcX1/Xxs97N715a1HsTFkmBBWwAIAAAAABuuRkJWAH1CynggBt1/5sPh9PoGiqTlS24D/OE2uHXLQADMTA1AH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzEwNgB9AAAABWQAIAAAAABb6LXDWqCp1beQgQjj8I3sRTtFhlrmiBi+h/+ikmrvugVzACAAAAAA9stpgTecT7uTyaGNs3K9Bp0A7R0QaIAOfscyMXHBPX8FbAAgAAAAAHUt+McyXrJ1H8SwnHNVO181Ki8vDAM1f7XI26mg95ZDAAMxMDcAfQAAAAVkACAAAAAA97NTT+81PhDhgptNtp4epzA0tP4iNb9j1AWkiiiKGM8FcwAgAAAAAKPbHg7ise16vxmdPCzksA/2Mn/qST0L9Xe8vnQugVkcBWwAIAAAAABB0EMXfvju4JU/mUH/OvxWbPEl9NJkcEp4iCbkXI41fAADMTA4AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzEwOQB9AAAABWQAIAAAAADQnslvt6Hm2kJPmqsTVYQHE/wWeZ4bE1XSkt7TKy0r1gVzACAAAAAA8URTA4ZMrhHPvlp53TH6FDCzS+0+61qHm5XK6UiOrKEFbAAgAAAAAHQbgTCdZcbdA0avaTmZXUKnIS7Nwf1tNrcXDCw+PdBRAAMxMTAAfQAAAAVkACAAAAAAhujlgFPFczsdCGXtQ/002Ck8YWQHHzvWvUHrkbjv4rwFcwAgAAAAALbV0lLGcSGfE7mDM3n/fgEvi+ifjl7WZ5b3aqjDNvx9BWwAIAAAAACbceTZy8E3QA1pHmPN5kTlOx3EO8kJM5PUjTVftw1VpgADMTExAH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzExMgB9AAAABWQAIAAAAACfw9/te4GkHZAapC9sDMHHHZgmlTrccyJDPFciOMSOcwVzACAAAAAAIIC1ZpHObvmMwUfqDRPl4C1aeuHwujM1G/yJbvybMNAFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5AAMxMTMAfQAAAAVkACAAAAAAkxHJRbnShpPOylLoDdNShfILeA1hChKFQY9qQyZ5VmsFcwAgAAAAAKidrY+rC3hTY+YWu2a7fuMH2RD/XaiTIBW1hrxNCQOJBWwAIAAAAACW0kkqMIzIFMn7g+R0MI8l15fr3k/w/mHtY5n6SYTEwAADMTE0AH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzExNQB9AAAABWQAIAAAAABxMy7X5hf7AXGDz3Y/POu1ZpkMlNcSvSP92NOO/Gs7wAVzACAAAAAAHJshWo2T5wU2zvqCyJzcJQKQaHFHpCpMc9oWBXkpUPoFbAAgAAAAAGeiJKzlUXAvL0gOlW+Hz1mSa2HsV4RGmyLmCHlzbAkoAAMxMTYAfQAAAAVkACAAAAAAlqbslixl7Zw3bRlibZbe/WmKw23k8uKeIzPKYEtbIy0FcwAgAAAAAHEKwpUxkxOfef5HYvulXPmdbzTivwdwrSYIHDeNRcpcBWwAIAAAAADuPckac21Hrg/h0kt5ShJwVEZ9rx6SOHd2+HDjqxEWTQADMTE3AH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzExOAB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFbAAgAAAAAIgbpyYtJj5513Z5XYqviH/HXG/5+mqR52iBbfqMmDtZAAMxMTkAfQAAAAVkACAAAAAAJRzYK0PUwr9RPG2/7yID0WgcTJPB2Xjccp5LAPDYunkFcwAgAAAAAIIh24h3DrltAzNFhF+MEmPrZtzr1PhCofhChZqfCW+jBWwAIAAAAAAzRNXtL5o9VXMk5D5ylI0odPDJDSZZry1wfN+TedH70gADMTIwAH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzEyMQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FbAAgAAAAAJci0w1ZB8W2spJQ+kMpod6HSCtSR2jrabOH+B0fj3A4AAMxMjIAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWwAIAAAAAAlswzAl81+0DteibwHD+CG5mZJrfHXa9NnEFRtXybzzwADMTIzAH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzEyNAB9AAAABWQAIAAAAAAfPUoy7QyZKhIIURso+mkP9qr1izbjETqF5s22GwjCjAVzACAAAAAAvLMsIDQ/go4VUxeh50UHmsvMvfx51cwyONnRD2odvC0FbAAgAAAAAKMb+1CodEalAFnDrEL1Ndt8ztamZ+9134m9Kp3GQgd+AAMxMjUAfQAAAAVkACAAAAAAE3ZqUar0Bq2zWbARE0bAv98jBlK9UJ73/xcwdMWWlSkFcwAgAAAAAK4M+MmC+9sFiFsumMyJZQKxWmmJiuG9H7IzKw083xxkBWwAIAAAAAAqkAONzhvMhkyL1D/6h7QQxEkdhC3p2WjXH+VGq5qCqQADMTI2AH0AAAAFZAAgAAAAAMo8FJiOq63cAmyk2O7eI7GcbQh/1j4RrMTqly3rexftBXMAIAAAAADjVmpd0WiRGTw/gAqEgGolt2EI7Csv14vKdmYoMD0aAgVsACAAAAAA07XQBzBUQMNw7F2/YxJjZNuPVpHTTgbLd1oGk77+bygAAzEyNwB9AAAABWQAIAAAAACu5IGaIx7A3Jvly/kzlCsSA4s3iJwuIl8jEdRH0k93NwVzACAAAAAA9NRUyxYE+t0Xyosyt6vIfMFW/vBoYg6sR+jBNs4JAxIFbAAgAAAAAAzyZ91dx+0oMlOVAjRGiMrPySikY/U9eMEB4WJb3uWtAAMxMjgAfQAAAAVkACAAAAAALkRy0GJInXYLA+cgjs6Myb0a+Gu9hgXhHvhLNoGWfckFcwAgAAAAANbALyt9zCSvwnLaWCd2/y2eoB7qkWTvv1Ldu8r40JPuBWwAIAAAAAD4Fl5bV5sz4isIE9bX+lmAp+aAKaZgVYVZeVfrItkCZAADMTI5AH0AAAAFZAAgAAAAAGoUK/DSWhT8LZhszSUqDbTrp8cSA7rdqmADKL+MILtTBXMAIAAAAABHnEE9bVa6lvhfhEMkkV2kzSSxH/sMW/FIJuw3CzWs6wVsACAAAAAAanavcBdqZxgRGKvEK95wTmeL1K1CeDSXZsXUAs81uOgAAzEzMAB9AAAABWQAIAAAAAC922ZDQE3h2fQKibGMZ9hV0WNlmrPYYSdtaSyYxsWYqgVzACAAAAAAagMovciKK6WVjIc2cCj8nK5O/gVOFFVeVAJpRp89tmQFbAAgAAAAAKcTFfPQzaFiAtSFhqbN02sCE1BKWJSrRfGN5L6oZwzkAAMxMzEAfQAAAAVkACAAAAAAtK+JqX3K/z2txjAU15DgX4y90DS2YLfIJFolCOkJJJwFcwAgAAAAAMnR5V7gfX7MNqqUdL5AkWlkhyFXaBRVNej+Rcn8lrQkBWwAIAAAAAA2cDNRXZuiC241TGRvdFyctJnrNcdbZOP9zHio81tkngADMTMyAH0AAAAFZAAgAAAAAAeGrIMK/bac6kPczxbvRYqKMkcpeI2FjdMpD91FDWIvBXMAIAAAAAAix62z1LeS8yvSXCl5gHSIomjyx76fF3S1lp9k900hygVsACAAAAAAiYwzf2m71aWFD5ajcXyW2JX2EzQOkBroTGMg29nLPYIAAzEzMwB9AAAABWQAIAAAAACphf298InM0Us4HT8o1W1MGw0D/02vd7Jh+U0h7qaFaQVzACAAAAAAFXtk7YpqsOJxsqGWSIL+YcBE96G3Zz9D31gPqDW94y8FbAAgAAAAAAOrS1KVA94rjB1jZ1pPocpCeBG+B14RzWoHqVDpp7JbAAMxMzQAfQAAAAVkACAAAAAATLDS2cuDVM3yDMuWNgk2iGKBTzPpfJMbvxVOSY39ZfcFcwAgAAAAAPT5wRi2cLHIUflXzm6EQB/m7xdThP80ir1VV/JBBqvxBWwAIAAAAAB9lEtZS0aXCFbCtSbhnis27S5IPcfWGygHW8AHn3QqzwADMTM1AH0AAAAFZAAgAAAAAJNjExiZVX7jfFGfYpQu16qxLN0YPqVU/5CQ/Y67YSinBXMAIAAAAABMpm2+6KrkRUlXzQoMPHrQmIO6dkQz66tYdfTeA3dKqQVsACAAAAAAFXobHiMLvNZuEPr8jtewCX2J93EZG3JNeyVg92fue6YAAzEzNgB9AAAABWQAIAAAAABlFkYtLCx901X6QVVMkSn6Z7k30UF4xHaA0OZJJ9bdyQVzACAAAAAATez+F9GHcGzTp7jjv4feboUNb8JCkIp4EqcPFisnq7MFbAAgAAAAACE7JvOpBgMoZ7kRd4QbxIhxukPTUxXpzhjnBHiR7XoRAAMxMzcAfQAAAAVkACAAAAAA8NJKN0IxZnruhswGQkiruv8Ih0EMwDcSZx/Xasup9dkFcwAgAAAAAKaJZRxzA+Igeydvuk6cSwUHXcrmT4PjhuPu//FslpdnBWwAIAAAAAD53Rok1Vq/PMAnXmarqoHJ0PEyYUBmVESa9hIpCv/G9QADMTM4AH0AAAAFZAAgAAAAABHxHdEClz7hbSSgE58+dWLlSMJnoPz+jFxp4bB1GmLQBXMAIAAAAAD3nSvT6aGD+A110J/NwEfp0nPutlmuB5B+wA3CC3noGAVsACAAAAAA3Apjd+TapONB7k5wBVwTWgn8t+Sq2oyyU5/+as109RcAAzEzOQB9AAAABWQAIAAAAAC/o8qW/ifk3KuJ01VFkyNLgQafxB5/bGs2G5VyyVafOwVzACAAAAAA1bMqAFGDHSl6BYNLbxApvkAv2K1/oafywiX0MDz1dGUFbAAgAAAAAHJXLlId3edFoniLD/9K2A5973MeP2Ro31flDyqm3l5QAAMxNDAAfQAAAAVkACAAAAAAY2V8I1bz3a1AxTtmED6UhdhA09huFkuuEX8R+d/WDPUFcwAgAAAAAPTVoNRiI76tcRKqd+JBBVyy4+YcKST42p0QX2BtmQ2VBWwAIAAAAACcxt9hg14WqPNiDv1MkqVljM2e2KJEv53lA17LhV6ZigADMTQxAH0AAAAFZAAgAAAAAO2kSsW0WGN9AOtK4xK2SHrGhWiaAbMEKT4iZkRpaDN/BXMAIAAAAABKGzQcPM8LT2dwOggxoWjv/1imYWabbG/G4kBw8OWaxAVsACAAAAAAC9hLK1dScQTAqg+YAG3ObdPzg2Xet57HmOFpGmyUR9UAAzE0MgB9AAAABWQAIAAAAAAiCwzNEEaH/mDam68IdDftnhthyUFdb+ZCNSBQ91WlHQVzACAAAAAA7tHyHcxCzmbJeFYZyPm4mEgkTGKOvwY4MX82OvH0Jn8FbAAgAAAAAAb5IAbZ1hXCNegQ+S+C9i/Z8y6sS8KeU04V6hXa2ml6AAMxNDMAfQAAAAVkACAAAAAAGuCHVNJSuoVkpPOnS5s89GuA+BLi2IPBUr2Bg1sWEPIFcwAgAAAAAEl1gncS5/xO7bQ/KQSstRV3rOT2SW6nV92ZANeG2SR6BWwAIAAAAAA9LOcKmhek8F2wAh8yvT/vjp2gaouuO+Hmv10lwAeWPAADMTQ0AH0AAAAFZAAgAAAAAMfxz7gEaoCdPvXrubDhCZUS0ARLZc1svgbXgMDlVBPgBXMAIAAAAAB6a5dDA3fuT5Vz2KvAcbUEFX/+B7Nw2p1QqbPoQ5TTuAVsACAAAAAAcf/y75UOuI62A6vWH7bYr/5Jz+nirZVYK/81trN6XOQAAzE0NQB9AAAABWQAIAAAAACnYsqF/VzmjIImC9+dqrHO1TM6lJ6fRwM0mM6Wf6paOwVzACAAAAAA5tgZzch8uDCR1ky3SllVaKVpxAlbrhvlNDTazZZRZOAFbAAgAAAAALeGiLJS4z2zhgVpxzyPdRYyACP9QzQBOob34YrIZumCAAMxNDYAfQAAAAVkACAAAAAAEC0sIVmadtW4YMuRXH7RpAhXclsd+3bmqGXCMeaT014FcwAgAAAAABPpXh0uzpsJJB+IRUNajmMB9WGwswfpw5T9xk3Xj6ANBWwAIAAAAAAmf+NYh9TZ/QRu3w/GQz66n7DtfbJijN3G7KzeL8lstAADMTQ3AH0AAAAFZAAgAAAAABaIB3n49Xm9cOafSrQsE0WCcYp8rMIO/qVwIlMF5YLRBXMAIAAAAAC9EyWJV3xOu9bzgdJ/yX+ko7qLf1u3AxNMataW2C9EzQVsACAAAAAAvVbDkLxXx2DcMLifIQ3K0IIJcLcAG9DUrNfI6aoUjNcAAzE0OAB9AAAABWQAIAAAAAA5rZItA/cocRnngYqcJ3nBXQ+l688aKz3EQyLbYYunPAVzACAAAAAAwKyA+L7TgxztPClLrIMk2JXR+w7c04N3ZOqPgjvrIvsFbAAgAAAAACzvZ33h6aWEe8hmo+1f6OXJ72FY5hvWaUuha64ZV3KFAAMxNDkAfQAAAAVkACAAAAAA3htn7oHJ0YYpIrs+Mzyh85Ys67HwAdv5LQl1mCdoMWkFcwAgAAAAAEHjCtNNLenHuSIYux6ezAHsXDaj2DlTF67ToDhDDe6HBWwAIAAAAAD+P4H0sk9jOd+7vOANt2/1Ectb+4ZRGPE8GkHWNXW3MgADMTUwAH0AAAAFZAAgAAAAAEnt18Km/nqggfIJWxzTr9r3hnXNaueG6XO9A5G11LnGBXMAIAAAAAD7QxzGMN/ard5TfFLecE6uusMmXG2+RBsBR+/NCQHUwAVsACAAAAAAQEZ1ZZ8GC8rdbg7s87OM5Gr9qkTXS9+P5DuAZxj5Gl4AAzE1MQB9AAAABWQAIAAAAAAVAKK/GoY8AACu/hyMpO4hdLq6JnEyWNzkyci9sbaD/wVzACAAAAAA2HmeqpMlvvBpV2zQTYIRmsc4MFlfHRwLof0ycJgMg/MFbAAgAAAAACdltCeWi5E/q1Li1eXLChpM2D9QQSGLBZ82NklQSc0oAAMxNTIAfQAAAAVkACAAAAAAhHyq1GQC/GiMwpYjcsfkNxolJ10ARKjIjfkW1Wipzi0FcwAgAAAAAD/uaGWxTDq87F8XZ6CrFI+RNa8yMqfSZdqK00Kj833BBWwAIAAAAAD6aEdOO0CsQGagioOCvANPCEHSpJ8BSixlPBq5ERhB7AADMTUzAH0AAAAFZAAgAAAAABAJJxHoZD+MQBWqm9UM9Dd3z5ZohIZGWRaRVRsMptKQBXMAIAAAAADrE/ca+gqj/SH4oao4wE4qn2ovoTydzcMbDbrfnUs3zAVsACAAAAAAeNCIQN6hVnGJinytQRFGlQ2ocoprXNqpia+BSxzl+uwAAzE1NAB9AAAABWQAIAAAAAAv01wz7VG9mTepjXQi6Zma+7b/OVBaKVkWNbgDLr1mFgVzACAAAAAA0I5sxz8r6wkCp5Tgvr+iL4p6MxSOq5d3e1kZG+0b7NkFbAAgAAAAAIA32v6oGkAOS96HexGouNTex+tLahtx9QF2dgGClk6WAAMxNTUAfQAAAAVkACAAAAAAWXecRwxSon68xaa9THXnRDw5ZfzARKnvvjTjtbae6T0FcwAgAAAAAPh0UfUMEo7eILCMv2tiJQe1bF9qtXq7GJtC6H5Va4fIBWwAIAAAAADqFr1ThRrTXNgIOrJWScO9mk86Ufi95IDu5gi4vP+HWQADMTU2AH0AAAAFZAAgAAAAAEY5WL8/LpX36iAB1wlQrMO/xHVjoO9BePVzbUlBYo+bBXMAIAAAAABoKcpadDXUARedDvTmzUzWPe1jTuvD0z9oIcZmKuiSXwVsACAAAAAAJuJbwuaMrAFoI+jU/IYr+k4RzAqITrOjAd3HWCpJHqEAAzE1NwB9AAAABWQAIAAAAADnJnWqsfx0xqNnqfFGCxIplVu8mXjaHTViJT9+y2RuTgVzACAAAAAAWAaSCwIXDwdYxWf2NZTly/iKVfG/KDjHUcA1BokN5sMFbAAgAAAAAJVxavipE0H4/JQvhagdytXBZ8qGooeXpkbPQ1RfYMVHAAMxNTgAfQAAAAVkACAAAAAAsPG7LaIpJvcwqcbtfFUpIjj+vpNj70Zjaw3eV9T+QYsFcwAgAAAAAJQ71zi0NlCyY8ZQs3IasJ4gB1PmWx57HpnlCf3+hmhqBWwAIAAAAACD58TO6d+71GaOoS+r73rAxliAO9GMs4Uc8JbOTmC0OwADMTU5AH0AAAAFZAAgAAAAAAGiSqKaQDakMi1W87rFAhkogfRAevnwQ41onWNUJKtuBXMAIAAAAAASgiDpXfGh7E47KkOD8MAcX8+BnDShlnU5JAGdnPdqOAVsACAAAAAAI+2TTQIgbFq4Yr3lkzGwhG/tqChP7hRAx2W0fNaH6jcAAzE2MAB9AAAABWQAIAAAAAB7L4EnhjKA5xJD3ORhH2wOA1BvpnQ+7IjRYi+jjVEaJAVzACAAAAAAuhBIm0nL3FJnVJId+7CKDASEo+l2E89Z9/5aWSITK4AFbAAgAAAAALtSICOzQDfV9d+gZuYxpEj6cCeHnKTT+2G3ceP2H65kAAMxNjEAfQAAAAVkACAAAAAAaROn1NaDZFOGEWw724dsXBAm6bgmL5i0cki6QZQNrOoFcwAgAAAAANVT8R6UvhrAlyqYlxtmnvkR4uYK/hlvyQmBu/LP6/3ZBWwAIAAAAAD+aHNMP/X+jcRHyUtrCNkk1KfMtoD3GTmShS8pWGLt+AADMTYyAH0AAAAFZAAgAAAAADqSR5e0/Th59LrauDA7OnGD1Xr3H3NokfVxzDWOFaN7BXMAIAAAAACt30faNwTWRbvmykDpiDYUOCwA6QDbBBYBFWS7rdOB4AVsACAAAAAAF7SvnjjRk5v2flFOKaBAEDvjXaL1cpjsQLtK2fv9zdQAAzE2MwB9AAAABWQAIAAAAADmtb1ZgpZjSeodPG/hIVlsnS8hoRRwRbrTVx89VwL62AVzACAAAAAAi38e1g6sEyVfSDkzZbaZXGxKI/zKNbMasOl2LYoWrq8FbAAgAAAAAALACk0KcCDN/Kv8WuazY8ORtUGkOZ5Dsm0ys1oOppp/AAMxNjQAfQAAAAVkACAAAAAAf/f7AWVgBxoKjr7YsEQ4w/fqSvuQWV2HMiA3rQ7ur0sFcwAgAAAAADkkeJozP6FFhUdRIN74H4UhIHue+eVbOs1NvbdWYFQrBWwAIAAAAAB55FlHAkmTzAYj/TWrGkRJw2EhrVWUnZXDoMYjyfB/ZwADMTY1AH0AAAAFZAAgAAAAAI2WEOymtuFpdKi4ctanPLnlQud+yMKKb8p/nfKmIy56BXMAIAAAAADVKrJmhjr1rfF3p+T+tl7UFd1B7+BfJRk0e7a4im7ozgVsACAAAAAA5E7Ti3PnFiBQoCcb/DN7V1uM3Xd6VKiexPKntssFL7kAAzE2NgB9AAAABWQAIAAAAAAuHU9Qd79hjyvKOujGanSGDIQlxzsql8JytTZhEnPw+AVzACAAAAAAjF2gV/4+sOHVgDd/oR5wDi9zL7NGpGD+NsEpGXy/a4QFbAAgAAAAAJzMoyojYV6Ed/LpVN5zge93Odv3U7JgP7wxeRaJZGTdAAMxNjcAfQAAAAVkACAAAAAA7dQDkt3iyWYCT94d7yqUtPPwp4qkC0ddu+HFdHgVKEkFcwAgAAAAANuYvtvZBTEq4Rm9+5eb7VuFopowkrAuv86PGP8Q8/QvBWwAIAAAAACeqXoAOQOE4j0zRMlkVd8plaW0RX1npsFvB38Xmzv7sAADMTY4AH0AAAAFZAAgAAAAAAwnZSDhL4tNGYxlHPhKYB8s28dY5ScSwiKZm3UhT8U3BXMAIAAAAABDoY6dhivufTURQExyC9Gx3ocpl09bgbbQLChj3qVGbgVsACAAAAAAF+1nS7O0v85s3CCy+9HkdeoEfm2C6ZiNbPMMnSfsMHUAAzE2OQB9AAAABWQAIAAAAAC2VuRdaC4ZJmLdNOvD6R2tnvkyARteqXouJmI46V306QVzACAAAAAAMn1Z6B35wFTX9mEYAPM+IiJ5hauEwfD0CyIvBrxHg7IFbAAgAAAAAOG6DvDZkT9B/xZWmjao2AevN7MMbs3Oh9YJeSd/hZ+hAAMxNzAAfQAAAAVkACAAAAAAVerb7qVNy457rNOHOgDSKyWl5ojun7iWrv1uHPXrIZQFcwAgAAAAAIDcYS9j5z+gx0xdJj09L7876r/vjvKTi/d3bXDE3PhyBWwAIAAAAADuhVLqb1Bkrx8aNymS+bx2cL8GvLFNH4SAi690DUgnWQADMTcxAH0AAAAFZAAgAAAAAH/E44yLxKCJjuSmU9A8SEhbmkDOx1PqqtYcZtgOzJdrBXMAIAAAAABgLh9v2HjBbogrRoQ82LS6KjZQnzjxyJH4PH+F3jupSAVsACAAAAAAIlO46ehXp4TqpDV0t6op++KO+uWBFh8iFORZjmx2IjkAAzE3MgB9AAAABWQAIAAAAAAlNUdDL+f/SSQ5074mrq0JNh7CTXwTbbhsQyDwWeDVMwVzACAAAAAANIH2IlSNG0kUw4qz0budjcWn8mNR9cJlYUqPYdonucAFbAAgAAAAAJMrOUOyiu5Y3sV76zwEFct8L7+i8WGlQI2+8z2W2kzaAAMxNzMAfQAAAAVkACAAAAAASZ+CvUDtlk/R4HAQ3a+PHrKeY/8ifAfh0oXYFqliu80FcwAgAAAAAJelpzPgM65OZFt/mvGGpwibclQ49wH+1gbUGzd9OindBWwAIAAAAAD9qeDchteEpVXWcycmD9kl9449C1dOw0r60TBm5jK+cQADMTc0AH0AAAAFZAAgAAAAAN9fkoUVbvFV2vMNMAkak4gYfEnzwKI3eDM3pnDK5q3lBXMAIAAAAACnDkgVNVNUlbQ9RhR6Aot2nVy+U4km6+GHPkLr631jEAVsACAAAAAANzg/BnkvkmvOr8nS4omF+q9EG/4oisB+ul4YHi938hwAAzE3NQB9AAAABWQAIAAAAAASyK3b1nmNCMptVEGOjwoxYLLS9fYWm/Zxilqea0jpEQVzACAAAAAADDHsGrbqlKGEpxlvfyqOJKQJjwJrzsrB7k3HG0AUJbkFbAAgAAAAAKwx3S4XfDZh4+LuI9jf7XgUh5qiefNv87JD4qvVRfPSAAMxNzYAfQAAAAVkACAAAAAAlSP9iK31GlcG9MKGbLmq+VXMslURr+As736rrVNXcsUFcwAgAAAAAAvbj0zfq9zzi8XReheKFbCB+h9IsOLgXPPpI5vrEJNZBWwAIAAAAABXvoZhaQE7ogWjeBjceVkp03N20cKYP3TA8vuNsgpfAgADMTc3AH0AAAAFZAAgAAAAAOJNORH8Bev97gVU7y6bznOxJ+E6Qoykur1QP76hG1/7BXMAIAAAAAC+C1PtOOrSZgzBAGhr+dPe/kR0JUw9GTwLVNr61xC1aAVsACAAAAAAeA/L8MQIXkamaObtMPLpoDoi5FypA5WAPtMeMrgi0eQAAzE3OAB9AAAABWQAIAAAAAAKcHzLUomavInN6upPkyWhAqYQACP/vdVCIYpiy6U6HgVzACAAAAAATsR4KItY6R2+U7Gg6sJdaEcf58gjd1OulyWovIqfxKcFbAAgAAAAAFbm10ko67ahboAejQdAV0U2uA5OhZYdb8XUFJ8OL46LAAMxNzkAfQAAAAVkACAAAAAAqTOLiMpCdR59tLZzzIPqJvbCNvz2XQL9ust0qYaehtcFcwAgAAAAAArefox/3k5xGOeiw2m6NUdzuGxmPwcu5IFcj+jMwHgHBWwAIAAAAADLZGFJ7MQd5JXMgMXjqZO5LDLxcFClcXPlnRMWRn+1oAADMTgwAH0AAAAFZAAgAAAAAIPSqSeVzSRgNVNmrPYHmUMgykCY27NbdDUNhE5kx/SgBXMAIAAAAAAhX90nNfxyXmZe/+btZ7q6xMX4PFyj0paM1ccJ/5IUUQVsACAAAAAA419oHmD2W0SYoOMwhrhrp8jf68fg9hTkaRdCuVd3CN0AAzE4MQB9AAAABWQAIAAAAACLn5DxiqAosHGXIAY96FwFKjeqrzXWf3VJIQMwx1fl4gVzACAAAAAAindvU27nveutopdvuHmzdENBbeGFtI3Qcsr07jxmvm8FbAAgAAAAAPvl9pBStQvP4OGkN5v0MghUY6djm9n7XdKKfrW0l1sMAAMxODIAfQAAAAVkACAAAAAA7i2S6rHRSPBwZEn59yxaS7HiYBOmObIkeyCcFU42kf8FcwAgAAAAAGb3RSEyBmgarkTvyLWtOLJcPwCKbCRkESG4RZjVmY4iBWwAIAAAAADB2/wo5CSHR4ANtifY6ZRXNTO5+O8qP82DfAiAeanpZwADMTgzAH0AAAAFZAAgAAAAAFz+M+H/Z94mdPW5oP51B4HWptp1rxcMWAjnlHvWJDWrBXMAIAAAAACBFEOQyL7ZHu4Cq33QvXkmKuH5ibG/Md3RaED9CtG5HwVsACAAAAAAfggtJTprQ/yZzj7y5z9KvXsdeXMWP0yUXMMJqpOwI88AAzE4NAB9AAAABWQAIAAAAAAE7c2x3Z3aM1XGfLNk/XQ9jCazNRbGhVm7H8c2NjS5ywVzACAAAAAARJ9h8fdcwA19velF3L/Wcvi2rCzewlKZ2nA0p8bT9uwFbAAgAAAAAJtWe6b4wK2Hae2dZm/OEpYQnvoZjz4Sz5IgJC2wInecAAMxODUAfQAAAAVkACAAAAAAVoRt9B9dNVvIMGN+ea5TzRzQC+lqSZ8dd/170zU5o9cFcwAgAAAAAEwM95XZin5mv2yhCI8+ugtKuvRVmNgzzIQN0yi1+9aIBWwAIAAAAAAMGBq72n00rox3uqhxSB98mkenTGCdbbUF1gXrgottzgADMTg2AH0AAAAFZAAgAAAAAKRDkjyWv/etlYT4GyoXrmBED2FgZHnhc+l9Wsl06cH2BXMAIAAAAABohlpm3K850Vndf3NmNE0hHqDlNbSR8/IvMidQ3LnIZAVsACAAAAAAW42nGHa6q2MCAaaPVwaIDfr8QLyQwjKq23onZJYsqVsAAzE4NwB9AAAABWQAIAAAAAC3DFh5oklLCNLY90bgWm68dFXz65JpAZSp1K99MBTPAQVzACAAAAAAQgZecmxEUZVHoptEQClDwAf8smI3WynQ/i+JBP0g+kQFbAAgAAAAAEUSQGVnAPISD6voD0DiBUqyWKgt2rta0tjmoe+LNt6IAAMxODgAfQAAAAVkACAAAAAAQ5WKvWSB503qeNlOI2Tpjd5blheNr6OBO8pfJfPNstcFcwAgAAAAAKwHgQLSDJ5NwLBQbY5OnblQIsVDpGV7q3RCbFLD1U4/BWwAIAAAAACQ5nED99LnpbqXZuUOUjnO2HTphEAFBjLD4OZeDEYybgADMTg5AH0AAAAFZAAgAAAAAGfhFY3RGRm5ZgWRQef1tXxHBq5Y6fXaLAR4yJhrTBplBXMAIAAAAACKEF0ApLoB6lP2UqTFsTQYNc9OdDrs/vziPGzttGVLKQVsACAAAAAArOO6FyfNRyBi0sPT5iye7M8d16MTLcwRfodZq4uCYKEAAzE5MAB9AAAABWQAIAAAAAAIM73gPcgzgotYHLeMa2zAU4mFsr7CbILUZWfnuKSwagVzACAAAAAAJCSu98uV8xv88f2BIOWzt6p+6EjQStMBdkGPUkgN79cFbAAgAAAAAMGqPGMPxXbmYbVfSa/japvUljht1zZT33TY7ZjAiuPfAAMxOTEAfQAAAAVkACAAAAAAkWmHCUsiMy1pwZTHxVPBzPTrWFBUDqHNrVqcyyt7nO8FcwAgAAAAAMv2CebFRG/br7USELR98sIdgE9OQCRBGV5JZCO+uPMgBWwAIAAAAABt7qSmn3gxJu7aswsbUiwvO+G6lXj/Xhx+J/zQyZxzLAADMTkyAH0AAAAFZAAgAAAAAGInUYv0lP/rK7McM8taEHXRefk8Q2AunrvWqdfSV7UaBXMAIAAAAACE+WPxJ3gan7iRTbIxXXx+bKVcaf8kP4JD8DcwU0aL7wVsACAAAAAAUC4eTprX4DUZn2X+UXYU6QjtiXk+u57yoOPBbPQUmDkAAzE5MwB9AAAABWQAIAAAAACmHlg2ud3cplXlTsNTpvNnY6Qm1Fce0m899COamoDjaQVzACAAAAAArtJQeJIlepBWRU2aYar7+YGYVQ7dfDc1oxgTmA8r9q0FbAAgAAAAAOk45vg5VqZHAFCO3i0Z52SZi5RADf8NXwf68T5yad/DAAMxOTQAfQAAAAVkACAAAAAApzcWSAbZWV/Rq+ylRNqqlJqNVR4fhXrz4633/MQOQgcFcwAgAAAAAN/jz/bsEleiuCl+li83EWlG6UMHA8CyaOMRKCkXkSCPBWwAIAAAAAC3Sd+Qg+uFDKpGZHbrQgokXHQ1az1aFl4YK343OB6hcQAAEmNtAAAAAAAAAAAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAEAAAASc3AAAQAAAAAAAAAQdGYAAQAAABNtbgD/////Y46NN8CHrb4J7f/fE214AP////9jjo03wIetvgnt/18A", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedDecimalNoPrecision": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.json new file mode 100644 index 0000000000..e3d52f5d04 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.json @@ -0,0 +1,1956 @@ +{ + "description": "fle2v2-Rangev2-Decimal-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.yml new file mode 100644 index 0000000000..6ca554f0b4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-InsertFind.yml @@ -0,0 +1,449 @@ +description: fle2v2-Rangev2-Decimal-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedDecimalNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "DR1jAAADcGF5bG9hZACxYgAABGcAnWIAAAMwAH0AAAAFZAAgAAAAAJu2KgiI8vM+kz9qD3ZQzFQY5qbgYqCqHG5R4jAlnlwXBXMAIAAAAAAAUXxFXsz764T79sGCdhxvNd5b6E/9p61FonsHyEIhogVsACAAAAAAt19RL3Oo5ni5L8kcvgOJYLgVYyXJExwP8pkuzLG7f/kAAzEAfQAAAAVkACAAAAAAPQPvL0ARjujSv2Rkm8r7spVsgeC1K3FWcskGGZ3OdDIFcwAgAAAAACgNn660GmefR8jLqzgR1u5O+Uocx9GyEHiBqVGko5FZBWwAIAAAAADflr+fsnZngm6KRWYgHa9JzK+bXogWl9evBU9sQUHPHQADMgB9AAAABWQAIAAAAAD2Zi6kcxmaD2mY3VWrP+wYJMPg6cSBIYPapxaFQxYFdQVzACAAAAAAM/cV36BLBY3xFBXsXJY8M9EHHOc/qrmdc2CJmj3M89gFbAAgAAAAAOpydOrKxx6m2gquSDV2Vv3w10GocmNCFeOo/fRhRH9JAAMzAH0AAAAFZAAgAAAAAOaNqI9srQ/mI9gwbk+VkizGBBH/PPWOVusgnfPk3tY1BXMAIAAAAAAc96O/pwKCmHCagT6T/QV/wz4vqO+R22GsZ1dse2Vg6QVsACAAAAAAgzIak+Q3UFLTHXPmJ+MuEklFtR3eLtvM+jdKkmGCV/YAAzQAfQAAAAVkACAAAAAA0XlQgy/Yu97EQOjronl9b3dcR1DFn3deuVhtTLbJZHkFcwAgAAAAACoMnpVl6EFJak8A+t5N4RFnQhkQEBnNAx8wDqmq5U/dBWwAIAAAAACR26FJif673qpwF1J1FEkQGJ1Ywcr/ZW6JQ7meGqzt1QADNQB9AAAABWQAIAAAAAAOtpNexRxfv0yRFvZO9DhlkpU4mDuAb8ykdLnE5Vf1VAVzACAAAAAAeblFKm/30orP16uQpZslvsoS8s0xfNPIBlw3VkHeekYFbAAgAAAAAPEoHj87sYE+nBut52/LPvleWQBzB/uaJFnosxp4NRO2AAM2AH0AAAAFZAAgAAAAAIr8xAFm1zPmrvW4Vy5Ct0W8FxMmyPmFzdWVzesBhAJFBXMAIAAAAABYeeXjJEzTHwxab6pUiCRiZjxgtN59a1y8Szy3hfkg+gVsACAAAAAAJuoY4rF8mbI+nKb+5XbZShJ8191o/e8ZCRHE0O4Ey8MAAzcAfQAAAAVkACAAAAAAl+ibLk0/+EwoqeC8S8cGgAtjtpQWGEZDsybMPnrrkwEFcwAgAAAAAHPPBudWgQ+HUorLDpJMqhS9VBF2VF5aLcxgrM1s+yU7BWwAIAAAAAAcCcBR2Vyv5pAFbaOU97yovuOi1+ATDnLLcAUqHecXcAADOAB9AAAABWQAIAAAAACR9erwLTb+tcWFZgJ2MEfM0PKI9uuwIjDTHADRFgD+SQVzACAAAAAAcOop8TXsGUVQoKhzUllMYWxL93xCOkwtIpV8Q6hiSYYFbAAgAAAAAKXKmh4V8veYwob1H03Q3p3PN8SRAaQwDT34KlNVUjiDAAM5AH0AAAAFZAAgAAAAALv0vCPgh7QpmM8Ug6ad5ioZJCh7pLMdT8FYyQioBQ6KBXMAIAAAAADsCPyIG8t6ApQkRk1fX/sfc1kpuWCWP8gAEpnYoBSHrQVsACAAAAAAJe/r67N6d8uTiogvfoR9rEXbIDjyLb9EVdqkayFFGaYAAzEwAH0AAAAFZAAgAAAAAIW4AxJgYoM0pcNTwk1RSbyjZGIqgKL1hcTJmNrnZmoPBXMAIAAAAAAZpfx3EFO0vY0f1eHnE0PazgqeNDTaj+pPJMUNW8lFrAVsACAAAAAAP+Um2vwW6Bj6vuz9DKz6+6aWkoKoEmFNoiz/xXm7lOsAAzExAH0AAAAFZAAgAAAAAKliO6L9zgeuufjj174hvmQGNRbmYYs9yAirL7OxwEW3BXMAIAAAAAAqU7vs3DWUQ95Eq8OejwWnD0GuXd+ASi/uD6S0l8MM1QVsACAAAAAAb9legYzsfctBPpHyl7YWpPmLr5QiNZFND/50N1vv2MUAAzEyAH0AAAAFZAAgAAAAAOGQcCBkk+j/Kzjt/Cs6g3BZPJG81wIHBS8JewHGpgk+BXMAIAAAAABjrxZXWCkdzrExwCgyHaafuPSQ4V4x2k9kUCAqUaYKDQVsACAAAAAADBU6KefT0v8zSmseaMNmQxKjJar72y7MojLFhkEHqrUAAzEzAH0AAAAFZAAgAAAAAPmCNEt4t97waOSd5hNi2fNCdWEkmcFJ37LI9k4Az4/5BXMAIAAAAABX7DuDPNg+duvELf3NbLWkPMFw2HGLgWGHyVWcPvSNCAVsACAAAAAAS7El1FtZ5STh8Q1FguvieyYX9b2DF1DFVsb9hzxXYRsAAzE0AH0AAAAFZAAgAAAAAD4vtVUYRNB+FD9yoQ2FVJH3nMeJeKbi6eZfth638YqbBXMAIAAAAAANCuUB4OdmuD6LaDK2f3vaqfgYYvg40wDXOBbcFjTqLwVsACAAAAAA9hqC2VoJBjwR7hcQ45xO8ZVojwC83jiRacCaDj6Px2gAAzE1AH0AAAAFZAAgAAAAAJPIRzjmTjbdIvshG6UslbEOd797ZSIdjGAhGWxVQvK1BXMAIAAAAABgmJ0Jh8WLs9IYs/a7DBjDWd8J3thW/AGJK7zDnMeYOAVsACAAAAAAi9zAsyAuou2oiCUHGc6QefLUkACa9IgeBhGu9W/r0X8AAzE2AH0AAAAFZAAgAAAAAABQyKQPoW8wGPIqnsTv69+DzIdRkohRhOhDmyVHkw9WBXMAIAAAAAAqWA2X4tB/h3O1Xlawtz6ndI6WaTwgU1QYflL35opu5gVsACAAAAAAWI/Gj5aZMwDIxztqmVL0g5LBcI8EdKEc2UA28pnekQoAAzE3AH0AAAAFZAAgAAAAACB7NOyGQ1Id3MYnxtBXqyZ5Ul/lHH6p1b10U63DfT6bBXMAIAAAAADpOryIcndxztkHSfLN3Kzq29sD8djS0PspDSqERMqokQVsACAAAAAADatsMW4ezgnyi1PiP7xk+gA4AFIN/fb5uJqfVkjg4UoAAzE4AH0AAAAFZAAgAAAAAKVfXLfs8XA14CRTB56oZwV+bFJN5BHraTXbqEXZDmTkBXMAIAAAAAASRWTsfGOpqdffiOodoqIgBzG/yzFyjR5CfUsIUIWGpgVsACAAAAAAkgCHbCwyX640/0Ni8+MoYxeHUiC+FSU4Mn9jTLYtgZgAAzE5AH0AAAAFZAAgAAAAAH/aZr4EuS0/noQR9rcF8vwoaxnxrwgOsSJ0ys8PkHhGBXMAIAAAAACd7ObGQW7qfddcvyxRTkPuvq/PHu7+6I5dxwS1Lzy5XAVsACAAAAAA3q0eKdV7KeU3pc+CtfypKR7BPxwaf30yu0j9FXeOOboAAzIwAH0AAAAFZAAgAAAAAKvlcpFFNq0oA+urq3w6d80PK1HHHw0H0yVWvU9aHijXBXMAIAAAAADWnAHQ5Fhlcjawki7kWzdqjM2f6IdGJblojrYElWjsZgVsACAAAAAAO0wvY66l24gx8nRxyVGC0QcTztIi81Kx3ndRhuZr6W4AAzIxAH0AAAAFZAAgAAAAAH/2aMezEOddrq+dNOkDrdqf13h2ttOnexZsJxG1G6PNBXMAIAAAAABNtgnibjC4VKy5poYjvdsBBnVvDTF/4mmEAxsXVgZVKgVsACAAAAAAqvadzJFLqQbs8WxgZ2D2X+XnaPSDMLCVVgWxx5jnLcYAAzIyAH0AAAAFZAAgAAAAAF2wZoDL6/V59QqO8vdRZWDpXpkV4h4KOCSn5e7x7nmzBXMAIAAAAADLZBu7LCYjbThaVUqMK14H/elrVOYIKJQCx4C9Yjw37gVsACAAAAAAEh6Vs81jLU204aGpL90fmYTm5i5R8/RT1uIbg6VU3HwAAzIzAH0AAAAFZAAgAAAAAH27yYaLn9zh2CpvaoomUPercSfJRUmBY6XFqmhcXi9QBXMAIAAAAAAUwumVlIYIs9JhDhSj0R0+59psCMsFk94E62VxkPt42QVsACAAAAAAT5x2hCCd2bpmpnyWaxas8nSxTc8e4C9DfKaqr0ABEysAAzI0AH0AAAAFZAAgAAAAALMg2kNAO4AFFs/mW3In04yFeN4AP6Vo0klyUoT06RquBXMAIAAAAAAgGWJbeIdwlpqXCyVIYSs0dt54Rfc8JF4b8uYc+YUj0AVsACAAAAAAWHeWxIkyvXTOWvfZzqtPXjfGaWWKjGSIQENTU3zBCrsAAzI1AH0AAAAFZAAgAAAAALas/i1T2DFCEmrrLEi7O2ngJZyFHialOoedVXS+OjenBXMAIAAAAAA1kK0QxY4REcGxHeMkgumyF7iwlsRFtw9MlbSSoQY7uAVsACAAAAAAUNlpMJZs1p4HfsD4Q4WZ4TBEi6Oc2fX34rzyynqWCdwAAzI2AH0AAAAFZAAgAAAAAP1TejmWg1CEuNSMt6NUgeQ5lT+oBoeyF7d2l5xQrbXWBXMAIAAAAABPX0kj6obggdJShmqtVfueKHplH4ZrXusiwrRDHMOKeQVsACAAAAAAIYOsNwC3DA7fLcOzqdr0bOFdHCfmK8tLwPoaE9uKOosAAzI3AH0AAAAFZAAgAAAAAMrKn+QPa/NxYezNhlOX9nyEkN1kE/gW7EuZkVqYl0b8BXMAIAAAAABUoZMSPUywRGfX2EEencJEKH5x/P9ySUVrhStAwgR/LgVsACAAAAAAMgZFH6lQIIDrgHnFeslv3ld20ynwQjQJt3cAp4GgrFkAAzI4AH0AAAAFZAAgAAAAAMmD1+a+oVbiUZd1HuZqdgtdVsVKwuWAn3/M1B6QGBM3BXMAIAAAAACLyytOYuZ9WEsIrrtJbXUx4QgipbaAbmlJvSZVkGi0CAVsACAAAAAA4v1lSp5H9BB+HYJ4bH43tC8aeuPZMf78Ng1JOhJh190AAzI5AH0AAAAFZAAgAAAAAOVKV7IuFwmYP1qVv8h0NvJmfPICu8yQhzjG7oJdTLDoBXMAIAAAAABL70XLfQLKRsw1deJ2MUvxSWKxpF/Ez73jqtbLvqbuogVsACAAAAAAvfgzIorXxE91dDt4nQxYfntTsx0M8Gzdsao5naQqcRUAAzMwAH0AAAAFZAAgAAAAAKS/1RSAQma+xV9rz04IcdzmavtrBDjOKPM+Z2NEyYfPBXMAIAAAAAAOJDWGORDgfRv8+w5nunh41wXb2hCA0MRzwnLnQtIqPgVsACAAAAAAf42C1+T7xdHEFF83+c2mF5S8PuuL22ogXXELnRAZ4boAAzMxAH0AAAAFZAAgAAAAAFeq8o82uNY1X8cH6OhdTzHNBUnCChsEDs5tm0kPBz3qBXMAIAAAAABaxMBbsaeEj/EDtr8nZfrhhhirBRPJwVamDo5WwbgvTQVsACAAAAAAMbH453A+BYAaDOTo5kdhV1VdND1avNwvshEG/4MIJjQAAzMyAH0AAAAFZAAgAAAAAI8IKIfDrohHh2cjspJHCovqroSr5N3QyVtNzFvT5+FzBXMAIAAAAABXHXteKG0DoOMmECKp6ro1MZNQvXGzqTDdZ0DUc8QfFAVsACAAAAAA/w5s++XYmO+9TWTbtGc3n3ndV4T9JUribIbF4jmDLSMAAzMzAH0AAAAFZAAgAAAAAJkHvm15kIu1OtAiaByj5ieWqzxiu/epK6c/9+KYIrB0BXMAIAAAAACzg5TcyANk0nes/wCJudd1BwlkWWF6zw3nGclq5v3SJQVsACAAAAAAvruXHTT3irPJLyWpI1j/Xwf2FeIE/IV+6Z49pqRzISoAAzM0AH0AAAAFZAAgAAAAAAYSOvEWWuSg1Aym7EssNLR+xsY7e9BcwsX4JKlnSHJcBXMAIAAAAABT48eY3PXVDOjw7JpNjOe1j2JyI3LjDnQoqZ8Je5B2KgVsACAAAAAAU2815RR57TQ9uDg0XjWjBkAKvf8yssxDMzrM4+FqP6AAAzM1AH0AAAAFZAAgAAAAAGQxC9L1e9DfO5XZvX1yvc3hTLtQEdKO9FPMkyg0Y9ZABXMAIAAAAADtmcMNJwdWLxQEArMGZQyzpnu+Z5yMmPAkvgq4eAKwNQVsACAAAAAAJ88zt4Y/Hoqh+zrf6KCOiUwHbOzCxSfp6k/qsZaYGEgAAzM2AH0AAAAFZAAgAAAAADLHK2LNCNRO0pv8n4fAsxwtUqCNnVK8rRgNiQfXpHSdBXMAIAAAAACf16EBIHRKD3SzjRW+LMOl+47QXA3CJhMzlcqyFRW22AVsACAAAAAAMGz4fAOa0EoVv90fUffwLjBrQhHATf+NdlgCR65vujAAAzM3AH0AAAAFZAAgAAAAAHiZJiXKNF8bbukQGsdYkEi95I+FSBHy1I5/hK2uEZruBXMAIAAAAADE+lZBa8HDUJPN+bF6xI9x4N7GF9pj3vBR7y0BcfFhBAVsACAAAAAAGIEN6sfqq30nyxW4dxDgXr/jz5HmvA9T1jx/pKCn4zgAAzM4AH0AAAAFZAAgAAAAAI1oa2OIw5TvhT14tYCGmhanUoYcCZtNbrVbeoMldHNZBXMAIAAAAAAx2nS0Ipblf2XOgBiUOuJFBupBhe7nb6QPLZlA4aMPCgVsACAAAAAA9xu828hugIgo0E3de9dZD+gTpVUGlwtDba+tw/WcbUoAAzM5AH0AAAAFZAAgAAAAABgTWS3Yap7Q59hii/uPPimHWXsr+DUmsqfwt/X73qsOBXMAIAAAAACKK05liW5KrmEAvtpCB1WUltruzUylDDpjea//UlWoOAVsACAAAAAAcgN4P/wakJ5aJK5c1bvJBqpVGND221dli2YicPFfuAYAAzQwAH0AAAAFZAAgAAAAABOAnBPXDp6i9TISQXvcNKwGDLepZTu3cKrB4vKnSCjBBXMAIAAAAADjjzZO7UowAAvpwyG8BNOVqLCccMFk3aDK4unUeft5ywVsACAAAAAA4zkCd4k9gvfXoD1C7vwTjNcdVJwEARh8h/cxZ4PNMfgAAzQxAH0AAAAFZAAgAAAAAHN8hyvT1lYrAsdiV5GBdd5jhtrAYE/KnSjw2Ka9hjz9BXMAIAAAAAD794JK7EeXBs+D7yOVK7nWF8SbZ/7U8gZ7nnT9JFNwTAVsACAAAAAAg8Wt1HO3NhByq2ggux2a4Lo6Gryr24rEFIqh2acrwWMAAzQyAH0AAAAFZAAgAAAAAO93bPrq8bsnp1AtNd9ETnXIz0lH/2HYN/vuw9wA3fyFBXMAIAAAAABHlls5fbaF2oAGqptC481XQ4eYxInTC29aElfmVZgDUgVsACAAAAAANoQXEWpXJpgrSNK/cKi/m7oYhuSRlp1IZBF0bqTEATcAAzQzAH0AAAAFZAAgAAAAAL1YsAZm1SA0ztU6ySIrQgCCA74V6rr0/4iIygCcaJL6BXMAIAAAAADTXWTHWovGmUR1Zg9l/Aqq9H5mOCJQQrb/Dfae7e3wKAVsACAAAAAA5dunyJK6/SVfDD0t9QlNBcFqoZnf9legRjHaLSKAoQMAAzQ0AH0AAAAFZAAgAAAAAEoFAeHk0RZ9kD+cJRD3j7PcE5gzWKnyBrF1I/MDNp5mBXMAIAAAAACgHtc2hMBRSZjKw8RAdDHK+Pi1HeyjiBuAslGVNcW5tAVsACAAAAAAXzBLfq+GxRtX4Wa9fazA49DBLG6AjZm2XODStJKH8D0AAzQ1AH0AAAAFZAAgAAAAAAW+7DmSN/LX+/0uBVJDHIc2dhxAGz4+ehyyz8fAnNGoBXMAIAAAAAA6Ilw42EvvfLJ3Eq8Afd+FjPoPcQutZO6ltmCLEr8kxQVsACAAAAAAbbZalyo07BbFjPFlYmbmv0z023eT9eLkHqeVUnfUAUAAAzQ2AH0AAAAFZAAgAAAAANBdV7M7kuYO3EMoQItAbXv4t2cIhfaT9V6+s4cg9djlBXMAIAAAAABvz4MIvZWxxrcJCL5qxLfFhXiUYB1OLHdKEjco94SgDgVsACAAAAAAK2GVGvyPIKolF/ECcmfmkVcf1/IZNcaTv96N92yGrkEAAzQ3AH0AAAAFZAAgAAAAAMoAoiAn1kc79j5oPZtlMWHMhhgwNhLUnvqkqIFvcH1NBXMAIAAAAADcJTW7WiCyW0Z9YDUYwppXhLj4Ac1povpJvcAq+i48MQVsACAAAAAAIGxGDzoeB3PTmudl4+j6piQB++e33EEzuzAiXcqGxvUAAzQ4AH0AAAAFZAAgAAAAACI3j5QP7dWHpcT6WO/OhsWwRJNASBYqIBDNzW8IorEyBXMAIAAAAABxUpBSjXwCKDdGP9hYU+RvyR+96kChfvyyRC4jZmztqAVsACAAAAAAvBCHguWswb4X0xdcAryCvZgQuthXzt7597bJ5VxAMdgAAzQ5AH0AAAAFZAAgAAAAAKsbycEuQSeNrF8Qnxqw3x3og8JmQabwGqnDbqzFRVrrBXMAIAAAAACno/3ef2JZJS93SVVzmOZSN+jjJHT8s0XYq2M46d2sLAVsACAAAAAAAt5zLJG+/j4K8rnkFtAn8IvdUVNefe6utJ3rdzgwudIAAzUwAH0AAAAFZAAgAAAAAPXIcoO8TiULqlxzb74NFg+I8kWX5uXIDUPnh2DobIoMBXMAIAAAAADR6/drkdTpnr9g1XNvKDwtBRBdKn7c2c4ZNUVK5CThdQVsACAAAAAAJqOA1c6KVog3F4Hb/GfDb3jCxXDRTqpXWSbMH4ePIJsAAzUxAH0AAAAFZAAgAAAAAEa03ZOJmfHT6/nVadvIw71jVxEuIloyvxXraYEW7u7pBXMAIAAAAADzRlBJK75FLiKjz3djqcgjCLo/e3yntI3MnPS48OORhgVsACAAAAAAnQhx4Rnyj081XrLRLD5NLpWmRWCsd0M9Hl7Jl19R0h8AAzUyAH0AAAAFZAAgAAAAAKx8NLSZUU04pSSGmHa5fh2oLHsEN5mmNMNHL95/tuC9BXMAIAAAAAA59hcXVaN3MNdHoo11OcH1aPRzHCwpVjO9mGfMz4xh3QVsACAAAAAAYIPdjV2XbPj7dBeHPwnwhVU7zMuJ+xtMUW5mIOYtmdAAAzUzAH0AAAAFZAAgAAAAAHNKAUxUqBFNS9Ea9NgCZoXMWgwhP4x0/OvoaPRWMquXBXMAIAAAAABUZ551mnP4ZjX+PXU9ttomzuOpo427MVynpkyq+nsYCQVsACAAAAAALnVK5p2tTTeZEh1zYt4iqKIQT9Z0si//Hy1L85oF+5IAAzU0AH0AAAAFZAAgAAAAALfGXDlyDVcGaqtyHkLT0qpuRhJQLgCxtznazhFtuyn/BXMAIAAAAABipxlXDq14C62pXhwAeen5+syA+/C6bN4rtZYcO4zKwAVsACAAAAAAXUf0pzUq0NhLYagWDap4uEiwq5rLpcx29rWbt1NYMsMAAzU1AH0AAAAFZAAgAAAAANoEr8sheJjg4UCfBkuUzarU9NFoy1xwbXjs5ifVDeA9BXMAIAAAAABPoyTf6M+xeZVGES4aNzVlq7LgjqZXJ/QunjYVusGUEAVsACAAAAAA1hA2gMeZZPUNytk9K+lB1RCqWRudRr7GtadJlExJf8oAAzU2AH0AAAAFZAAgAAAAAKvDiK+xjlBe1uQ3SZTNQl2lClIIvpP/5CHwY6Kb3WlgBXMAIAAAAAANnxImq5MFbWaRBHdJp+yD09bVlcFtiFDYsy1eDZj+iQVsACAAAAAAWtsyO+FxMPSIezwsV1TJD8ZrXAdRnQM6DJ+f+1V3qEkAAzU3AH0AAAAFZAAgAAAAAF49IlFH9RmSUSvUQpEPUedEksrQUcjsOv44nMkwXhjzBXMAIAAAAADJtWGbk0bZzmk20obz+mNsp86UCu/nLLlbg7ppxYn7PgVsACAAAAAA3k0Tj/XgPQtcYijH8cIlQoe/VXf15q1nrZNmg7yWYEgAAzU4AH0AAAAFZAAgAAAAAOuSJyuvz50lp3BzXlFKnq62QkN2quNU1Gq1IDsnFoJCBXMAIAAAAAAqavH1d93XV3IzshWlMnzznucadBF0ND092/2ApI1AcAVsACAAAAAAzUrK4kpoKCmcpdZlZNI13fddjdoAseVe67jaX1LobIIAAzU5AH0AAAAFZAAgAAAAALtgC4Whb4ZdkCiI30zY6fwlsxSa7lEaOAU3SfUXr02XBXMAIAAAAACgdZ6U1ZVgUaZZwbIaCdlANpCw6TZV0bwg3DS1NC/mnAVsACAAAAAAzI49hdpp0PbO7S2KexISxC16sE73EUAEyuqUFAC/J48AAzYwAH0AAAAFZAAgAAAAAF6PfplcGp6vek1ThwenMHVkbZgrc/dHgdsgx1VdPqZ5BXMAIAAAAACha3qhWkqmuwJSEXPozDO8y1ZdRLyzt9Crt2vjGnT7AAVsACAAAAAA7nvcU59+LwxGupSF21jAeAE0x7JE94tjRkJfgM1yKU8AAzYxAH0AAAAFZAAgAAAAAKoLEhLvLjKc7lhOJfx+VrGJCx9tXlOSa9bxQzGR6rfbBXMAIAAAAAAIDK5wNnjRMBzET7x/KAMExL/zi1IumJM92XTgXfoPoAVsACAAAAAAFkUYWFwNr815dEdFqp+TiIozDcq5IBNVkyMoDjharDQAAzYyAH0AAAAFZAAgAAAAADoQv6lutRmh5scQFvIW6K5JBquLxszuygM1tzBiGknIBXMAIAAAAADAD+JjW7FoBQ76/rsECmmcL76bmyfXpUU/awqIsZdO+wVsACAAAAAAPFHdLw3jssmEXsgtvl/RBNaUCRA1kgSwsofG364VOvQAAzYzAH0AAAAFZAAgAAAAAJNHUGAgn56KekghO19d11nai3lAh0JAlWfeP+6w4lJBBXMAIAAAAAD9XGJlvz59msJvA6St9fKW9CG4JoHV61rlWWnkdBRLzwVsACAAAAAAxwP/X/InJJHmrjznvahIMgj6pQR30B62UtHCthSjrP0AAzY0AH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzY1AH0AAAAFZAAgAAAAANpIljbxHOM7pydY877gpRQvYY2TGK7igqgGsavqGPBABXMAIAAAAAAqHyEu9gpurPOulApPnr0x9wrygY/7mXe9rAC+tPK80wVsACAAAAAA7gkPzNsS3gCxdFBWbSW9tkBjoR5ib+saDvpGSB3A3ogAAzY2AH0AAAAFZAAgAAAAAGR+gEaZTeGNgG9BuM1bX2R9ed4FCxBA9F9QvdQDAjZwBXMAIAAAAABSkrYFQ6pf8MZ1flgmeIRkxaSh/Eep4Btdx4QYnGGnwAVsACAAAAAApRovMiV00hm/pEcT4XBsyPNw0eo8RLAX/fuabjdU+uwAAzY3AH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzY4AH0AAAAFZAAgAAAAADgyPqQdqQrgfmJjRFAILTHzXbdw5kpKyfeoEcy6YYG/BXMAIAAAAAAE+3XsBQ8VAxAkN81au+f3FDeCD/s7KoZD+fnM1MJSSAVsACAAAAAAhRnjrXecwV0yeCWKJ5J/x12Xx4qVJahsCEVHB/1U2rcAAzY5AH0AAAAFZAAgAAAAAI0CT7JNngTCTUSei1Arw7eHWCD0jumv2rb7imjWIlWABXMAIAAAAABSP8t6ya0SyCphXMwnru6ZUDXWElN0NfBvEOhDvW9bJQVsACAAAAAAGWeGmBNDRaMtvm7Rv+8TJ2sJ4WNXKcp3tqpv5Se9Ut4AAzcwAH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcxAH0AAAAFZAAgAAAAAHIkVuNDkSS1cHIThKc/O0r2/ubaABTOi8Q1r/dvBAsEBXMAIAAAAADdHYqchEiJLM340c3Q4vJABmmth3+MKzwLYlsG6GS7sQVsACAAAAAADa+KP/pdTiG22l+ZWd30P1iHjnBF4zSNRdFm0oEK82kAAzcyAH0AAAAFZAAgAAAAAJmoDILNhC6kn3masElfnjIjP1VjsjRavGk1gSUIjh1NBXMAIAAAAAD97Ilvp3XF8T6MmVVcxMPcdL80RgQ09UoC6PnoOvZ1IQVsACAAAAAA2RK3Xng6v8kpvfVW9tkVXjpE+BSnx9/+Fw85Evs+kUEAAzczAH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzc0AH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzc1AH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzc2AH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzc3AH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzc4AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzc5AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzgwAH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzgxAH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzgyAH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzgzAH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzg0AH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzg1AH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzg2AH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzg3AH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzg4AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzg5AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzkwAH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzkxAH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzkyAH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzkzAH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzk0AH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzk1AH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzk2AH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzk3AH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzk4AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzk5AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzEwMAB9AAAABWQAIAAAAADJDdC9aEFl4Y8J/awHbnXGHjfP+VXQilPHJg7ewaJI7AVzACAAAAAAE+tqRl6EcBMXvbr4GDiNIYObTsYpa1n6BJk9EjIJVicFbAAgAAAAAJVc+HYYqa0m1Hq6OiRX8c0iRnJYOt6AJAJoG0sG3GMSAAMxMDEAfQAAAAVkACAAAAAA3F9rjEKhpoHuTULVGgfUsGGwJs3bISrXkFP1v6KoQLgFcwAgAAAAAIBf0tXw96Z/Ds0XSIHX/zk3MzUR/7WZR/J6FpxRWChtBWwAIAAAAABWrjGlvKYuTS2s8L9rYy8Hf0juFGJfwQmxVIjkTmFIGQADMTAyAH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzEwMwB9AAAABWQAIAAAAACMtPm12YtdEAvqu6Eji1yuRXnu1RJP6h0l7pH3lSH4MwVzACAAAAAAENyCFfyUAh1veQBGx+cxiB7Sasrj41jzCGflZkB5cRMFbAAgAAAAAKdI2LMqISr/T5vuJPg6ZRBm5fVi2aQCc4ra3A4+AjbDAAMxMDQAfQAAAAVkACAAAAAAvlI4lDcs6GB1cnm/Tzo014CXWqidCdyE5t2lknWQd4QFcwAgAAAAAD60SpNc4O2KT7J0llKdSpcX1/Xxs97N715a1HsTFkmBBWwAIAAAAABuuRkJWAH1CynggBt1/5sPh9PoGiqTlS24D/OE2uHXLQADMTA1AH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzEwNgB9AAAABWQAIAAAAABb6LXDWqCp1beQgQjj8I3sRTtFhlrmiBi+h/+ikmrvugVzACAAAAAA9stpgTecT7uTyaGNs3K9Bp0A7R0QaIAOfscyMXHBPX8FbAAgAAAAAHUt+McyXrJ1H8SwnHNVO181Ki8vDAM1f7XI26mg95ZDAAMxMDcAfQAAAAVkACAAAAAA97NTT+81PhDhgptNtp4epzA0tP4iNb9j1AWkiiiKGM8FcwAgAAAAAKPbHg7ise16vxmdPCzksA/2Mn/qST0L9Xe8vnQugVkcBWwAIAAAAABB0EMXfvju4JU/mUH/OvxWbPEl9NJkcEp4iCbkXI41fAADMTA4AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzEwOQB9AAAABWQAIAAAAADQnslvt6Hm2kJPmqsTVYQHE/wWeZ4bE1XSkt7TKy0r1gVzACAAAAAA8URTA4ZMrhHPvlp53TH6FDCzS+0+61qHm5XK6UiOrKEFbAAgAAAAAHQbgTCdZcbdA0avaTmZXUKnIS7Nwf1tNrcXDCw+PdBRAAMxMTAAfQAAAAVkACAAAAAAhujlgFPFczsdCGXtQ/002Ck8YWQHHzvWvUHrkbjv4rwFcwAgAAAAALbV0lLGcSGfE7mDM3n/fgEvi+ifjl7WZ5b3aqjDNvx9BWwAIAAAAACbceTZy8E3QA1pHmPN5kTlOx3EO8kJM5PUjTVftw1VpgADMTExAH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzExMgB9AAAABWQAIAAAAACfw9/te4GkHZAapC9sDMHHHZgmlTrccyJDPFciOMSOcwVzACAAAAAAIIC1ZpHObvmMwUfqDRPl4C1aeuHwujM1G/yJbvybMNAFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5AAMxMTMAfQAAAAVkACAAAAAAkxHJRbnShpPOylLoDdNShfILeA1hChKFQY9qQyZ5VmsFcwAgAAAAAKidrY+rC3hTY+YWu2a7fuMH2RD/XaiTIBW1hrxNCQOJBWwAIAAAAACW0kkqMIzIFMn7g+R0MI8l15fr3k/w/mHtY5n6SYTEwAADMTE0AH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzExNQB9AAAABWQAIAAAAABxMy7X5hf7AXGDz3Y/POu1ZpkMlNcSvSP92NOO/Gs7wAVzACAAAAAAHJshWo2T5wU2zvqCyJzcJQKQaHFHpCpMc9oWBXkpUPoFbAAgAAAAAGeiJKzlUXAvL0gOlW+Hz1mSa2HsV4RGmyLmCHlzbAkoAAMxMTYAfQAAAAVkACAAAAAAlqbslixl7Zw3bRlibZbe/WmKw23k8uKeIzPKYEtbIy0FcwAgAAAAAHEKwpUxkxOfef5HYvulXPmdbzTivwdwrSYIHDeNRcpcBWwAIAAAAADuPckac21Hrg/h0kt5ShJwVEZ9rx6SOHd2+HDjqxEWTQADMTE3AH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzExOAB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFbAAgAAAAAIgbpyYtJj5513Z5XYqviH/HXG/5+mqR52iBbfqMmDtZAAMxMTkAfQAAAAVkACAAAAAAJRzYK0PUwr9RPG2/7yID0WgcTJPB2Xjccp5LAPDYunkFcwAgAAAAAIIh24h3DrltAzNFhF+MEmPrZtzr1PhCofhChZqfCW+jBWwAIAAAAAAzRNXtL5o9VXMk5D5ylI0odPDJDSZZry1wfN+TedH70gADMTIwAH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzEyMQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FbAAgAAAAAJci0w1ZB8W2spJQ+kMpod6HSCtSR2jrabOH+B0fj3A4AAMxMjIAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWwAIAAAAAAlswzAl81+0DteibwHD+CG5mZJrfHXa9NnEFRtXybzzwADMTIzAH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzEyNAB9AAAABWQAIAAAAAAfPUoy7QyZKhIIURso+mkP9qr1izbjETqF5s22GwjCjAVzACAAAAAAvLMsIDQ/go4VUxeh50UHmsvMvfx51cwyONnRD2odvC0FbAAgAAAAAKMb+1CodEalAFnDrEL1Ndt8ztamZ+9134m9Kp3GQgd+AAMxMjUAfQAAAAVkACAAAAAAE3ZqUar0Bq2zWbARE0bAv98jBlK9UJ73/xcwdMWWlSkFcwAgAAAAAK4M+MmC+9sFiFsumMyJZQKxWmmJiuG9H7IzKw083xxkBWwAIAAAAAAqkAONzhvMhkyL1D/6h7QQxEkdhC3p2WjXH+VGq5qCqQADMTI2AH0AAAAFZAAgAAAAAMo8FJiOq63cAmyk2O7eI7GcbQh/1j4RrMTqly3rexftBXMAIAAAAADjVmpd0WiRGTw/gAqEgGolt2EI7Csv14vKdmYoMD0aAgVsACAAAAAA07XQBzBUQMNw7F2/YxJjZNuPVpHTTgbLd1oGk77+bygAAzEyNwB9AAAABWQAIAAAAACu5IGaIx7A3Jvly/kzlCsSA4s3iJwuIl8jEdRH0k93NwVzACAAAAAA9NRUyxYE+t0Xyosyt6vIfMFW/vBoYg6sR+jBNs4JAxIFbAAgAAAAAAzyZ91dx+0oMlOVAjRGiMrPySikY/U9eMEB4WJb3uWtAAMxMjgAfQAAAAVkACAAAAAALkRy0GJInXYLA+cgjs6Myb0a+Gu9hgXhHvhLNoGWfckFcwAgAAAAANbALyt9zCSvwnLaWCd2/y2eoB7qkWTvv1Ldu8r40JPuBWwAIAAAAAD4Fl5bV5sz4isIE9bX+lmAp+aAKaZgVYVZeVfrItkCZAADMTI5AH0AAAAFZAAgAAAAAGoUK/DSWhT8LZhszSUqDbTrp8cSA7rdqmADKL+MILtTBXMAIAAAAABHnEE9bVa6lvhfhEMkkV2kzSSxH/sMW/FIJuw3CzWs6wVsACAAAAAAanavcBdqZxgRGKvEK95wTmeL1K1CeDSXZsXUAs81uOgAAzEzMAB9AAAABWQAIAAAAAC922ZDQE3h2fQKibGMZ9hV0WNlmrPYYSdtaSyYxsWYqgVzACAAAAAAagMovciKK6WVjIc2cCj8nK5O/gVOFFVeVAJpRp89tmQFbAAgAAAAAKcTFfPQzaFiAtSFhqbN02sCE1BKWJSrRfGN5L6oZwzkAAMxMzEAfQAAAAVkACAAAAAAtK+JqX3K/z2txjAU15DgX4y90DS2YLfIJFolCOkJJJwFcwAgAAAAAMnR5V7gfX7MNqqUdL5AkWlkhyFXaBRVNej+Rcn8lrQkBWwAIAAAAAA2cDNRXZuiC241TGRvdFyctJnrNcdbZOP9zHio81tkngADMTMyAH0AAAAFZAAgAAAAAAeGrIMK/bac6kPczxbvRYqKMkcpeI2FjdMpD91FDWIvBXMAIAAAAAAix62z1LeS8yvSXCl5gHSIomjyx76fF3S1lp9k900hygVsACAAAAAAiYwzf2m71aWFD5ajcXyW2JX2EzQOkBroTGMg29nLPYIAAzEzMwB9AAAABWQAIAAAAACphf298InM0Us4HT8o1W1MGw0D/02vd7Jh+U0h7qaFaQVzACAAAAAAFXtk7YpqsOJxsqGWSIL+YcBE96G3Zz9D31gPqDW94y8FbAAgAAAAAAOrS1KVA94rjB1jZ1pPocpCeBG+B14RzWoHqVDpp7JbAAMxMzQAfQAAAAVkACAAAAAATLDS2cuDVM3yDMuWNgk2iGKBTzPpfJMbvxVOSY39ZfcFcwAgAAAAAPT5wRi2cLHIUflXzm6EQB/m7xdThP80ir1VV/JBBqvxBWwAIAAAAAB9lEtZS0aXCFbCtSbhnis27S5IPcfWGygHW8AHn3QqzwADMTM1AH0AAAAFZAAgAAAAAJNjExiZVX7jfFGfYpQu16qxLN0YPqVU/5CQ/Y67YSinBXMAIAAAAABMpm2+6KrkRUlXzQoMPHrQmIO6dkQz66tYdfTeA3dKqQVsACAAAAAAFXobHiMLvNZuEPr8jtewCX2J93EZG3JNeyVg92fue6YAAzEzNgB9AAAABWQAIAAAAABlFkYtLCx901X6QVVMkSn6Z7k30UF4xHaA0OZJJ9bdyQVzACAAAAAATez+F9GHcGzTp7jjv4feboUNb8JCkIp4EqcPFisnq7MFbAAgAAAAACE7JvOpBgMoZ7kRd4QbxIhxukPTUxXpzhjnBHiR7XoRAAMxMzcAfQAAAAVkACAAAAAA8NJKN0IxZnruhswGQkiruv8Ih0EMwDcSZx/Xasup9dkFcwAgAAAAAKaJZRxzA+Igeydvuk6cSwUHXcrmT4PjhuPu//FslpdnBWwAIAAAAAD53Rok1Vq/PMAnXmarqoHJ0PEyYUBmVESa9hIpCv/G9QADMTM4AH0AAAAFZAAgAAAAABHxHdEClz7hbSSgE58+dWLlSMJnoPz+jFxp4bB1GmLQBXMAIAAAAAD3nSvT6aGD+A110J/NwEfp0nPutlmuB5B+wA3CC3noGAVsACAAAAAA3Apjd+TapONB7k5wBVwTWgn8t+Sq2oyyU5/+as109RcAAzEzOQB9AAAABWQAIAAAAAC/o8qW/ifk3KuJ01VFkyNLgQafxB5/bGs2G5VyyVafOwVzACAAAAAA1bMqAFGDHSl6BYNLbxApvkAv2K1/oafywiX0MDz1dGUFbAAgAAAAAHJXLlId3edFoniLD/9K2A5973MeP2Ro31flDyqm3l5QAAMxNDAAfQAAAAVkACAAAAAAY2V8I1bz3a1AxTtmED6UhdhA09huFkuuEX8R+d/WDPUFcwAgAAAAAPTVoNRiI76tcRKqd+JBBVyy4+YcKST42p0QX2BtmQ2VBWwAIAAAAACcxt9hg14WqPNiDv1MkqVljM2e2KJEv53lA17LhV6ZigADMTQxAH0AAAAFZAAgAAAAAO2kSsW0WGN9AOtK4xK2SHrGhWiaAbMEKT4iZkRpaDN/BXMAIAAAAABKGzQcPM8LT2dwOggxoWjv/1imYWabbG/G4kBw8OWaxAVsACAAAAAAC9hLK1dScQTAqg+YAG3ObdPzg2Xet57HmOFpGmyUR9UAAzE0MgB9AAAABWQAIAAAAAAiCwzNEEaH/mDam68IdDftnhthyUFdb+ZCNSBQ91WlHQVzACAAAAAA7tHyHcxCzmbJeFYZyPm4mEgkTGKOvwY4MX82OvH0Jn8FbAAgAAAAAAb5IAbZ1hXCNegQ+S+C9i/Z8y6sS8KeU04V6hXa2ml6AAMxNDMAfQAAAAVkACAAAAAAGuCHVNJSuoVkpPOnS5s89GuA+BLi2IPBUr2Bg1sWEPIFcwAgAAAAAEl1gncS5/xO7bQ/KQSstRV3rOT2SW6nV92ZANeG2SR6BWwAIAAAAAA9LOcKmhek8F2wAh8yvT/vjp2gaouuO+Hmv10lwAeWPAADMTQ0AH0AAAAFZAAgAAAAAMfxz7gEaoCdPvXrubDhCZUS0ARLZc1svgbXgMDlVBPgBXMAIAAAAAB6a5dDA3fuT5Vz2KvAcbUEFX/+B7Nw2p1QqbPoQ5TTuAVsACAAAAAAcf/y75UOuI62A6vWH7bYr/5Jz+nirZVYK/81trN6XOQAAzE0NQB9AAAABWQAIAAAAACnYsqF/VzmjIImC9+dqrHO1TM6lJ6fRwM0mM6Wf6paOwVzACAAAAAA5tgZzch8uDCR1ky3SllVaKVpxAlbrhvlNDTazZZRZOAFbAAgAAAAALeGiLJS4z2zhgVpxzyPdRYyACP9QzQBOob34YrIZumCAAMxNDYAfQAAAAVkACAAAAAAEC0sIVmadtW4YMuRXH7RpAhXclsd+3bmqGXCMeaT014FcwAgAAAAABPpXh0uzpsJJB+IRUNajmMB9WGwswfpw5T9xk3Xj6ANBWwAIAAAAAAmf+NYh9TZ/QRu3w/GQz66n7DtfbJijN3G7KzeL8lstAADMTQ3AH0AAAAFZAAgAAAAABaIB3n49Xm9cOafSrQsE0WCcYp8rMIO/qVwIlMF5YLRBXMAIAAAAAC9EyWJV3xOu9bzgdJ/yX+ko7qLf1u3AxNMataW2C9EzQVsACAAAAAAvVbDkLxXx2DcMLifIQ3K0IIJcLcAG9DUrNfI6aoUjNcAAzE0OAB9AAAABWQAIAAAAAA5rZItA/cocRnngYqcJ3nBXQ+l688aKz3EQyLbYYunPAVzACAAAAAAwKyA+L7TgxztPClLrIMk2JXR+w7c04N3ZOqPgjvrIvsFbAAgAAAAACzvZ33h6aWEe8hmo+1f6OXJ72FY5hvWaUuha64ZV3KFAAMxNDkAfQAAAAVkACAAAAAA3htn7oHJ0YYpIrs+Mzyh85Ys67HwAdv5LQl1mCdoMWkFcwAgAAAAAEHjCtNNLenHuSIYux6ezAHsXDaj2DlTF67ToDhDDe6HBWwAIAAAAAD+P4H0sk9jOd+7vOANt2/1Ectb+4ZRGPE8GkHWNXW3MgADMTUwAH0AAAAFZAAgAAAAAEnt18Km/nqggfIJWxzTr9r3hnXNaueG6XO9A5G11LnGBXMAIAAAAAD7QxzGMN/ard5TfFLecE6uusMmXG2+RBsBR+/NCQHUwAVsACAAAAAAQEZ1ZZ8GC8rdbg7s87OM5Gr9qkTXS9+P5DuAZxj5Gl4AAzE1MQB9AAAABWQAIAAAAAAVAKK/GoY8AACu/hyMpO4hdLq6JnEyWNzkyci9sbaD/wVzACAAAAAA2HmeqpMlvvBpV2zQTYIRmsc4MFlfHRwLof0ycJgMg/MFbAAgAAAAACdltCeWi5E/q1Li1eXLChpM2D9QQSGLBZ82NklQSc0oAAMxNTIAfQAAAAVkACAAAAAAhHyq1GQC/GiMwpYjcsfkNxolJ10ARKjIjfkW1Wipzi0FcwAgAAAAAD/uaGWxTDq87F8XZ6CrFI+RNa8yMqfSZdqK00Kj833BBWwAIAAAAAD6aEdOO0CsQGagioOCvANPCEHSpJ8BSixlPBq5ERhB7AADMTUzAH0AAAAFZAAgAAAAABAJJxHoZD+MQBWqm9UM9Dd3z5ZohIZGWRaRVRsMptKQBXMAIAAAAADrE/ca+gqj/SH4oao4wE4qn2ovoTydzcMbDbrfnUs3zAVsACAAAAAAeNCIQN6hVnGJinytQRFGlQ2ocoprXNqpia+BSxzl+uwAAzE1NAB9AAAABWQAIAAAAAAv01wz7VG9mTepjXQi6Zma+7b/OVBaKVkWNbgDLr1mFgVzACAAAAAA0I5sxz8r6wkCp5Tgvr+iL4p6MxSOq5d3e1kZG+0b7NkFbAAgAAAAAIA32v6oGkAOS96HexGouNTex+tLahtx9QF2dgGClk6WAAMxNTUAfQAAAAVkACAAAAAAWXecRwxSon68xaa9THXnRDw5ZfzARKnvvjTjtbae6T0FcwAgAAAAAPh0UfUMEo7eILCMv2tiJQe1bF9qtXq7GJtC6H5Va4fIBWwAIAAAAADqFr1ThRrTXNgIOrJWScO9mk86Ufi95IDu5gi4vP+HWQADMTU2AH0AAAAFZAAgAAAAAEY5WL8/LpX36iAB1wlQrMO/xHVjoO9BePVzbUlBYo+bBXMAIAAAAABoKcpadDXUARedDvTmzUzWPe1jTuvD0z9oIcZmKuiSXwVsACAAAAAAJuJbwuaMrAFoI+jU/IYr+k4RzAqITrOjAd3HWCpJHqEAAzE1NwB9AAAABWQAIAAAAADnJnWqsfx0xqNnqfFGCxIplVu8mXjaHTViJT9+y2RuTgVzACAAAAAAWAaSCwIXDwdYxWf2NZTly/iKVfG/KDjHUcA1BokN5sMFbAAgAAAAAJVxavipE0H4/JQvhagdytXBZ8qGooeXpkbPQ1RfYMVHAAMxNTgAfQAAAAVkACAAAAAAsPG7LaIpJvcwqcbtfFUpIjj+vpNj70Zjaw3eV9T+QYsFcwAgAAAAAJQ71zi0NlCyY8ZQs3IasJ4gB1PmWx57HpnlCf3+hmhqBWwAIAAAAACD58TO6d+71GaOoS+r73rAxliAO9GMs4Uc8JbOTmC0OwADMTU5AH0AAAAFZAAgAAAAAAGiSqKaQDakMi1W87rFAhkogfRAevnwQ41onWNUJKtuBXMAIAAAAAASgiDpXfGh7E47KkOD8MAcX8+BnDShlnU5JAGdnPdqOAVsACAAAAAAI+2TTQIgbFq4Yr3lkzGwhG/tqChP7hRAx2W0fNaH6jcAAzE2MAB9AAAABWQAIAAAAAB7L4EnhjKA5xJD3ORhH2wOA1BvpnQ+7IjRYi+jjVEaJAVzACAAAAAAuhBIm0nL3FJnVJId+7CKDASEo+l2E89Z9/5aWSITK4AFbAAgAAAAALtSICOzQDfV9d+gZuYxpEj6cCeHnKTT+2G3ceP2H65kAAMxNjEAfQAAAAVkACAAAAAAaROn1NaDZFOGEWw724dsXBAm6bgmL5i0cki6QZQNrOoFcwAgAAAAANVT8R6UvhrAlyqYlxtmnvkR4uYK/hlvyQmBu/LP6/3ZBWwAIAAAAAD+aHNMP/X+jcRHyUtrCNkk1KfMtoD3GTmShS8pWGLt+AADMTYyAH0AAAAFZAAgAAAAADqSR5e0/Th59LrauDA7OnGD1Xr3H3NokfVxzDWOFaN7BXMAIAAAAACt30faNwTWRbvmykDpiDYUOCwA6QDbBBYBFWS7rdOB4AVsACAAAAAAF7SvnjjRk5v2flFOKaBAEDvjXaL1cpjsQLtK2fv9zdQAAzE2MwB9AAAABWQAIAAAAADmtb1ZgpZjSeodPG/hIVlsnS8hoRRwRbrTVx89VwL62AVzACAAAAAAi38e1g6sEyVfSDkzZbaZXGxKI/zKNbMasOl2LYoWrq8FbAAgAAAAAALACk0KcCDN/Kv8WuazY8ORtUGkOZ5Dsm0ys1oOppp/AAMxNjQAfQAAAAVkACAAAAAAf/f7AWVgBxoKjr7YsEQ4w/fqSvuQWV2HMiA3rQ7ur0sFcwAgAAAAADkkeJozP6FFhUdRIN74H4UhIHue+eVbOs1NvbdWYFQrBWwAIAAAAAB55FlHAkmTzAYj/TWrGkRJw2EhrVWUnZXDoMYjyfB/ZwADMTY1AH0AAAAFZAAgAAAAAI2WEOymtuFpdKi4ctanPLnlQud+yMKKb8p/nfKmIy56BXMAIAAAAADVKrJmhjr1rfF3p+T+tl7UFd1B7+BfJRk0e7a4im7ozgVsACAAAAAA5E7Ti3PnFiBQoCcb/DN7V1uM3Xd6VKiexPKntssFL7kAAzE2NgB9AAAABWQAIAAAAAAuHU9Qd79hjyvKOujGanSGDIQlxzsql8JytTZhEnPw+AVzACAAAAAAjF2gV/4+sOHVgDd/oR5wDi9zL7NGpGD+NsEpGXy/a4QFbAAgAAAAAJzMoyojYV6Ed/LpVN5zge93Odv3U7JgP7wxeRaJZGTdAAMxNjcAfQAAAAVkACAAAAAA7dQDkt3iyWYCT94d7yqUtPPwp4qkC0ddu+HFdHgVKEkFcwAgAAAAANuYvtvZBTEq4Rm9+5eb7VuFopowkrAuv86PGP8Q8/QvBWwAIAAAAACeqXoAOQOE4j0zRMlkVd8plaW0RX1npsFvB38Xmzv7sAADMTY4AH0AAAAFZAAgAAAAAAwnZSDhL4tNGYxlHPhKYB8s28dY5ScSwiKZm3UhT8U3BXMAIAAAAABDoY6dhivufTURQExyC9Gx3ocpl09bgbbQLChj3qVGbgVsACAAAAAAF+1nS7O0v85s3CCy+9HkdeoEfm2C6ZiNbPMMnSfsMHUAAzE2OQB9AAAABWQAIAAAAAC2VuRdaC4ZJmLdNOvD6R2tnvkyARteqXouJmI46V306QVzACAAAAAAMn1Z6B35wFTX9mEYAPM+IiJ5hauEwfD0CyIvBrxHg7IFbAAgAAAAAOG6DvDZkT9B/xZWmjao2AevN7MMbs3Oh9YJeSd/hZ+hAAMxNzAAfQAAAAVkACAAAAAAVerb7qVNy457rNOHOgDSKyWl5ojun7iWrv1uHPXrIZQFcwAgAAAAAIDcYS9j5z+gx0xdJj09L7876r/vjvKTi/d3bXDE3PhyBWwAIAAAAADuhVLqb1Bkrx8aNymS+bx2cL8GvLFNH4SAi690DUgnWQADMTcxAH0AAAAFZAAgAAAAAH/E44yLxKCJjuSmU9A8SEhbmkDOx1PqqtYcZtgOzJdrBXMAIAAAAABgLh9v2HjBbogrRoQ82LS6KjZQnzjxyJH4PH+F3jupSAVsACAAAAAAIlO46ehXp4TqpDV0t6op++KO+uWBFh8iFORZjmx2IjkAAzE3MgB9AAAABWQAIAAAAAAlNUdDL+f/SSQ5074mrq0JNh7CTXwTbbhsQyDwWeDVMwVzACAAAAAANIH2IlSNG0kUw4qz0budjcWn8mNR9cJlYUqPYdonucAFbAAgAAAAAJMrOUOyiu5Y3sV76zwEFct8L7+i8WGlQI2+8z2W2kzaAAMxNzMAfQAAAAVkACAAAAAASZ+CvUDtlk/R4HAQ3a+PHrKeY/8ifAfh0oXYFqliu80FcwAgAAAAAJelpzPgM65OZFt/mvGGpwibclQ49wH+1gbUGzd9OindBWwAIAAAAAD9qeDchteEpVXWcycmD9kl9449C1dOw0r60TBm5jK+cQADMTc0AH0AAAAFZAAgAAAAAN9fkoUVbvFV2vMNMAkak4gYfEnzwKI3eDM3pnDK5q3lBXMAIAAAAACnDkgVNVNUlbQ9RhR6Aot2nVy+U4km6+GHPkLr631jEAVsACAAAAAANzg/BnkvkmvOr8nS4omF+q9EG/4oisB+ul4YHi938hwAAzE3NQB9AAAABWQAIAAAAAASyK3b1nmNCMptVEGOjwoxYLLS9fYWm/Zxilqea0jpEQVzACAAAAAADDHsGrbqlKGEpxlvfyqOJKQJjwJrzsrB7k3HG0AUJbkFbAAgAAAAAKwx3S4XfDZh4+LuI9jf7XgUh5qiefNv87JD4qvVRfPSAAMxNzYAfQAAAAVkACAAAAAAlSP9iK31GlcG9MKGbLmq+VXMslURr+As736rrVNXcsUFcwAgAAAAAAvbj0zfq9zzi8XReheKFbCB+h9IsOLgXPPpI5vrEJNZBWwAIAAAAABXvoZhaQE7ogWjeBjceVkp03N20cKYP3TA8vuNsgpfAgADMTc3AH0AAAAFZAAgAAAAAOJNORH8Bev97gVU7y6bznOxJ+E6Qoykur1QP76hG1/7BXMAIAAAAAC+C1PtOOrSZgzBAGhr+dPe/kR0JUw9GTwLVNr61xC1aAVsACAAAAAAeA/L8MQIXkamaObtMPLpoDoi5FypA5WAPtMeMrgi0eQAAzE3OAB9AAAABWQAIAAAAAAKcHzLUomavInN6upPkyWhAqYQACP/vdVCIYpiy6U6HgVzACAAAAAATsR4KItY6R2+U7Gg6sJdaEcf58gjd1OulyWovIqfxKcFbAAgAAAAAFbm10ko67ahboAejQdAV0U2uA5OhZYdb8XUFJ8OL46LAAMxNzkAfQAAAAVkACAAAAAAqTOLiMpCdR59tLZzzIPqJvbCNvz2XQL9ust0qYaehtcFcwAgAAAAAArefox/3k5xGOeiw2m6NUdzuGxmPwcu5IFcj+jMwHgHBWwAIAAAAADLZGFJ7MQd5JXMgMXjqZO5LDLxcFClcXPlnRMWRn+1oAADMTgwAH0AAAAFZAAgAAAAAIPSqSeVzSRgNVNmrPYHmUMgykCY27NbdDUNhE5kx/SgBXMAIAAAAAAhX90nNfxyXmZe/+btZ7q6xMX4PFyj0paM1ccJ/5IUUQVsACAAAAAA419oHmD2W0SYoOMwhrhrp8jf68fg9hTkaRdCuVd3CN0AAzE4MQB9AAAABWQAIAAAAACLn5DxiqAosHGXIAY96FwFKjeqrzXWf3VJIQMwx1fl4gVzACAAAAAAindvU27nveutopdvuHmzdENBbeGFtI3Qcsr07jxmvm8FbAAgAAAAAPvl9pBStQvP4OGkN5v0MghUY6djm9n7XdKKfrW0l1sMAAMxODIAfQAAAAVkACAAAAAA7i2S6rHRSPBwZEn59yxaS7HiYBOmObIkeyCcFU42kf8FcwAgAAAAAGb3RSEyBmgarkTvyLWtOLJcPwCKbCRkESG4RZjVmY4iBWwAIAAAAADB2/wo5CSHR4ANtifY6ZRXNTO5+O8qP82DfAiAeanpZwADMTgzAH0AAAAFZAAgAAAAAFz+M+H/Z94mdPW5oP51B4HWptp1rxcMWAjnlHvWJDWrBXMAIAAAAACBFEOQyL7ZHu4Cq33QvXkmKuH5ibG/Md3RaED9CtG5HwVsACAAAAAAfggtJTprQ/yZzj7y5z9KvXsdeXMWP0yUXMMJqpOwI88AAzE4NAB9AAAABWQAIAAAAAAE7c2x3Z3aM1XGfLNk/XQ9jCazNRbGhVm7H8c2NjS5ywVzACAAAAAARJ9h8fdcwA19velF3L/Wcvi2rCzewlKZ2nA0p8bT9uwFbAAgAAAAAJtWe6b4wK2Hae2dZm/OEpYQnvoZjz4Sz5IgJC2wInecAAMxODUAfQAAAAVkACAAAAAAVoRt9B9dNVvIMGN+ea5TzRzQC+lqSZ8dd/170zU5o9cFcwAgAAAAAEwM95XZin5mv2yhCI8+ugtKuvRVmNgzzIQN0yi1+9aIBWwAIAAAAAAMGBq72n00rox3uqhxSB98mkenTGCdbbUF1gXrgottzgADMTg2AH0AAAAFZAAgAAAAAKRDkjyWv/etlYT4GyoXrmBED2FgZHnhc+l9Wsl06cH2BXMAIAAAAABohlpm3K850Vndf3NmNE0hHqDlNbSR8/IvMidQ3LnIZAVsACAAAAAAW42nGHa6q2MCAaaPVwaIDfr8QLyQwjKq23onZJYsqVsAAzE4NwB9AAAABWQAIAAAAAC3DFh5oklLCNLY90bgWm68dFXz65JpAZSp1K99MBTPAQVzACAAAAAAQgZecmxEUZVHoptEQClDwAf8smI3WynQ/i+JBP0g+kQFbAAgAAAAAEUSQGVnAPISD6voD0DiBUqyWKgt2rta0tjmoe+LNt6IAAMxODgAfQAAAAVkACAAAAAAQ5WKvWSB503qeNlOI2Tpjd5blheNr6OBO8pfJfPNstcFcwAgAAAAAKwHgQLSDJ5NwLBQbY5OnblQIsVDpGV7q3RCbFLD1U4/BWwAIAAAAACQ5nED99LnpbqXZuUOUjnO2HTphEAFBjLD4OZeDEYybgADMTg5AH0AAAAFZAAgAAAAAGfhFY3RGRm5ZgWRQef1tXxHBq5Y6fXaLAR4yJhrTBplBXMAIAAAAACKEF0ApLoB6lP2UqTFsTQYNc9OdDrs/vziPGzttGVLKQVsACAAAAAArOO6FyfNRyBi0sPT5iye7M8d16MTLcwRfodZq4uCYKEAAzE5MAB9AAAABWQAIAAAAAAIM73gPcgzgotYHLeMa2zAU4mFsr7CbILUZWfnuKSwagVzACAAAAAAJCSu98uV8xv88f2BIOWzt6p+6EjQStMBdkGPUkgN79cFbAAgAAAAAMGqPGMPxXbmYbVfSa/japvUljht1zZT33TY7ZjAiuPfAAMxOTEAfQAAAAVkACAAAAAAkWmHCUsiMy1pwZTHxVPBzPTrWFBUDqHNrVqcyyt7nO8FcwAgAAAAAMv2CebFRG/br7USELR98sIdgE9OQCRBGV5JZCO+uPMgBWwAIAAAAABt7qSmn3gxJu7aswsbUiwvO+G6lXj/Xhx+J/zQyZxzLAADMTkyAH0AAAAFZAAgAAAAAGInUYv0lP/rK7McM8taEHXRefk8Q2AunrvWqdfSV7UaBXMAIAAAAACE+WPxJ3gan7iRTbIxXXx+bKVcaf8kP4JD8DcwU0aL7wVsACAAAAAAUC4eTprX4DUZn2X+UXYU6QjtiXk+u57yoOPBbPQUmDkAAzE5MwB9AAAABWQAIAAAAACmHlg2ud3cplXlTsNTpvNnY6Qm1Fce0m899COamoDjaQVzACAAAAAArtJQeJIlepBWRU2aYar7+YGYVQ7dfDc1oxgTmA8r9q0FbAAgAAAAAOk45vg5VqZHAFCO3i0Z52SZi5RADf8NXwf68T5yad/DAAMxOTQAfQAAAAVkACAAAAAApzcWSAbZWV/Rq+ylRNqqlJqNVR4fhXrz4633/MQOQgcFcwAgAAAAAN/jz/bsEleiuCl+li83EWlG6UMHA8CyaOMRKCkXkSCPBWwAIAAAAAC3Sd+Qg+uFDKpGZHbrQgokXHQ1az1aFl4YK343OB6hcQAAEmNtAAAAAAAAAAAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAEAAAASc3AAAQAAAAAAAAAQdGYAAQAAABNtbgD/////Y46NN8CHrb4J7f/fE214AP////9jjo03wIetvgnt/18A", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.json new file mode 100644 index 0000000000..8ade3593e6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.json @@ -0,0 +1,1975 @@ +{ + "description": "fle2v2-Rangev2-Decimal-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalNoPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalNoPrecision": { + "$numberDecimal": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalNoPrecision": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.yml new file mode 100644 index 0000000000..fc63960c5c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Decimal-Update.yml @@ -0,0 +1,462 @@ +description: fle2v2-Rangev2-Decimal-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + # Tests for Decimal (without precision) must only run against a replica set. Decimal (without precision) queries are expected to take a long time and may exceed the default mongos timeout. + topologies: ["replicaset"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalNoPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalNoPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDecimalNoPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedDecimalNoPrecision: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimalNoPrecision": { $numberDecimal: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedDecimalNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "DR1jAAADcGF5bG9hZACxYgAABGcAnWIAAAMwAH0AAAAFZAAgAAAAAJu2KgiI8vM+kz9qD3ZQzFQY5qbgYqCqHG5R4jAlnlwXBXMAIAAAAAAAUXxFXsz764T79sGCdhxvNd5b6E/9p61FonsHyEIhogVsACAAAAAAt19RL3Oo5ni5L8kcvgOJYLgVYyXJExwP8pkuzLG7f/kAAzEAfQAAAAVkACAAAAAAPQPvL0ARjujSv2Rkm8r7spVsgeC1K3FWcskGGZ3OdDIFcwAgAAAAACgNn660GmefR8jLqzgR1u5O+Uocx9GyEHiBqVGko5FZBWwAIAAAAADflr+fsnZngm6KRWYgHa9JzK+bXogWl9evBU9sQUHPHQADMgB9AAAABWQAIAAAAAD2Zi6kcxmaD2mY3VWrP+wYJMPg6cSBIYPapxaFQxYFdQVzACAAAAAAM/cV36BLBY3xFBXsXJY8M9EHHOc/qrmdc2CJmj3M89gFbAAgAAAAAOpydOrKxx6m2gquSDV2Vv3w10GocmNCFeOo/fRhRH9JAAMzAH0AAAAFZAAgAAAAAOaNqI9srQ/mI9gwbk+VkizGBBH/PPWOVusgnfPk3tY1BXMAIAAAAAAc96O/pwKCmHCagT6T/QV/wz4vqO+R22GsZ1dse2Vg6QVsACAAAAAAgzIak+Q3UFLTHXPmJ+MuEklFtR3eLtvM+jdKkmGCV/YAAzQAfQAAAAVkACAAAAAA0XlQgy/Yu97EQOjronl9b3dcR1DFn3deuVhtTLbJZHkFcwAgAAAAACoMnpVl6EFJak8A+t5N4RFnQhkQEBnNAx8wDqmq5U/dBWwAIAAAAACR26FJif673qpwF1J1FEkQGJ1Ywcr/ZW6JQ7meGqzt1QADNQB9AAAABWQAIAAAAAAOtpNexRxfv0yRFvZO9DhlkpU4mDuAb8ykdLnE5Vf1VAVzACAAAAAAeblFKm/30orP16uQpZslvsoS8s0xfNPIBlw3VkHeekYFbAAgAAAAAPEoHj87sYE+nBut52/LPvleWQBzB/uaJFnosxp4NRO2AAM2AH0AAAAFZAAgAAAAAIr8xAFm1zPmrvW4Vy5Ct0W8FxMmyPmFzdWVzesBhAJFBXMAIAAAAABYeeXjJEzTHwxab6pUiCRiZjxgtN59a1y8Szy3hfkg+gVsACAAAAAAJuoY4rF8mbI+nKb+5XbZShJ8191o/e8ZCRHE0O4Ey8MAAzcAfQAAAAVkACAAAAAAl+ibLk0/+EwoqeC8S8cGgAtjtpQWGEZDsybMPnrrkwEFcwAgAAAAAHPPBudWgQ+HUorLDpJMqhS9VBF2VF5aLcxgrM1s+yU7BWwAIAAAAAAcCcBR2Vyv5pAFbaOU97yovuOi1+ATDnLLcAUqHecXcAADOAB9AAAABWQAIAAAAACR9erwLTb+tcWFZgJ2MEfM0PKI9uuwIjDTHADRFgD+SQVzACAAAAAAcOop8TXsGUVQoKhzUllMYWxL93xCOkwtIpV8Q6hiSYYFbAAgAAAAAKXKmh4V8veYwob1H03Q3p3PN8SRAaQwDT34KlNVUjiDAAM5AH0AAAAFZAAgAAAAALv0vCPgh7QpmM8Ug6ad5ioZJCh7pLMdT8FYyQioBQ6KBXMAIAAAAADsCPyIG8t6ApQkRk1fX/sfc1kpuWCWP8gAEpnYoBSHrQVsACAAAAAAJe/r67N6d8uTiogvfoR9rEXbIDjyLb9EVdqkayFFGaYAAzEwAH0AAAAFZAAgAAAAAIW4AxJgYoM0pcNTwk1RSbyjZGIqgKL1hcTJmNrnZmoPBXMAIAAAAAAZpfx3EFO0vY0f1eHnE0PazgqeNDTaj+pPJMUNW8lFrAVsACAAAAAAP+Um2vwW6Bj6vuz9DKz6+6aWkoKoEmFNoiz/xXm7lOsAAzExAH0AAAAFZAAgAAAAAKliO6L9zgeuufjj174hvmQGNRbmYYs9yAirL7OxwEW3BXMAIAAAAAAqU7vs3DWUQ95Eq8OejwWnD0GuXd+ASi/uD6S0l8MM1QVsACAAAAAAb9legYzsfctBPpHyl7YWpPmLr5QiNZFND/50N1vv2MUAAzEyAH0AAAAFZAAgAAAAAOGQcCBkk+j/Kzjt/Cs6g3BZPJG81wIHBS8JewHGpgk+BXMAIAAAAABjrxZXWCkdzrExwCgyHaafuPSQ4V4x2k9kUCAqUaYKDQVsACAAAAAADBU6KefT0v8zSmseaMNmQxKjJar72y7MojLFhkEHqrUAAzEzAH0AAAAFZAAgAAAAAPmCNEt4t97waOSd5hNi2fNCdWEkmcFJ37LI9k4Az4/5BXMAIAAAAABX7DuDPNg+duvELf3NbLWkPMFw2HGLgWGHyVWcPvSNCAVsACAAAAAAS7El1FtZ5STh8Q1FguvieyYX9b2DF1DFVsb9hzxXYRsAAzE0AH0AAAAFZAAgAAAAAD4vtVUYRNB+FD9yoQ2FVJH3nMeJeKbi6eZfth638YqbBXMAIAAAAAANCuUB4OdmuD6LaDK2f3vaqfgYYvg40wDXOBbcFjTqLwVsACAAAAAA9hqC2VoJBjwR7hcQ45xO8ZVojwC83jiRacCaDj6Px2gAAzE1AH0AAAAFZAAgAAAAAJPIRzjmTjbdIvshG6UslbEOd797ZSIdjGAhGWxVQvK1BXMAIAAAAABgmJ0Jh8WLs9IYs/a7DBjDWd8J3thW/AGJK7zDnMeYOAVsACAAAAAAi9zAsyAuou2oiCUHGc6QefLUkACa9IgeBhGu9W/r0X8AAzE2AH0AAAAFZAAgAAAAAABQyKQPoW8wGPIqnsTv69+DzIdRkohRhOhDmyVHkw9WBXMAIAAAAAAqWA2X4tB/h3O1Xlawtz6ndI6WaTwgU1QYflL35opu5gVsACAAAAAAWI/Gj5aZMwDIxztqmVL0g5LBcI8EdKEc2UA28pnekQoAAzE3AH0AAAAFZAAgAAAAACB7NOyGQ1Id3MYnxtBXqyZ5Ul/lHH6p1b10U63DfT6bBXMAIAAAAADpOryIcndxztkHSfLN3Kzq29sD8djS0PspDSqERMqokQVsACAAAAAADatsMW4ezgnyi1PiP7xk+gA4AFIN/fb5uJqfVkjg4UoAAzE4AH0AAAAFZAAgAAAAAKVfXLfs8XA14CRTB56oZwV+bFJN5BHraTXbqEXZDmTkBXMAIAAAAAASRWTsfGOpqdffiOodoqIgBzG/yzFyjR5CfUsIUIWGpgVsACAAAAAAkgCHbCwyX640/0Ni8+MoYxeHUiC+FSU4Mn9jTLYtgZgAAzE5AH0AAAAFZAAgAAAAAH/aZr4EuS0/noQR9rcF8vwoaxnxrwgOsSJ0ys8PkHhGBXMAIAAAAACd7ObGQW7qfddcvyxRTkPuvq/PHu7+6I5dxwS1Lzy5XAVsACAAAAAA3q0eKdV7KeU3pc+CtfypKR7BPxwaf30yu0j9FXeOOboAAzIwAH0AAAAFZAAgAAAAAKvlcpFFNq0oA+urq3w6d80PK1HHHw0H0yVWvU9aHijXBXMAIAAAAADWnAHQ5Fhlcjawki7kWzdqjM2f6IdGJblojrYElWjsZgVsACAAAAAAO0wvY66l24gx8nRxyVGC0QcTztIi81Kx3ndRhuZr6W4AAzIxAH0AAAAFZAAgAAAAAH/2aMezEOddrq+dNOkDrdqf13h2ttOnexZsJxG1G6PNBXMAIAAAAABNtgnibjC4VKy5poYjvdsBBnVvDTF/4mmEAxsXVgZVKgVsACAAAAAAqvadzJFLqQbs8WxgZ2D2X+XnaPSDMLCVVgWxx5jnLcYAAzIyAH0AAAAFZAAgAAAAAF2wZoDL6/V59QqO8vdRZWDpXpkV4h4KOCSn5e7x7nmzBXMAIAAAAADLZBu7LCYjbThaVUqMK14H/elrVOYIKJQCx4C9Yjw37gVsACAAAAAAEh6Vs81jLU204aGpL90fmYTm5i5R8/RT1uIbg6VU3HwAAzIzAH0AAAAFZAAgAAAAAH27yYaLn9zh2CpvaoomUPercSfJRUmBY6XFqmhcXi9QBXMAIAAAAAAUwumVlIYIs9JhDhSj0R0+59psCMsFk94E62VxkPt42QVsACAAAAAAT5x2hCCd2bpmpnyWaxas8nSxTc8e4C9DfKaqr0ABEysAAzI0AH0AAAAFZAAgAAAAALMg2kNAO4AFFs/mW3In04yFeN4AP6Vo0klyUoT06RquBXMAIAAAAAAgGWJbeIdwlpqXCyVIYSs0dt54Rfc8JF4b8uYc+YUj0AVsACAAAAAAWHeWxIkyvXTOWvfZzqtPXjfGaWWKjGSIQENTU3zBCrsAAzI1AH0AAAAFZAAgAAAAALas/i1T2DFCEmrrLEi7O2ngJZyFHialOoedVXS+OjenBXMAIAAAAAA1kK0QxY4REcGxHeMkgumyF7iwlsRFtw9MlbSSoQY7uAVsACAAAAAAUNlpMJZs1p4HfsD4Q4WZ4TBEi6Oc2fX34rzyynqWCdwAAzI2AH0AAAAFZAAgAAAAAP1TejmWg1CEuNSMt6NUgeQ5lT+oBoeyF7d2l5xQrbXWBXMAIAAAAABPX0kj6obggdJShmqtVfueKHplH4ZrXusiwrRDHMOKeQVsACAAAAAAIYOsNwC3DA7fLcOzqdr0bOFdHCfmK8tLwPoaE9uKOosAAzI3AH0AAAAFZAAgAAAAAMrKn+QPa/NxYezNhlOX9nyEkN1kE/gW7EuZkVqYl0b8BXMAIAAAAABUoZMSPUywRGfX2EEencJEKH5x/P9ySUVrhStAwgR/LgVsACAAAAAAMgZFH6lQIIDrgHnFeslv3ld20ynwQjQJt3cAp4GgrFkAAzI4AH0AAAAFZAAgAAAAAMmD1+a+oVbiUZd1HuZqdgtdVsVKwuWAn3/M1B6QGBM3BXMAIAAAAACLyytOYuZ9WEsIrrtJbXUx4QgipbaAbmlJvSZVkGi0CAVsACAAAAAA4v1lSp5H9BB+HYJ4bH43tC8aeuPZMf78Ng1JOhJh190AAzI5AH0AAAAFZAAgAAAAAOVKV7IuFwmYP1qVv8h0NvJmfPICu8yQhzjG7oJdTLDoBXMAIAAAAABL70XLfQLKRsw1deJ2MUvxSWKxpF/Ez73jqtbLvqbuogVsACAAAAAAvfgzIorXxE91dDt4nQxYfntTsx0M8Gzdsao5naQqcRUAAzMwAH0AAAAFZAAgAAAAAKS/1RSAQma+xV9rz04IcdzmavtrBDjOKPM+Z2NEyYfPBXMAIAAAAAAOJDWGORDgfRv8+w5nunh41wXb2hCA0MRzwnLnQtIqPgVsACAAAAAAf42C1+T7xdHEFF83+c2mF5S8PuuL22ogXXELnRAZ4boAAzMxAH0AAAAFZAAgAAAAAFeq8o82uNY1X8cH6OhdTzHNBUnCChsEDs5tm0kPBz3qBXMAIAAAAABaxMBbsaeEj/EDtr8nZfrhhhirBRPJwVamDo5WwbgvTQVsACAAAAAAMbH453A+BYAaDOTo5kdhV1VdND1avNwvshEG/4MIJjQAAzMyAH0AAAAFZAAgAAAAAI8IKIfDrohHh2cjspJHCovqroSr5N3QyVtNzFvT5+FzBXMAIAAAAABXHXteKG0DoOMmECKp6ro1MZNQvXGzqTDdZ0DUc8QfFAVsACAAAAAA/w5s++XYmO+9TWTbtGc3n3ndV4T9JUribIbF4jmDLSMAAzMzAH0AAAAFZAAgAAAAAJkHvm15kIu1OtAiaByj5ieWqzxiu/epK6c/9+KYIrB0BXMAIAAAAACzg5TcyANk0nes/wCJudd1BwlkWWF6zw3nGclq5v3SJQVsACAAAAAAvruXHTT3irPJLyWpI1j/Xwf2FeIE/IV+6Z49pqRzISoAAzM0AH0AAAAFZAAgAAAAAAYSOvEWWuSg1Aym7EssNLR+xsY7e9BcwsX4JKlnSHJcBXMAIAAAAABT48eY3PXVDOjw7JpNjOe1j2JyI3LjDnQoqZ8Je5B2KgVsACAAAAAAU2815RR57TQ9uDg0XjWjBkAKvf8yssxDMzrM4+FqP6AAAzM1AH0AAAAFZAAgAAAAAGQxC9L1e9DfO5XZvX1yvc3hTLtQEdKO9FPMkyg0Y9ZABXMAIAAAAADtmcMNJwdWLxQEArMGZQyzpnu+Z5yMmPAkvgq4eAKwNQVsACAAAAAAJ88zt4Y/Hoqh+zrf6KCOiUwHbOzCxSfp6k/qsZaYGEgAAzM2AH0AAAAFZAAgAAAAADLHK2LNCNRO0pv8n4fAsxwtUqCNnVK8rRgNiQfXpHSdBXMAIAAAAACf16EBIHRKD3SzjRW+LMOl+47QXA3CJhMzlcqyFRW22AVsACAAAAAAMGz4fAOa0EoVv90fUffwLjBrQhHATf+NdlgCR65vujAAAzM3AH0AAAAFZAAgAAAAAHiZJiXKNF8bbukQGsdYkEi95I+FSBHy1I5/hK2uEZruBXMAIAAAAADE+lZBa8HDUJPN+bF6xI9x4N7GF9pj3vBR7y0BcfFhBAVsACAAAAAAGIEN6sfqq30nyxW4dxDgXr/jz5HmvA9T1jx/pKCn4zgAAzM4AH0AAAAFZAAgAAAAAI1oa2OIw5TvhT14tYCGmhanUoYcCZtNbrVbeoMldHNZBXMAIAAAAAAx2nS0Ipblf2XOgBiUOuJFBupBhe7nb6QPLZlA4aMPCgVsACAAAAAA9xu828hugIgo0E3de9dZD+gTpVUGlwtDba+tw/WcbUoAAzM5AH0AAAAFZAAgAAAAABgTWS3Yap7Q59hii/uPPimHWXsr+DUmsqfwt/X73qsOBXMAIAAAAACKK05liW5KrmEAvtpCB1WUltruzUylDDpjea//UlWoOAVsACAAAAAAcgN4P/wakJ5aJK5c1bvJBqpVGND221dli2YicPFfuAYAAzQwAH0AAAAFZAAgAAAAABOAnBPXDp6i9TISQXvcNKwGDLepZTu3cKrB4vKnSCjBBXMAIAAAAADjjzZO7UowAAvpwyG8BNOVqLCccMFk3aDK4unUeft5ywVsACAAAAAA4zkCd4k9gvfXoD1C7vwTjNcdVJwEARh8h/cxZ4PNMfgAAzQxAH0AAAAFZAAgAAAAAHN8hyvT1lYrAsdiV5GBdd5jhtrAYE/KnSjw2Ka9hjz9BXMAIAAAAAD794JK7EeXBs+D7yOVK7nWF8SbZ/7U8gZ7nnT9JFNwTAVsACAAAAAAg8Wt1HO3NhByq2ggux2a4Lo6Gryr24rEFIqh2acrwWMAAzQyAH0AAAAFZAAgAAAAAO93bPrq8bsnp1AtNd9ETnXIz0lH/2HYN/vuw9wA3fyFBXMAIAAAAABHlls5fbaF2oAGqptC481XQ4eYxInTC29aElfmVZgDUgVsACAAAAAANoQXEWpXJpgrSNK/cKi/m7oYhuSRlp1IZBF0bqTEATcAAzQzAH0AAAAFZAAgAAAAAL1YsAZm1SA0ztU6ySIrQgCCA74V6rr0/4iIygCcaJL6BXMAIAAAAADTXWTHWovGmUR1Zg9l/Aqq9H5mOCJQQrb/Dfae7e3wKAVsACAAAAAA5dunyJK6/SVfDD0t9QlNBcFqoZnf9legRjHaLSKAoQMAAzQ0AH0AAAAFZAAgAAAAAEoFAeHk0RZ9kD+cJRD3j7PcE5gzWKnyBrF1I/MDNp5mBXMAIAAAAACgHtc2hMBRSZjKw8RAdDHK+Pi1HeyjiBuAslGVNcW5tAVsACAAAAAAXzBLfq+GxRtX4Wa9fazA49DBLG6AjZm2XODStJKH8D0AAzQ1AH0AAAAFZAAgAAAAAAW+7DmSN/LX+/0uBVJDHIc2dhxAGz4+ehyyz8fAnNGoBXMAIAAAAAA6Ilw42EvvfLJ3Eq8Afd+FjPoPcQutZO6ltmCLEr8kxQVsACAAAAAAbbZalyo07BbFjPFlYmbmv0z023eT9eLkHqeVUnfUAUAAAzQ2AH0AAAAFZAAgAAAAANBdV7M7kuYO3EMoQItAbXv4t2cIhfaT9V6+s4cg9djlBXMAIAAAAABvz4MIvZWxxrcJCL5qxLfFhXiUYB1OLHdKEjco94SgDgVsACAAAAAAK2GVGvyPIKolF/ECcmfmkVcf1/IZNcaTv96N92yGrkEAAzQ3AH0AAAAFZAAgAAAAAMoAoiAn1kc79j5oPZtlMWHMhhgwNhLUnvqkqIFvcH1NBXMAIAAAAADcJTW7WiCyW0Z9YDUYwppXhLj4Ac1povpJvcAq+i48MQVsACAAAAAAIGxGDzoeB3PTmudl4+j6piQB++e33EEzuzAiXcqGxvUAAzQ4AH0AAAAFZAAgAAAAACI3j5QP7dWHpcT6WO/OhsWwRJNASBYqIBDNzW8IorEyBXMAIAAAAABxUpBSjXwCKDdGP9hYU+RvyR+96kChfvyyRC4jZmztqAVsACAAAAAAvBCHguWswb4X0xdcAryCvZgQuthXzt7597bJ5VxAMdgAAzQ5AH0AAAAFZAAgAAAAAKsbycEuQSeNrF8Qnxqw3x3og8JmQabwGqnDbqzFRVrrBXMAIAAAAACno/3ef2JZJS93SVVzmOZSN+jjJHT8s0XYq2M46d2sLAVsACAAAAAAAt5zLJG+/j4K8rnkFtAn8IvdUVNefe6utJ3rdzgwudIAAzUwAH0AAAAFZAAgAAAAAPXIcoO8TiULqlxzb74NFg+I8kWX5uXIDUPnh2DobIoMBXMAIAAAAADR6/drkdTpnr9g1XNvKDwtBRBdKn7c2c4ZNUVK5CThdQVsACAAAAAAJqOA1c6KVog3F4Hb/GfDb3jCxXDRTqpXWSbMH4ePIJsAAzUxAH0AAAAFZAAgAAAAAEa03ZOJmfHT6/nVadvIw71jVxEuIloyvxXraYEW7u7pBXMAIAAAAADzRlBJK75FLiKjz3djqcgjCLo/e3yntI3MnPS48OORhgVsACAAAAAAnQhx4Rnyj081XrLRLD5NLpWmRWCsd0M9Hl7Jl19R0h8AAzUyAH0AAAAFZAAgAAAAAKx8NLSZUU04pSSGmHa5fh2oLHsEN5mmNMNHL95/tuC9BXMAIAAAAAA59hcXVaN3MNdHoo11OcH1aPRzHCwpVjO9mGfMz4xh3QVsACAAAAAAYIPdjV2XbPj7dBeHPwnwhVU7zMuJ+xtMUW5mIOYtmdAAAzUzAH0AAAAFZAAgAAAAAHNKAUxUqBFNS9Ea9NgCZoXMWgwhP4x0/OvoaPRWMquXBXMAIAAAAABUZ551mnP4ZjX+PXU9ttomzuOpo427MVynpkyq+nsYCQVsACAAAAAALnVK5p2tTTeZEh1zYt4iqKIQT9Z0si//Hy1L85oF+5IAAzU0AH0AAAAFZAAgAAAAALfGXDlyDVcGaqtyHkLT0qpuRhJQLgCxtznazhFtuyn/BXMAIAAAAABipxlXDq14C62pXhwAeen5+syA+/C6bN4rtZYcO4zKwAVsACAAAAAAXUf0pzUq0NhLYagWDap4uEiwq5rLpcx29rWbt1NYMsMAAzU1AH0AAAAFZAAgAAAAANoEr8sheJjg4UCfBkuUzarU9NFoy1xwbXjs5ifVDeA9BXMAIAAAAABPoyTf6M+xeZVGES4aNzVlq7LgjqZXJ/QunjYVusGUEAVsACAAAAAA1hA2gMeZZPUNytk9K+lB1RCqWRudRr7GtadJlExJf8oAAzU2AH0AAAAFZAAgAAAAAKvDiK+xjlBe1uQ3SZTNQl2lClIIvpP/5CHwY6Kb3WlgBXMAIAAAAAANnxImq5MFbWaRBHdJp+yD09bVlcFtiFDYsy1eDZj+iQVsACAAAAAAWtsyO+FxMPSIezwsV1TJD8ZrXAdRnQM6DJ+f+1V3qEkAAzU3AH0AAAAFZAAgAAAAAF49IlFH9RmSUSvUQpEPUedEksrQUcjsOv44nMkwXhjzBXMAIAAAAADJtWGbk0bZzmk20obz+mNsp86UCu/nLLlbg7ppxYn7PgVsACAAAAAA3k0Tj/XgPQtcYijH8cIlQoe/VXf15q1nrZNmg7yWYEgAAzU4AH0AAAAFZAAgAAAAAOuSJyuvz50lp3BzXlFKnq62QkN2quNU1Gq1IDsnFoJCBXMAIAAAAAAqavH1d93XV3IzshWlMnzznucadBF0ND092/2ApI1AcAVsACAAAAAAzUrK4kpoKCmcpdZlZNI13fddjdoAseVe67jaX1LobIIAAzU5AH0AAAAFZAAgAAAAALtgC4Whb4ZdkCiI30zY6fwlsxSa7lEaOAU3SfUXr02XBXMAIAAAAACgdZ6U1ZVgUaZZwbIaCdlANpCw6TZV0bwg3DS1NC/mnAVsACAAAAAAzI49hdpp0PbO7S2KexISxC16sE73EUAEyuqUFAC/J48AAzYwAH0AAAAFZAAgAAAAAF6PfplcGp6vek1ThwenMHVkbZgrc/dHgdsgx1VdPqZ5BXMAIAAAAACha3qhWkqmuwJSEXPozDO8y1ZdRLyzt9Crt2vjGnT7AAVsACAAAAAA7nvcU59+LwxGupSF21jAeAE0x7JE94tjRkJfgM1yKU8AAzYxAH0AAAAFZAAgAAAAAKoLEhLvLjKc7lhOJfx+VrGJCx9tXlOSa9bxQzGR6rfbBXMAIAAAAAAIDK5wNnjRMBzET7x/KAMExL/zi1IumJM92XTgXfoPoAVsACAAAAAAFkUYWFwNr815dEdFqp+TiIozDcq5IBNVkyMoDjharDQAAzYyAH0AAAAFZAAgAAAAADoQv6lutRmh5scQFvIW6K5JBquLxszuygM1tzBiGknIBXMAIAAAAADAD+JjW7FoBQ76/rsECmmcL76bmyfXpUU/awqIsZdO+wVsACAAAAAAPFHdLw3jssmEXsgtvl/RBNaUCRA1kgSwsofG364VOvQAAzYzAH0AAAAFZAAgAAAAAJNHUGAgn56KekghO19d11nai3lAh0JAlWfeP+6w4lJBBXMAIAAAAAD9XGJlvz59msJvA6St9fKW9CG4JoHV61rlWWnkdBRLzwVsACAAAAAAxwP/X/InJJHmrjznvahIMgj6pQR30B62UtHCthSjrP0AAzY0AH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzY1AH0AAAAFZAAgAAAAANpIljbxHOM7pydY877gpRQvYY2TGK7igqgGsavqGPBABXMAIAAAAAAqHyEu9gpurPOulApPnr0x9wrygY/7mXe9rAC+tPK80wVsACAAAAAA7gkPzNsS3gCxdFBWbSW9tkBjoR5ib+saDvpGSB3A3ogAAzY2AH0AAAAFZAAgAAAAAGR+gEaZTeGNgG9BuM1bX2R9ed4FCxBA9F9QvdQDAjZwBXMAIAAAAABSkrYFQ6pf8MZ1flgmeIRkxaSh/Eep4Btdx4QYnGGnwAVsACAAAAAApRovMiV00hm/pEcT4XBsyPNw0eo8RLAX/fuabjdU+uwAAzY3AH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzY4AH0AAAAFZAAgAAAAADgyPqQdqQrgfmJjRFAILTHzXbdw5kpKyfeoEcy6YYG/BXMAIAAAAAAE+3XsBQ8VAxAkN81au+f3FDeCD/s7KoZD+fnM1MJSSAVsACAAAAAAhRnjrXecwV0yeCWKJ5J/x12Xx4qVJahsCEVHB/1U2rcAAzY5AH0AAAAFZAAgAAAAAI0CT7JNngTCTUSei1Arw7eHWCD0jumv2rb7imjWIlWABXMAIAAAAABSP8t6ya0SyCphXMwnru6ZUDXWElN0NfBvEOhDvW9bJQVsACAAAAAAGWeGmBNDRaMtvm7Rv+8TJ2sJ4WNXKcp3tqpv5Se9Ut4AAzcwAH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcxAH0AAAAFZAAgAAAAAHIkVuNDkSS1cHIThKc/O0r2/ubaABTOi8Q1r/dvBAsEBXMAIAAAAADdHYqchEiJLM340c3Q4vJABmmth3+MKzwLYlsG6GS7sQVsACAAAAAADa+KP/pdTiG22l+ZWd30P1iHjnBF4zSNRdFm0oEK82kAAzcyAH0AAAAFZAAgAAAAAJmoDILNhC6kn3masElfnjIjP1VjsjRavGk1gSUIjh1NBXMAIAAAAAD97Ilvp3XF8T6MmVVcxMPcdL80RgQ09UoC6PnoOvZ1IQVsACAAAAAA2RK3Xng6v8kpvfVW9tkVXjpE+BSnx9/+Fw85Evs+kUEAAzczAH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzc0AH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzc1AH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzc2AH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzc3AH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzc4AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzc5AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzgwAH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzgxAH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzgyAH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzgzAH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzg0AH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzg1AH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzg2AH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzg3AH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzg4AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzg5AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzkwAH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzkxAH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzkyAH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzkzAH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzk0AH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzk1AH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzk2AH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzk3AH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzk4AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzk5AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzEwMAB9AAAABWQAIAAAAADJDdC9aEFl4Y8J/awHbnXGHjfP+VXQilPHJg7ewaJI7AVzACAAAAAAE+tqRl6EcBMXvbr4GDiNIYObTsYpa1n6BJk9EjIJVicFbAAgAAAAAJVc+HYYqa0m1Hq6OiRX8c0iRnJYOt6AJAJoG0sG3GMSAAMxMDEAfQAAAAVkACAAAAAA3F9rjEKhpoHuTULVGgfUsGGwJs3bISrXkFP1v6KoQLgFcwAgAAAAAIBf0tXw96Z/Ds0XSIHX/zk3MzUR/7WZR/J6FpxRWChtBWwAIAAAAABWrjGlvKYuTS2s8L9rYy8Hf0juFGJfwQmxVIjkTmFIGQADMTAyAH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzEwMwB9AAAABWQAIAAAAACMtPm12YtdEAvqu6Eji1yuRXnu1RJP6h0l7pH3lSH4MwVzACAAAAAAENyCFfyUAh1veQBGx+cxiB7Sasrj41jzCGflZkB5cRMFbAAgAAAAAKdI2LMqISr/T5vuJPg6ZRBm5fVi2aQCc4ra3A4+AjbDAAMxMDQAfQAAAAVkACAAAAAAvlI4lDcs6GB1cnm/Tzo014CXWqidCdyE5t2lknWQd4QFcwAgAAAAAD60SpNc4O2KT7J0llKdSpcX1/Xxs97N715a1HsTFkmBBWwAIAAAAABuuRkJWAH1CynggBt1/5sPh9PoGiqTlS24D/OE2uHXLQADMTA1AH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzEwNgB9AAAABWQAIAAAAABb6LXDWqCp1beQgQjj8I3sRTtFhlrmiBi+h/+ikmrvugVzACAAAAAA9stpgTecT7uTyaGNs3K9Bp0A7R0QaIAOfscyMXHBPX8FbAAgAAAAAHUt+McyXrJ1H8SwnHNVO181Ki8vDAM1f7XI26mg95ZDAAMxMDcAfQAAAAVkACAAAAAA97NTT+81PhDhgptNtp4epzA0tP4iNb9j1AWkiiiKGM8FcwAgAAAAAKPbHg7ise16vxmdPCzksA/2Mn/qST0L9Xe8vnQugVkcBWwAIAAAAABB0EMXfvju4JU/mUH/OvxWbPEl9NJkcEp4iCbkXI41fAADMTA4AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzEwOQB9AAAABWQAIAAAAADQnslvt6Hm2kJPmqsTVYQHE/wWeZ4bE1XSkt7TKy0r1gVzACAAAAAA8URTA4ZMrhHPvlp53TH6FDCzS+0+61qHm5XK6UiOrKEFbAAgAAAAAHQbgTCdZcbdA0avaTmZXUKnIS7Nwf1tNrcXDCw+PdBRAAMxMTAAfQAAAAVkACAAAAAAhujlgFPFczsdCGXtQ/002Ck8YWQHHzvWvUHrkbjv4rwFcwAgAAAAALbV0lLGcSGfE7mDM3n/fgEvi+ifjl7WZ5b3aqjDNvx9BWwAIAAAAACbceTZy8E3QA1pHmPN5kTlOx3EO8kJM5PUjTVftw1VpgADMTExAH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzExMgB9AAAABWQAIAAAAACfw9/te4GkHZAapC9sDMHHHZgmlTrccyJDPFciOMSOcwVzACAAAAAAIIC1ZpHObvmMwUfqDRPl4C1aeuHwujM1G/yJbvybMNAFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5AAMxMTMAfQAAAAVkACAAAAAAkxHJRbnShpPOylLoDdNShfILeA1hChKFQY9qQyZ5VmsFcwAgAAAAAKidrY+rC3hTY+YWu2a7fuMH2RD/XaiTIBW1hrxNCQOJBWwAIAAAAACW0kkqMIzIFMn7g+R0MI8l15fr3k/w/mHtY5n6SYTEwAADMTE0AH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzExNQB9AAAABWQAIAAAAABxMy7X5hf7AXGDz3Y/POu1ZpkMlNcSvSP92NOO/Gs7wAVzACAAAAAAHJshWo2T5wU2zvqCyJzcJQKQaHFHpCpMc9oWBXkpUPoFbAAgAAAAAGeiJKzlUXAvL0gOlW+Hz1mSa2HsV4RGmyLmCHlzbAkoAAMxMTYAfQAAAAVkACAAAAAAlqbslixl7Zw3bRlibZbe/WmKw23k8uKeIzPKYEtbIy0FcwAgAAAAAHEKwpUxkxOfef5HYvulXPmdbzTivwdwrSYIHDeNRcpcBWwAIAAAAADuPckac21Hrg/h0kt5ShJwVEZ9rx6SOHd2+HDjqxEWTQADMTE3AH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzExOAB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFbAAgAAAAAIgbpyYtJj5513Z5XYqviH/HXG/5+mqR52iBbfqMmDtZAAMxMTkAfQAAAAVkACAAAAAAJRzYK0PUwr9RPG2/7yID0WgcTJPB2Xjccp5LAPDYunkFcwAgAAAAAIIh24h3DrltAzNFhF+MEmPrZtzr1PhCofhChZqfCW+jBWwAIAAAAAAzRNXtL5o9VXMk5D5ylI0odPDJDSZZry1wfN+TedH70gADMTIwAH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzEyMQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FbAAgAAAAAJci0w1ZB8W2spJQ+kMpod6HSCtSR2jrabOH+B0fj3A4AAMxMjIAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWwAIAAAAAAlswzAl81+0DteibwHD+CG5mZJrfHXa9NnEFRtXybzzwADMTIzAH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzEyNAB9AAAABWQAIAAAAAAfPUoy7QyZKhIIURso+mkP9qr1izbjETqF5s22GwjCjAVzACAAAAAAvLMsIDQ/go4VUxeh50UHmsvMvfx51cwyONnRD2odvC0FbAAgAAAAAKMb+1CodEalAFnDrEL1Ndt8ztamZ+9134m9Kp3GQgd+AAMxMjUAfQAAAAVkACAAAAAAE3ZqUar0Bq2zWbARE0bAv98jBlK9UJ73/xcwdMWWlSkFcwAgAAAAAK4M+MmC+9sFiFsumMyJZQKxWmmJiuG9H7IzKw083xxkBWwAIAAAAAAqkAONzhvMhkyL1D/6h7QQxEkdhC3p2WjXH+VGq5qCqQADMTI2AH0AAAAFZAAgAAAAAMo8FJiOq63cAmyk2O7eI7GcbQh/1j4RrMTqly3rexftBXMAIAAAAADjVmpd0WiRGTw/gAqEgGolt2EI7Csv14vKdmYoMD0aAgVsACAAAAAA07XQBzBUQMNw7F2/YxJjZNuPVpHTTgbLd1oGk77+bygAAzEyNwB9AAAABWQAIAAAAACu5IGaIx7A3Jvly/kzlCsSA4s3iJwuIl8jEdRH0k93NwVzACAAAAAA9NRUyxYE+t0Xyosyt6vIfMFW/vBoYg6sR+jBNs4JAxIFbAAgAAAAAAzyZ91dx+0oMlOVAjRGiMrPySikY/U9eMEB4WJb3uWtAAMxMjgAfQAAAAVkACAAAAAALkRy0GJInXYLA+cgjs6Myb0a+Gu9hgXhHvhLNoGWfckFcwAgAAAAANbALyt9zCSvwnLaWCd2/y2eoB7qkWTvv1Ldu8r40JPuBWwAIAAAAAD4Fl5bV5sz4isIE9bX+lmAp+aAKaZgVYVZeVfrItkCZAADMTI5AH0AAAAFZAAgAAAAAGoUK/DSWhT8LZhszSUqDbTrp8cSA7rdqmADKL+MILtTBXMAIAAAAABHnEE9bVa6lvhfhEMkkV2kzSSxH/sMW/FIJuw3CzWs6wVsACAAAAAAanavcBdqZxgRGKvEK95wTmeL1K1CeDSXZsXUAs81uOgAAzEzMAB9AAAABWQAIAAAAAC922ZDQE3h2fQKibGMZ9hV0WNlmrPYYSdtaSyYxsWYqgVzACAAAAAAagMovciKK6WVjIc2cCj8nK5O/gVOFFVeVAJpRp89tmQFbAAgAAAAAKcTFfPQzaFiAtSFhqbN02sCE1BKWJSrRfGN5L6oZwzkAAMxMzEAfQAAAAVkACAAAAAAtK+JqX3K/z2txjAU15DgX4y90DS2YLfIJFolCOkJJJwFcwAgAAAAAMnR5V7gfX7MNqqUdL5AkWlkhyFXaBRVNej+Rcn8lrQkBWwAIAAAAAA2cDNRXZuiC241TGRvdFyctJnrNcdbZOP9zHio81tkngADMTMyAH0AAAAFZAAgAAAAAAeGrIMK/bac6kPczxbvRYqKMkcpeI2FjdMpD91FDWIvBXMAIAAAAAAix62z1LeS8yvSXCl5gHSIomjyx76fF3S1lp9k900hygVsACAAAAAAiYwzf2m71aWFD5ajcXyW2JX2EzQOkBroTGMg29nLPYIAAzEzMwB9AAAABWQAIAAAAACphf298InM0Us4HT8o1W1MGw0D/02vd7Jh+U0h7qaFaQVzACAAAAAAFXtk7YpqsOJxsqGWSIL+YcBE96G3Zz9D31gPqDW94y8FbAAgAAAAAAOrS1KVA94rjB1jZ1pPocpCeBG+B14RzWoHqVDpp7JbAAMxMzQAfQAAAAVkACAAAAAATLDS2cuDVM3yDMuWNgk2iGKBTzPpfJMbvxVOSY39ZfcFcwAgAAAAAPT5wRi2cLHIUflXzm6EQB/m7xdThP80ir1VV/JBBqvxBWwAIAAAAAB9lEtZS0aXCFbCtSbhnis27S5IPcfWGygHW8AHn3QqzwADMTM1AH0AAAAFZAAgAAAAAJNjExiZVX7jfFGfYpQu16qxLN0YPqVU/5CQ/Y67YSinBXMAIAAAAABMpm2+6KrkRUlXzQoMPHrQmIO6dkQz66tYdfTeA3dKqQVsACAAAAAAFXobHiMLvNZuEPr8jtewCX2J93EZG3JNeyVg92fue6YAAzEzNgB9AAAABWQAIAAAAABlFkYtLCx901X6QVVMkSn6Z7k30UF4xHaA0OZJJ9bdyQVzACAAAAAATez+F9GHcGzTp7jjv4feboUNb8JCkIp4EqcPFisnq7MFbAAgAAAAACE7JvOpBgMoZ7kRd4QbxIhxukPTUxXpzhjnBHiR7XoRAAMxMzcAfQAAAAVkACAAAAAA8NJKN0IxZnruhswGQkiruv8Ih0EMwDcSZx/Xasup9dkFcwAgAAAAAKaJZRxzA+Igeydvuk6cSwUHXcrmT4PjhuPu//FslpdnBWwAIAAAAAD53Rok1Vq/PMAnXmarqoHJ0PEyYUBmVESa9hIpCv/G9QADMTM4AH0AAAAFZAAgAAAAABHxHdEClz7hbSSgE58+dWLlSMJnoPz+jFxp4bB1GmLQBXMAIAAAAAD3nSvT6aGD+A110J/NwEfp0nPutlmuB5B+wA3CC3noGAVsACAAAAAA3Apjd+TapONB7k5wBVwTWgn8t+Sq2oyyU5/+as109RcAAzEzOQB9AAAABWQAIAAAAAC/o8qW/ifk3KuJ01VFkyNLgQafxB5/bGs2G5VyyVafOwVzACAAAAAA1bMqAFGDHSl6BYNLbxApvkAv2K1/oafywiX0MDz1dGUFbAAgAAAAAHJXLlId3edFoniLD/9K2A5973MeP2Ro31flDyqm3l5QAAMxNDAAfQAAAAVkACAAAAAAY2V8I1bz3a1AxTtmED6UhdhA09huFkuuEX8R+d/WDPUFcwAgAAAAAPTVoNRiI76tcRKqd+JBBVyy4+YcKST42p0QX2BtmQ2VBWwAIAAAAACcxt9hg14WqPNiDv1MkqVljM2e2KJEv53lA17LhV6ZigADMTQxAH0AAAAFZAAgAAAAAO2kSsW0WGN9AOtK4xK2SHrGhWiaAbMEKT4iZkRpaDN/BXMAIAAAAABKGzQcPM8LT2dwOggxoWjv/1imYWabbG/G4kBw8OWaxAVsACAAAAAAC9hLK1dScQTAqg+YAG3ObdPzg2Xet57HmOFpGmyUR9UAAzE0MgB9AAAABWQAIAAAAAAiCwzNEEaH/mDam68IdDftnhthyUFdb+ZCNSBQ91WlHQVzACAAAAAA7tHyHcxCzmbJeFYZyPm4mEgkTGKOvwY4MX82OvH0Jn8FbAAgAAAAAAb5IAbZ1hXCNegQ+S+C9i/Z8y6sS8KeU04V6hXa2ml6AAMxNDMAfQAAAAVkACAAAAAAGuCHVNJSuoVkpPOnS5s89GuA+BLi2IPBUr2Bg1sWEPIFcwAgAAAAAEl1gncS5/xO7bQ/KQSstRV3rOT2SW6nV92ZANeG2SR6BWwAIAAAAAA9LOcKmhek8F2wAh8yvT/vjp2gaouuO+Hmv10lwAeWPAADMTQ0AH0AAAAFZAAgAAAAAMfxz7gEaoCdPvXrubDhCZUS0ARLZc1svgbXgMDlVBPgBXMAIAAAAAB6a5dDA3fuT5Vz2KvAcbUEFX/+B7Nw2p1QqbPoQ5TTuAVsACAAAAAAcf/y75UOuI62A6vWH7bYr/5Jz+nirZVYK/81trN6XOQAAzE0NQB9AAAABWQAIAAAAACnYsqF/VzmjIImC9+dqrHO1TM6lJ6fRwM0mM6Wf6paOwVzACAAAAAA5tgZzch8uDCR1ky3SllVaKVpxAlbrhvlNDTazZZRZOAFbAAgAAAAALeGiLJS4z2zhgVpxzyPdRYyACP9QzQBOob34YrIZumCAAMxNDYAfQAAAAVkACAAAAAAEC0sIVmadtW4YMuRXH7RpAhXclsd+3bmqGXCMeaT014FcwAgAAAAABPpXh0uzpsJJB+IRUNajmMB9WGwswfpw5T9xk3Xj6ANBWwAIAAAAAAmf+NYh9TZ/QRu3w/GQz66n7DtfbJijN3G7KzeL8lstAADMTQ3AH0AAAAFZAAgAAAAABaIB3n49Xm9cOafSrQsE0WCcYp8rMIO/qVwIlMF5YLRBXMAIAAAAAC9EyWJV3xOu9bzgdJ/yX+ko7qLf1u3AxNMataW2C9EzQVsACAAAAAAvVbDkLxXx2DcMLifIQ3K0IIJcLcAG9DUrNfI6aoUjNcAAzE0OAB9AAAABWQAIAAAAAA5rZItA/cocRnngYqcJ3nBXQ+l688aKz3EQyLbYYunPAVzACAAAAAAwKyA+L7TgxztPClLrIMk2JXR+w7c04N3ZOqPgjvrIvsFbAAgAAAAACzvZ33h6aWEe8hmo+1f6OXJ72FY5hvWaUuha64ZV3KFAAMxNDkAfQAAAAVkACAAAAAA3htn7oHJ0YYpIrs+Mzyh85Ys67HwAdv5LQl1mCdoMWkFcwAgAAAAAEHjCtNNLenHuSIYux6ezAHsXDaj2DlTF67ToDhDDe6HBWwAIAAAAAD+P4H0sk9jOd+7vOANt2/1Ectb+4ZRGPE8GkHWNXW3MgADMTUwAH0AAAAFZAAgAAAAAEnt18Km/nqggfIJWxzTr9r3hnXNaueG6XO9A5G11LnGBXMAIAAAAAD7QxzGMN/ard5TfFLecE6uusMmXG2+RBsBR+/NCQHUwAVsACAAAAAAQEZ1ZZ8GC8rdbg7s87OM5Gr9qkTXS9+P5DuAZxj5Gl4AAzE1MQB9AAAABWQAIAAAAAAVAKK/GoY8AACu/hyMpO4hdLq6JnEyWNzkyci9sbaD/wVzACAAAAAA2HmeqpMlvvBpV2zQTYIRmsc4MFlfHRwLof0ycJgMg/MFbAAgAAAAACdltCeWi5E/q1Li1eXLChpM2D9QQSGLBZ82NklQSc0oAAMxNTIAfQAAAAVkACAAAAAAhHyq1GQC/GiMwpYjcsfkNxolJ10ARKjIjfkW1Wipzi0FcwAgAAAAAD/uaGWxTDq87F8XZ6CrFI+RNa8yMqfSZdqK00Kj833BBWwAIAAAAAD6aEdOO0CsQGagioOCvANPCEHSpJ8BSixlPBq5ERhB7AADMTUzAH0AAAAFZAAgAAAAABAJJxHoZD+MQBWqm9UM9Dd3z5ZohIZGWRaRVRsMptKQBXMAIAAAAADrE/ca+gqj/SH4oao4wE4qn2ovoTydzcMbDbrfnUs3zAVsACAAAAAAeNCIQN6hVnGJinytQRFGlQ2ocoprXNqpia+BSxzl+uwAAzE1NAB9AAAABWQAIAAAAAAv01wz7VG9mTepjXQi6Zma+7b/OVBaKVkWNbgDLr1mFgVzACAAAAAA0I5sxz8r6wkCp5Tgvr+iL4p6MxSOq5d3e1kZG+0b7NkFbAAgAAAAAIA32v6oGkAOS96HexGouNTex+tLahtx9QF2dgGClk6WAAMxNTUAfQAAAAVkACAAAAAAWXecRwxSon68xaa9THXnRDw5ZfzARKnvvjTjtbae6T0FcwAgAAAAAPh0UfUMEo7eILCMv2tiJQe1bF9qtXq7GJtC6H5Va4fIBWwAIAAAAADqFr1ThRrTXNgIOrJWScO9mk86Ufi95IDu5gi4vP+HWQADMTU2AH0AAAAFZAAgAAAAAEY5WL8/LpX36iAB1wlQrMO/xHVjoO9BePVzbUlBYo+bBXMAIAAAAABoKcpadDXUARedDvTmzUzWPe1jTuvD0z9oIcZmKuiSXwVsACAAAAAAJuJbwuaMrAFoI+jU/IYr+k4RzAqITrOjAd3HWCpJHqEAAzE1NwB9AAAABWQAIAAAAADnJnWqsfx0xqNnqfFGCxIplVu8mXjaHTViJT9+y2RuTgVzACAAAAAAWAaSCwIXDwdYxWf2NZTly/iKVfG/KDjHUcA1BokN5sMFbAAgAAAAAJVxavipE0H4/JQvhagdytXBZ8qGooeXpkbPQ1RfYMVHAAMxNTgAfQAAAAVkACAAAAAAsPG7LaIpJvcwqcbtfFUpIjj+vpNj70Zjaw3eV9T+QYsFcwAgAAAAAJQ71zi0NlCyY8ZQs3IasJ4gB1PmWx57HpnlCf3+hmhqBWwAIAAAAACD58TO6d+71GaOoS+r73rAxliAO9GMs4Uc8JbOTmC0OwADMTU5AH0AAAAFZAAgAAAAAAGiSqKaQDakMi1W87rFAhkogfRAevnwQ41onWNUJKtuBXMAIAAAAAASgiDpXfGh7E47KkOD8MAcX8+BnDShlnU5JAGdnPdqOAVsACAAAAAAI+2TTQIgbFq4Yr3lkzGwhG/tqChP7hRAx2W0fNaH6jcAAzE2MAB9AAAABWQAIAAAAAB7L4EnhjKA5xJD3ORhH2wOA1BvpnQ+7IjRYi+jjVEaJAVzACAAAAAAuhBIm0nL3FJnVJId+7CKDASEo+l2E89Z9/5aWSITK4AFbAAgAAAAALtSICOzQDfV9d+gZuYxpEj6cCeHnKTT+2G3ceP2H65kAAMxNjEAfQAAAAVkACAAAAAAaROn1NaDZFOGEWw724dsXBAm6bgmL5i0cki6QZQNrOoFcwAgAAAAANVT8R6UvhrAlyqYlxtmnvkR4uYK/hlvyQmBu/LP6/3ZBWwAIAAAAAD+aHNMP/X+jcRHyUtrCNkk1KfMtoD3GTmShS8pWGLt+AADMTYyAH0AAAAFZAAgAAAAADqSR5e0/Th59LrauDA7OnGD1Xr3H3NokfVxzDWOFaN7BXMAIAAAAACt30faNwTWRbvmykDpiDYUOCwA6QDbBBYBFWS7rdOB4AVsACAAAAAAF7SvnjjRk5v2flFOKaBAEDvjXaL1cpjsQLtK2fv9zdQAAzE2MwB9AAAABWQAIAAAAADmtb1ZgpZjSeodPG/hIVlsnS8hoRRwRbrTVx89VwL62AVzACAAAAAAi38e1g6sEyVfSDkzZbaZXGxKI/zKNbMasOl2LYoWrq8FbAAgAAAAAALACk0KcCDN/Kv8WuazY8ORtUGkOZ5Dsm0ys1oOppp/AAMxNjQAfQAAAAVkACAAAAAAf/f7AWVgBxoKjr7YsEQ4w/fqSvuQWV2HMiA3rQ7ur0sFcwAgAAAAADkkeJozP6FFhUdRIN74H4UhIHue+eVbOs1NvbdWYFQrBWwAIAAAAAB55FlHAkmTzAYj/TWrGkRJw2EhrVWUnZXDoMYjyfB/ZwADMTY1AH0AAAAFZAAgAAAAAI2WEOymtuFpdKi4ctanPLnlQud+yMKKb8p/nfKmIy56BXMAIAAAAADVKrJmhjr1rfF3p+T+tl7UFd1B7+BfJRk0e7a4im7ozgVsACAAAAAA5E7Ti3PnFiBQoCcb/DN7V1uM3Xd6VKiexPKntssFL7kAAzE2NgB9AAAABWQAIAAAAAAuHU9Qd79hjyvKOujGanSGDIQlxzsql8JytTZhEnPw+AVzACAAAAAAjF2gV/4+sOHVgDd/oR5wDi9zL7NGpGD+NsEpGXy/a4QFbAAgAAAAAJzMoyojYV6Ed/LpVN5zge93Odv3U7JgP7wxeRaJZGTdAAMxNjcAfQAAAAVkACAAAAAA7dQDkt3iyWYCT94d7yqUtPPwp4qkC0ddu+HFdHgVKEkFcwAgAAAAANuYvtvZBTEq4Rm9+5eb7VuFopowkrAuv86PGP8Q8/QvBWwAIAAAAACeqXoAOQOE4j0zRMlkVd8plaW0RX1npsFvB38Xmzv7sAADMTY4AH0AAAAFZAAgAAAAAAwnZSDhL4tNGYxlHPhKYB8s28dY5ScSwiKZm3UhT8U3BXMAIAAAAABDoY6dhivufTURQExyC9Gx3ocpl09bgbbQLChj3qVGbgVsACAAAAAAF+1nS7O0v85s3CCy+9HkdeoEfm2C6ZiNbPMMnSfsMHUAAzE2OQB9AAAABWQAIAAAAAC2VuRdaC4ZJmLdNOvD6R2tnvkyARteqXouJmI46V306QVzACAAAAAAMn1Z6B35wFTX9mEYAPM+IiJ5hauEwfD0CyIvBrxHg7IFbAAgAAAAAOG6DvDZkT9B/xZWmjao2AevN7MMbs3Oh9YJeSd/hZ+hAAMxNzAAfQAAAAVkACAAAAAAVerb7qVNy457rNOHOgDSKyWl5ojun7iWrv1uHPXrIZQFcwAgAAAAAIDcYS9j5z+gx0xdJj09L7876r/vjvKTi/d3bXDE3PhyBWwAIAAAAADuhVLqb1Bkrx8aNymS+bx2cL8GvLFNH4SAi690DUgnWQADMTcxAH0AAAAFZAAgAAAAAH/E44yLxKCJjuSmU9A8SEhbmkDOx1PqqtYcZtgOzJdrBXMAIAAAAABgLh9v2HjBbogrRoQ82LS6KjZQnzjxyJH4PH+F3jupSAVsACAAAAAAIlO46ehXp4TqpDV0t6op++KO+uWBFh8iFORZjmx2IjkAAzE3MgB9AAAABWQAIAAAAAAlNUdDL+f/SSQ5074mrq0JNh7CTXwTbbhsQyDwWeDVMwVzACAAAAAANIH2IlSNG0kUw4qz0budjcWn8mNR9cJlYUqPYdonucAFbAAgAAAAAJMrOUOyiu5Y3sV76zwEFct8L7+i8WGlQI2+8z2W2kzaAAMxNzMAfQAAAAVkACAAAAAASZ+CvUDtlk/R4HAQ3a+PHrKeY/8ifAfh0oXYFqliu80FcwAgAAAAAJelpzPgM65OZFt/mvGGpwibclQ49wH+1gbUGzd9OindBWwAIAAAAAD9qeDchteEpVXWcycmD9kl9449C1dOw0r60TBm5jK+cQADMTc0AH0AAAAFZAAgAAAAAN9fkoUVbvFV2vMNMAkak4gYfEnzwKI3eDM3pnDK5q3lBXMAIAAAAACnDkgVNVNUlbQ9RhR6Aot2nVy+U4km6+GHPkLr631jEAVsACAAAAAANzg/BnkvkmvOr8nS4omF+q9EG/4oisB+ul4YHi938hwAAzE3NQB9AAAABWQAIAAAAAASyK3b1nmNCMptVEGOjwoxYLLS9fYWm/Zxilqea0jpEQVzACAAAAAADDHsGrbqlKGEpxlvfyqOJKQJjwJrzsrB7k3HG0AUJbkFbAAgAAAAAKwx3S4XfDZh4+LuI9jf7XgUh5qiefNv87JD4qvVRfPSAAMxNzYAfQAAAAVkACAAAAAAlSP9iK31GlcG9MKGbLmq+VXMslURr+As736rrVNXcsUFcwAgAAAAAAvbj0zfq9zzi8XReheKFbCB+h9IsOLgXPPpI5vrEJNZBWwAIAAAAABXvoZhaQE7ogWjeBjceVkp03N20cKYP3TA8vuNsgpfAgADMTc3AH0AAAAFZAAgAAAAAOJNORH8Bev97gVU7y6bznOxJ+E6Qoykur1QP76hG1/7BXMAIAAAAAC+C1PtOOrSZgzBAGhr+dPe/kR0JUw9GTwLVNr61xC1aAVsACAAAAAAeA/L8MQIXkamaObtMPLpoDoi5FypA5WAPtMeMrgi0eQAAzE3OAB9AAAABWQAIAAAAAAKcHzLUomavInN6upPkyWhAqYQACP/vdVCIYpiy6U6HgVzACAAAAAATsR4KItY6R2+U7Gg6sJdaEcf58gjd1OulyWovIqfxKcFbAAgAAAAAFbm10ko67ahboAejQdAV0U2uA5OhZYdb8XUFJ8OL46LAAMxNzkAfQAAAAVkACAAAAAAqTOLiMpCdR59tLZzzIPqJvbCNvz2XQL9ust0qYaehtcFcwAgAAAAAArefox/3k5xGOeiw2m6NUdzuGxmPwcu5IFcj+jMwHgHBWwAIAAAAADLZGFJ7MQd5JXMgMXjqZO5LDLxcFClcXPlnRMWRn+1oAADMTgwAH0AAAAFZAAgAAAAAIPSqSeVzSRgNVNmrPYHmUMgykCY27NbdDUNhE5kx/SgBXMAIAAAAAAhX90nNfxyXmZe/+btZ7q6xMX4PFyj0paM1ccJ/5IUUQVsACAAAAAA419oHmD2W0SYoOMwhrhrp8jf68fg9hTkaRdCuVd3CN0AAzE4MQB9AAAABWQAIAAAAACLn5DxiqAosHGXIAY96FwFKjeqrzXWf3VJIQMwx1fl4gVzACAAAAAAindvU27nveutopdvuHmzdENBbeGFtI3Qcsr07jxmvm8FbAAgAAAAAPvl9pBStQvP4OGkN5v0MghUY6djm9n7XdKKfrW0l1sMAAMxODIAfQAAAAVkACAAAAAA7i2S6rHRSPBwZEn59yxaS7HiYBOmObIkeyCcFU42kf8FcwAgAAAAAGb3RSEyBmgarkTvyLWtOLJcPwCKbCRkESG4RZjVmY4iBWwAIAAAAADB2/wo5CSHR4ANtifY6ZRXNTO5+O8qP82DfAiAeanpZwADMTgzAH0AAAAFZAAgAAAAAFz+M+H/Z94mdPW5oP51B4HWptp1rxcMWAjnlHvWJDWrBXMAIAAAAACBFEOQyL7ZHu4Cq33QvXkmKuH5ibG/Md3RaED9CtG5HwVsACAAAAAAfggtJTprQ/yZzj7y5z9KvXsdeXMWP0yUXMMJqpOwI88AAzE4NAB9AAAABWQAIAAAAAAE7c2x3Z3aM1XGfLNk/XQ9jCazNRbGhVm7H8c2NjS5ywVzACAAAAAARJ9h8fdcwA19velF3L/Wcvi2rCzewlKZ2nA0p8bT9uwFbAAgAAAAAJtWe6b4wK2Hae2dZm/OEpYQnvoZjz4Sz5IgJC2wInecAAMxODUAfQAAAAVkACAAAAAAVoRt9B9dNVvIMGN+ea5TzRzQC+lqSZ8dd/170zU5o9cFcwAgAAAAAEwM95XZin5mv2yhCI8+ugtKuvRVmNgzzIQN0yi1+9aIBWwAIAAAAAAMGBq72n00rox3uqhxSB98mkenTGCdbbUF1gXrgottzgADMTg2AH0AAAAFZAAgAAAAAKRDkjyWv/etlYT4GyoXrmBED2FgZHnhc+l9Wsl06cH2BXMAIAAAAABohlpm3K850Vndf3NmNE0hHqDlNbSR8/IvMidQ3LnIZAVsACAAAAAAW42nGHa6q2MCAaaPVwaIDfr8QLyQwjKq23onZJYsqVsAAzE4NwB9AAAABWQAIAAAAAC3DFh5oklLCNLY90bgWm68dFXz65JpAZSp1K99MBTPAQVzACAAAAAAQgZecmxEUZVHoptEQClDwAf8smI3WynQ/i+JBP0g+kQFbAAgAAAAAEUSQGVnAPISD6voD0DiBUqyWKgt2rta0tjmoe+LNt6IAAMxODgAfQAAAAVkACAAAAAAQ5WKvWSB503qeNlOI2Tpjd5blheNr6OBO8pfJfPNstcFcwAgAAAAAKwHgQLSDJ5NwLBQbY5OnblQIsVDpGV7q3RCbFLD1U4/BWwAIAAAAACQ5nED99LnpbqXZuUOUjnO2HTphEAFBjLD4OZeDEYybgADMTg5AH0AAAAFZAAgAAAAAGfhFY3RGRm5ZgWRQef1tXxHBq5Y6fXaLAR4yJhrTBplBXMAIAAAAACKEF0ApLoB6lP2UqTFsTQYNc9OdDrs/vziPGzttGVLKQVsACAAAAAArOO6FyfNRyBi0sPT5iye7M8d16MTLcwRfodZq4uCYKEAAzE5MAB9AAAABWQAIAAAAAAIM73gPcgzgotYHLeMa2zAU4mFsr7CbILUZWfnuKSwagVzACAAAAAAJCSu98uV8xv88f2BIOWzt6p+6EjQStMBdkGPUkgN79cFbAAgAAAAAMGqPGMPxXbmYbVfSa/japvUljht1zZT33TY7ZjAiuPfAAMxOTEAfQAAAAVkACAAAAAAkWmHCUsiMy1pwZTHxVPBzPTrWFBUDqHNrVqcyyt7nO8FcwAgAAAAAMv2CebFRG/br7USELR98sIdgE9OQCRBGV5JZCO+uPMgBWwAIAAAAABt7qSmn3gxJu7aswsbUiwvO+G6lXj/Xhx+J/zQyZxzLAADMTkyAH0AAAAFZAAgAAAAAGInUYv0lP/rK7McM8taEHXRefk8Q2AunrvWqdfSV7UaBXMAIAAAAACE+WPxJ3gan7iRTbIxXXx+bKVcaf8kP4JD8DcwU0aL7wVsACAAAAAAUC4eTprX4DUZn2X+UXYU6QjtiXk+u57yoOPBbPQUmDkAAzE5MwB9AAAABWQAIAAAAACmHlg2ud3cplXlTsNTpvNnY6Qm1Fce0m899COamoDjaQVzACAAAAAArtJQeJIlepBWRU2aYar7+YGYVQ7dfDc1oxgTmA8r9q0FbAAgAAAAAOk45vg5VqZHAFCO3i0Z52SZi5RADf8NXwf68T5yad/DAAMxOTQAfQAAAAVkACAAAAAApzcWSAbZWV/Rq+ylRNqqlJqNVR4fhXrz4633/MQOQgcFcwAgAAAAAN/jz/bsEleiuCl+li83EWlG6UMHA8CyaOMRKCkXkSCPBWwAIAAAAAC3Sd+Qg+uFDKpGZHbrQgokXHQ1az1aFl4YK343OB6hcQAAEmNtAAAAAAAAAAAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAEAAAASc3AAAQAAAAAAAAAQdGYAAQAAABNtbgD/////Y46NN8CHrb4J7f/fE214AP////9jjo03wIetvgnt/18A", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedDecimalNoPrecision": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.json new file mode 100644 index 0000000000..41ba49112b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.json @@ -0,0 +1,647 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml new file mode 100644 index 0000000000..d641d955ef --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Aggregate.yml @@ -0,0 +1,231 @@ +description: fle2v2-Rangev2-DecimalPrecision-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedDecimalPrecision": { $gt: { $numberDecimal: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedDecimalPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.json new file mode 100644 index 0000000000..bc4e1f4508 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.json @@ -0,0 +1,1418 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "-1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 200, + "encryptedDecimalPrecision": { + "$numberDecimal": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lte": { + "$numberDecimal": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$lt": { + "$numberDecimal": "0.0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "200.0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0.0" + }, + "$lt": { + "$numberDecimal": "2.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberDecimal": "0.0" + }, + "$lte": { + "$numberDecimal": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + }, + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDecimalPrecision": { + "$in": [ + { + "$numberDecimal": "0.0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberInt": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.yml new file mode 100644 index 0000000000..8aa2e8e026 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Correctness.yml @@ -0,0 +1,460 @@ +description: fle2v2-Rangev2-DecimalPrecision-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } } } + object: "coll" + expectError: + errorContains: must be less than the range max + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" }, $lte: { $numberDecimal: "200.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $in: [$numberDecimal: "0.0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Insert out of range" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "-1" } } + object: "coll" + expectError: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll" + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDecimalPrecision: { $numberDecimal: "200.0" } } + object: "coll" + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc200] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "1.0" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "1.0" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lte: { $numberDecimal: "1.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $lt: { $numberDecimal: "0.0" } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "200.0" } } } } + object: "coll" + expectError: + errorContains: must be less than the range max + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0.0" }, $lt: { $numberDecimal: "2.0" } } }, + } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "0.0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $numberDecimal: "1.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDecimalPrecision: { $gte: { $numberDecimal: "0.0" }, $lte: { $numberDecimal: "200.0" } } }, + } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDecimalPrecision: { $in: [$numberDecimal: "0.0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Int" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDecimalPrecision: { $numberInt: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Int" + operations: + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gte: { $numberInt: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.json new file mode 100644 index 0000000000..1912f68ee5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.json @@ -0,0 +1,539 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.yml new file mode 100644 index 0000000000..48c16426a3 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Delete.yml @@ -0,0 +1,213 @@ +description: fle2v2-Rangev2-DecimalPrecision-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedDecimalPrecision": { $gt: { $numberDecimal: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedDecimalPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json new file mode 100644 index 0000000000..9cf4488622 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.json @@ -0,0 +1,651 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$numberInt": "0" + }, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml new file mode 100644 index 0000000000..d237d3e157 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate.yml @@ -0,0 +1,230 @@ +description: fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimalPrecision": { $numberDecimal: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": { "$numberInt": "0" }, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedDecimalPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedDecimalPrecision": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.json new file mode 100644 index 0000000000..a9c3a8a46a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.json @@ -0,0 +1,634 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml new file mode 100644 index 0000000000..3be4311f3e --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-InsertFind.yml @@ -0,0 +1,225 @@ +description: fle2v2-Rangev2-DecimalPrecision-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedDecimalPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.json new file mode 100644 index 0000000000..7f8ea38ae0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.json @@ -0,0 +1,653 @@ +{ + "description": "fle2v2-Rangev2-DecimalPrecision-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDecimalPrecision": { + "$numberDecimal": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDecimalPrecision": { + "$numberDecimal": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDecimalPrecision": { + "$gt": { + "$numberDecimal": "0" + } + } + }, + "update": { + "$set": { + "encryptedDecimalPrecision": { + "$numberDecimal": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDecimalPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDecimalPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDecimalPrecision": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDecimal": "0.0" + }, + "max": { + "$numberDecimal": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.yml new file mode 100644 index 0000000000..a5d1798955 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DecimalPrecision-Update.yml @@ -0,0 +1,238 @@ +description: fle2v2-Rangev2-DecimalPrecision-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDecimalPrecision", + "bsonType": "decimal", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDecimal": "0.0" }, + "max": { "$numberDecimal": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDecimalPrecision: { $numberDecimal: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDecimalPrecision: { $numberDecimal: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedDecimalPrecision: { $gt: { $numberDecimal: "0" } } } + update: { "$set": { "encryptedDecimalPrecision": { $numberDecimal: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDecimalPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDecimalPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedDecimalPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedDecimalPrecision": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.json new file mode 100644 index 0000000000..cdbd169676 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.json @@ -0,0 +1,444 @@ +{ + "description": "fle2v2-Rangev2-Defaults", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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 applies defaults for trimFactor and sparsity", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", + "subType": "00" + } + } + ] + }, + { + "_id": { + "$numberInt": "1" + }, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", + "subType": "00" + } + }, + { + "$binary": { + "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$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": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.yml new file mode 100644 index 0000000000..c3199cc2ae --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Defaults.yml @@ -0,0 +1,180 @@ +description: fle2v2-Rangev2-Defaults +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + topologies: ["replicaset", "sharded", "load-balanced"] # Exclude "standalone". QE collections are not supported on standalone. + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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 applies defaults for trimFactor and sparsity" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$binary": { "base64": "RjBYT2h3ZAoHxhf8DU6/dFbDkEBZp0IxREcsRTu2MXs=", "subType": "00" }, + "$binary": { "base64": "+vC6araOEo+fpW7PSIP40/EnzBCj1d2N10Jr3rrXJJM=", "subType": "00" }, + ], + } + - { + "_id": { "$numberInt": "1" }, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$binary": { "base64": "25j9sQXZCihCmHKvTHgaBsAVZFcGPn7JjHdrCGlwyyw=", "subType": "00" }, + "$binary": { "base64": "SlNHXyqVFGDPrX/2ppwog6l4pwj3PKda2TkZbqgfSfA=", "subType": "00" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: &encryptionInformation + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: *encryptionInformation + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + } + encryptionInformation: *encryptionInformation + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.json new file mode 100644 index 0000000000..c0211a1a34 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.json @@ -0,0 +1,1195 @@ +{ + "description": "fle2v2-Rangev2-Double-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "DbMkAAADcGF5bG9hZABXJAAABGcAQyQAAAMwAH0AAAAFZAAgAAAAAHgYoMGjEE6fAlAhICv0+doHcVX8CmMVxyq7+jlyGrvmBXMAIAAAAAC/5MQZgTHuIr/O5Z3mXPvqrom5JTQ8IeSpQGhO9sB+8gVsACAAAAAAuPSXVmJUAUpTQg/A9Bu1hYczZF58KEhVofakygbsvJQAAzEAfQAAAAVkACAAAAAA2kiWNvEc4zunJ1jzvuClFC9hjZMYruKCqAaxq+oY8EAFcwAgAAAAACofIS72Cm6s866UCk+evTH3CvKBj/uZd72sAL608rzTBWwAIAAAAADuCQ/M2xLeALF0UFZtJb22QGOhHmJv6xoO+kZIHcDeiAADMgB9AAAABWQAIAAAAABkfoBGmU3hjYBvQbjNW19kfXneBQsQQPRfUL3UAwI2cAVzACAAAAAAUpK2BUOqX/DGdX5YJniEZMWkofxHqeAbXceEGJxhp8AFbAAgAAAAAKUaLzIldNIZv6RHE+FwbMjzcNHqPESwF/37mm43VPrsAAMzAH0AAAAFZAAgAAAAAFNprhQ3ZwIcYbuzLolAT5n/vc14P9kUUQComDu6eFyKBXMAIAAAAAAcx9z9pk32YbPV/sfPZl9ALIEVsqoLXgqWLVK/tP+heAVsACAAAAAA/qxvuvJbAHwwhfrPVpmCFzNvg2cU/NXaWgqgYUZpgXwAAzQAfQAAAAVkACAAAAAAODI+pB2pCuB+YmNEUAgtMfNdt3DmSkrJ96gRzLphgb8FcwAgAAAAAAT7dewFDxUDECQ3zVq75/cUN4IP+zsqhkP5+czUwlJIBWwAIAAAAACFGeOtd5zBXTJ4JYonkn/HXZfHipUlqGwIRUcH/VTatwADNQB9AAAABWQAIAAAAACNAk+yTZ4Ewk1EnotQK8O3h1gg9I7pr9q2+4po1iJVgAVzACAAAAAAUj/LesmtEsgqYVzMJ67umVA11hJTdDXwbxDoQ71vWyUFbAAgAAAAABlnhpgTQ0WjLb5u0b/vEydrCeFjVynKd7aqb+UnvVLeAAM2AH0AAAAFZAAgAAAAAD/FIrGYFDjyYmVb7oTMVwweWP7A6F9LnyIuNO4MjBnXBXMAIAAAAACIZgJCQRZu7NhuNMyOqCn1tf+DfU1qm10TPCfj5JYV3wVsACAAAAAA5hmY4ptuNxULGf87SUFXQWGAONsL9U29duh8xqsHtxoAAzcAfQAAAAVkACAAAAAAciRW40ORJLVwchOEpz87Svb+5toAFM6LxDWv928ECwQFcwAgAAAAAN0dipyESIkszfjRzdDi8kAGaa2Hf4wrPAtiWwboZLuxBWwAIAAAAAANr4o/+l1OIbbaX5lZ3fQ/WIeOcEXjNI1F0WbSgQrzaQADOAB9AAAABWQAIAAAAACZqAyCzYQupJ95mrBJX54yIz9VY7I0WrxpNYElCI4dTQVzACAAAAAA/eyJb6d1xfE+jJlVXMTD3HS/NEYENPVKAuj56Dr2dSEFbAAgAAAAANkSt154Or/JKb31VvbZFV46RPgUp8ff/hcPORL7PpFBAAM5AH0AAAAFZAAgAAAAAI5bm3YO0Xgf0VT+qjVTTfvckecM3Cwqj7DTKZXf8/NXBXMAIAAAAAD/m+h8fBhWaHm6Ykuz0WX1xL4Eme3ErLObyEVJf8NCywVsACAAAAAAfb1VZZCqs2ivYbRzX4p5CtaCkKW+g20Pr57FWXzEZi8AAzEwAH0AAAAFZAAgAAAAANqo4+p6qdtCzcB4BX1wQ6llU7eFBnuu4MtZwp4B6mDlBXMAIAAAAAAGiz+VaukMZ+6IH4jtn4KWWdKK4/W+O+gRioQDrfzpMgVsACAAAAAAG4YYkTp80EKo59mlHExDodRQFR7njhR5dmISwUJ6ukAAAzExAH0AAAAFZAAgAAAAAPrFXmHP2Y4YAm7b/aqsdn/DPoDkv7B8egWkfe23XsM1BXMAIAAAAAAGhwpKAr7skeqHm3oseSbO7qKNhmYsuUrECBxJ5k+D2AVsACAAAAAAAqPQi9luYAu3GrFCEsVjd9z2zIDcp6SPTR2w6KQEr+IAAzEyAH0AAAAFZAAgAAAAABzjYxwAjXxXc0Uxv18rH8I3my0Aguow0kTwKyxbrm+cBXMAIAAAAADVbqJVr6IdokuhXkEtXF0C2gINLiAjMVN20lE20Vmp2QVsACAAAAAAD7K1Fx4gFaaizkIUrf+EGXQeG7QX1jadhGc6Ji471H8AAzEzAH0AAAAFZAAgAAAAAFMm2feF2fFCm/UC6AfIyepX/xJDSmnnolQIBnHcPmb5BXMAIAAAAABLI11kFrQoaNVZFmq/38aRNImPOjdJh0Lo6irI8M/AaAVsACAAAAAAOWul0oVqJ9CejD2RqphhTC98DJeRQy5EwbNerU2+4l8AAzE0AH0AAAAFZAAgAAAAAJvXB3KyNiNtQko4SSzo/9b2qmM2zU9CQTTDfLSBWMgRBXMAIAAAAAAvjuVP7KsLRDeqVqRziTKpBrjVyqKiIbO9Gw8Wl2wFTAVsACAAAAAADlE+oc1ins+paNcaOZJhBlKlObDJ4VQORWjFYocM4LgAAzE1AH0AAAAFZAAgAAAAAPGdcxDiid8z8XYnfdDivNMYVPgBKdGOUw6UStU+48CdBXMAIAAAAAARj6g1Ap0eEfuCZ4X2TsEw+Djrhto3fA5nLwPaY0vCTgVsACAAAAAAoHqiwGOUkBu8SX5U1yHho+UIFdSN2MdQN5s6bQ0EsJYAAzE2AH0AAAAFZAAgAAAAAP5rGPrYGt3aKob5f/ldP0qrW7bmWvqnKY4QwdDWz400BXMAIAAAAADTQkW2ymaaf/bhteOOGmSrIR97bAnJx+yN3yMj1bTeewVsACAAAAAADyQnHGH2gF4w4L8axUsSTf6Ubk7L5/eoFOJk12MtZAoAAzE3AH0AAAAFZAAgAAAAAAlz6wJze5UkIxKpJOZFGCOf3v2KByWyI6NB6JM9wNcBBXMAIAAAAABUC7P/neUIHHoZtq0jFVBHY75tSFYr1Y5S16YN5XxC1QVsACAAAAAAgvxRbXDisNnLY3pfsjDdnFLtkvYUC4lhA68eBXc7KAwAAzE4AH0AAAAFZAAgAAAAAFJ8AtHcjia/9Y5pLEc3qVgH5xKiXw12G9Kn2A1EY8McBXMAIAAAAAAxe7Bdw7eUSBk/oAawa7uicTEDgXLymRNhBy1LAxhDvwVsACAAAAAAxKPaIBKVx3jTA+R/el7P7AZ7efrmTGjJs3Hj/YdMddwAAzE5AH0AAAAFZAAgAAAAAO8uwQUaKFb6vqR3Sv3Wn4QAonC2exOC9lGG1juqP5DtBXMAIAAAAABZf1KyJgQg8/Rf5c02DgDK2aQu0rNCOvaL60ohDHyY+gVsACAAAAAAqyEjfKC8lYoIfoXYHUqHZPoaA6EK5BAZy5dxXZmay4kAAzIwAH0AAAAFZAAgAAAAAE8YtqyRsGCeiR6hhiyisR/hccmK4nZqIMzO4lUBmEFzBXMAIAAAAAC1UYOSKqAeG1UJiKjWFVskRhuFKpj9Ezy+lICZvFlN5AVsACAAAAAA6Ct9nNMKyRazn1OKnRKagm746CGu+jyhbL1qJnZxGi0AAzIxAH0AAAAFZAAgAAAAAPhCrMausDx1QUIEqp9rUdRKyM6a9AAx7jQ3ILIu8wNIBXMAIAAAAACmH8lotGCiF2q9VQxhsS+7LAZv79VUAsOUALaGxE/EpAVsACAAAAAAnc1xCKfdvbUEc8F7XZqlNn1C+hZTtC0I9I3LL06iaNkAAzIyAH0AAAAFZAAgAAAAAOBi/GAYFcstMSJPgp3VkMiuuUUCrZytvqYaU8dwm8v2BXMAIAAAAACEZSZVyD3pKzGlbdwlYmWQhHHTV5SnNLknl2Gw8IaUTQVsACAAAAAAfsLZsEDcWSuNsIo/TD1ReyQW75HPMgmuKZuWFOLKRLoAAzIzAH0AAAAFZAAgAAAAAIQuup+YGfH3mflzWopN8J1X8o8a0d9CSGIvrA5HOzraBXMAIAAAAADYvNLURXsC2ITMqK14LABQBI+hZZ5wNf24JMcKLW+84AVsACAAAAAACzfjbTBH7IwDU91OqLAz94RFkoqBOkzKAqQb55gT4/MAAzI0AH0AAAAFZAAgAAAAAKsh0ADyOnVocFrOrf6MpTrNvAj8iaiE923DPryu124gBXMAIAAAAADg24a8NVE1GyScc6tmnTbmu5ulzO+896fE92lN08MeswVsACAAAAAAaPxcOIxnU7But88/yadOuDJDMcCywwrRitaxMODT4msAAzI1AH0AAAAFZAAgAAAAAKkVC2Y6HtRmv72tDnPUSjJBvse7SxLqnr09/Uuj9sVVBXMAIAAAAABYNFUkH7ylPMN+Bc3HWX1e0flGYNbtJNCY9SltJCW/UAVsACAAAAAAZYK/f9H4OeihmpiFMH7Wm7uLvs2s92zNA8wyrNZTsuMAAzI2AH0AAAAFZAAgAAAAADDggcwcb/Yn1Kk39sOHsv7BO/MfP3m/AJzjGH506Wf9BXMAIAAAAAAYZIsdjICS0+BDyRUPnrSAZfPrwtuMaEDEn0/ijLNQmAVsACAAAAAAGPnYVvo2ulO9z4LGd/69NAklfIcZqZvFX2KK0s+FcTUAAzI3AH0AAAAFZAAgAAAAAEWY7dEUOJBgjOoWVht1wLehsWAzB3rSOBtLgTuM2HC8BXMAIAAAAAAAoswiHRROurjwUW8u8D5EUT+67yvrgpB/j6PzBDAfVwVsACAAAAAA6NhRTYFL/Sz4tao7vpPjLNgAJ0FX6P/IyMW65qT6YsMAAzI4AH0AAAAFZAAgAAAAAPZaapeAUUFPA7JTCMOWHJa9lnPFh0/gXfAPjA1ezm4ZBXMAIAAAAACmJvLY2nivw7/b3DOKH/X7bBXjJwoowqb1GtEFO3OYgAVsACAAAAAA/JcUoyKacCB1NfmH8vYqC1f7rd13KShrQqV2r9QBP44AAzI5AH0AAAAFZAAgAAAAAK00u6jadxCZAiA+fTsPVDsnW5p5LCr4+kZZZOTDuZlfBXMAIAAAAAAote4zTEYMDgaaQbAdN8Dzv93ljPLdGjJzvnRn3KXgtQVsACAAAAAAxXd9Mh6R3mnJy8m7UfqMKi6oD5DlZpkaOz6bEjMOdiwAAzMwAH0AAAAFZAAgAAAAAFbgabdyymiEVYYwtJSWa7lfl/oYuj/SukzJeDOR6wPVBXMAIAAAAADAFGFjS1vPbN6mQEhkDYTD6V2V23Ys9gUEUMGNvMPkaAVsACAAAAAAL/D5Sze/ZoEanZLK0IeEkhgVkxEjMWVCfmJaD3a8uNIAAzMxAH0AAAAFZAAgAAAAABNMR6UBv2E627CqLtQ/eDYx7OEwQ7JrR4mSHFa1N8tLBXMAIAAAAAAxH4gucI4UmNVB7625C6hFSVCuIpJO3lusJlPuL8H5EQVsACAAAAAAVLHNg0OUVqZ7WGOP53BkTap9FOw9dr1P4J8HxqFqU04AAzMyAH0AAAAFZAAgAAAAAG8cd6WBneNunlqrQ2EmNf35W7OGObGq9WL4ePX+LUDmBXMAIAAAAAAjJ2+sX87NSis9hBsgb1QprVRnO7Bf+GObCGoUqyPE4wVsACAAAAAAs9c9SM49/pWmyUQKslpt3RTMBNSRppfNO0JBvUqHPg0AAzMzAH0AAAAFZAAgAAAAAFWOUGkUpy8yf6gB3dio/aOfRKh7XuhvsUj48iESFJrGBXMAIAAAAAAY7sCDMcrUXvNuL6dO0m11WyijzXZvPIcOKob6IpC4PQVsACAAAAAAJOP+EHz6awDb1qK2bZQ3kTV7wsj5Daj/IGAWh4g7omAAAzM0AH0AAAAFZAAgAAAAAGUrIdKxOihwNmo6B+aG+Ag1qa0+iqdksHOjQj+Oy9bZBXMAIAAAAABwa5dbI2KmzBDNBTQBEkjZv4sPaeRkRNejcjdVymRFKQVsACAAAAAA4ml/nm0gJNTcJ4vuD+T2Qfq2fQZlibJp/j6MOGDrbHMAAzM1AH0AAAAFZAAgAAAAAOx89xV/hRk64/CkM9N2EMK6aldII0c8smdcsZ46NbP8BXMAIAAAAADBF6tfQ+7q9kTuLyuyrSnDgmrdmrXkdhl980i1KHuGHgVsACAAAAAACUqiFqHZdGbwAA+hN0YUE5zFg+H+dabIB4dj5/75W/YAAzM2AH0AAAAFZAAgAAAAAMkN0L1oQWXhjwn9rAdudcYeN8/5VdCKU8cmDt7BokjsBXMAIAAAAAAT62pGXoRwExe9uvgYOI0hg5tOxilrWfoEmT0SMglWJwVsACAAAAAAlVz4dhiprSbUero6JFfxzSJGclg63oAkAmgbSwbcYxIAAzM3AH0AAAAFZAAgAAAAANxfa4xCoaaB7k1C1RoH1LBhsCbN2yEq15BT9b+iqEC4BXMAIAAAAACAX9LV8Pemfw7NF0iB1/85NzM1Ef+1mUfyehacUVgobQVsACAAAAAAVq4xpbymLk0trPC/a2MvB39I7hRiX8EJsVSI5E5hSBkAAzM4AH0AAAAFZAAgAAAAAOYIYoWkX7dGuyKfi3XssUlc7u/gWzqrR9KMkikKVdmSBXMAIAAAAABVF2OYjRTGi9Tw8XCAwZWLpX35Yl271TlNWp6N/nROhAVsACAAAAAA0nWwYzXQ1+EkDvnGq+SMlq20z+j32Su+i/A95SggPb4AAzM5AH0AAAAFZAAgAAAAAIy0+bXZi10QC+q7oSOLXK5Fee7VEk/qHSXukfeVIfgzBXMAIAAAAAAQ3IIV/JQCHW95AEbH5zGIHtJqyuPjWPMIZ+VmQHlxEwVsACAAAAAAp0jYsyohKv9Pm+4k+DplEGbl9WLZpAJzitrcDj4CNsMAAzQwAH0AAAAFZAAgAAAAAL5SOJQ3LOhgdXJ5v086NNeAl1qonQnchObdpZJ1kHeEBXMAIAAAAAA+tEqTXODtik+ydJZSnUqXF9f18bPeze9eWtR7ExZJgQVsACAAAAAAbrkZCVgB9Qsp4IAbdf+bD4fT6Boqk5UtuA/zhNrh1y0AAzQxAH0AAAAFZAAgAAAAAKl8zcHJRDjSjJeV/WvMxulW1zrTFtaeBy/aKKhadc6UBXMAIAAAAADBdWQl5SBIvtZZLIHszePwkO14W1mQ0izUk2Ov21cPNAVsACAAAAAAHErCYycpqiIcCZHdmPL1hi+ovLQk4TAvENpfLdTRamQAAzQyAH0AAAAFZAAgAAAAAFvotcNaoKnVt5CBCOPwjexFO0WGWuaIGL6H/6KSau+6BXMAIAAAAAD2y2mBN5xPu5PJoY2zcr0GnQDtHRBogA5+xzIxccE9fwVsACAAAAAAdS34xzJesnUfxLCcc1U7XzUqLy8MAzV/tcjbqaD3lkMAAzQzAH0AAAAFZAAgAAAAAPezU0/vNT4Q4YKbTbaeHqcwNLT+IjW/Y9QFpIooihjPBXMAIAAAAACj2x4O4rHter8ZnTws5LAP9jJ/6kk9C/V3vL50LoFZHAVsACAAAAAAQdBDF3747uCVP5lB/zr8VmzxJfTSZHBKeIgm5FyONXwAAzQ0AH0AAAAFZAAgAAAAAMqpayM2XotEFmm0gwQd9rIzApy0X+7HfOhNk6VU7F5lBXMAIAAAAACJR9+q5T9qFHXFNgGbZnPubG8rkO6cwWhzITQTmd6VgwVsACAAAAAAOZLQ6o7e4mVfDzbpQioa4d3RoTvqwgnbmc5Qh2wsZuoAAzQ1AH0AAAAFZAAgAAAAANCeyW+3oebaQk+aqxNVhAcT/BZ5nhsTVdKS3tMrLSvWBXMAIAAAAADxRFMDhkyuEc++WnndMfoUMLNL7T7rWoeblcrpSI6soQVsACAAAAAAdBuBMJ1lxt0DRq9pOZldQqchLs3B/W02txcMLD490FEAAzQ2AH0AAAAFZAAgAAAAAIbo5YBTxXM7HQhl7UP9NNgpPGFkBx871r1B65G47+K8BXMAIAAAAAC21dJSxnEhnxO5gzN5/34BL4von45e1meW92qowzb8fQVsACAAAAAAm3Hk2cvBN0ANaR5jzeZE5TsdxDvJCTOT1I01X7cNVaYAAzQ3AH0AAAAFZAAgAAAAABm/6pF96j26Jm7z5KkY1y33zcAEXLx2n0DwC03bs/ixBXMAIAAAAAD01OMvTZI/mqMgxIhA5nLs068mW+GKl3OW3ilf2D8+LgVsACAAAAAAaLvJDrqBESTNZSdcXsd+8GXPl8ZkUsGpeYuyYVv/kygAAzQ4AH0AAAAFZAAgAAAAAJ/D3+17gaQdkBqkL2wMwccdmCaVOtxzIkM8VyI4xI5zBXMAIAAAAAAggLVmkc5u+YzBR+oNE+XgLVp64fC6MzUb/Ilu/Jsw0AVsACAAAAAACz3HVKdWkx82/kGbVpcbAeZtsj2R5Zr0dEPfle4IErkAAzQ5AH0AAAAFZAAgAAAAAJMRyUW50oaTzspS6A3TUoXyC3gNYQoShUGPakMmeVZrBXMAIAAAAACona2Pqwt4U2PmFrtmu37jB9kQ/12okyAVtYa8TQkDiQVsACAAAAAAltJJKjCMyBTJ+4PkdDCPJdeX695P8P5h7WOZ+kmExMAAAzUwAH0AAAAFZAAgAAAAAByuYl8dBvfaZ0LO/81JW4hYypeNmvLMaxsIdvqMPrWoBXMAIAAAAABNddwobOUJzm9HOUD8BMZJqkNCUCqstHZkC76FIdNg9AVsACAAAAAAQQOkIQtkyNavqCnhQbNg3HfqrJdsAGaoxSJePJl1qXsAAzUxAH0AAAAFZAAgAAAAAHEzLtfmF/sBcYPPdj8867VmmQyU1xK9I/3Y0478azvABXMAIAAAAAAcmyFajZPnBTbO+oLInNwlApBocUekKkxz2hYFeSlQ+gVsACAAAAAAZ6IkrOVRcC8vSA6Vb4fPWZJrYexXhEabIuYIeXNsCSgAAzUyAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzUzAH0AAAAFZAAgAAAAAMXrXx0saZ+5gORmwM2FLuZG6iuO2YS+1IGPoAtDKoKBBXMAIAAAAADIQsxCr8CfFKaBcx8kIeSywnGh7JHjKRJ9vJd9x79y7wVsACAAAAAAcvBV+SykDYhmRFyVYwFYB9oBKBSHr55Jdz2cXeowsUQAAzU0AH0AAAAFZAAgAAAAACbzcUD3INSnCRspOKF7ubne74OK9L0FTZvi9Ay0JVDYBXMAIAAAAADPebVQH8Btk9rhBIoUOdSAdpPvz7qIY4UC2i6IGisSAQVsACAAAAAAiBunJi0mPnnXdnldiq+If8dcb/n6apHnaIFt+oyYO1kAAzU1AH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzU2AH0AAAAFZAAgAAAAAHSaHWs/dnmI9sc7nB50VB2Bzs0kHapMHCQdyVEYY30TBXMAIAAAAACkV22lhEjWv/9/DubfHBAcwJggKI5mIbSK5L2nyqloqQVsACAAAAAAS19m7DccQxgryOsBJ3GsCs37yfQqNi1G+S6fCXpEhn4AAzU3AH0AAAAFZAAgAAAAAAL8jhNBG0KXXZhmZ0bPXtfgapJCB/AI+BEHB0eZ3C75BXMAIAAAAADHx/fPa639EBmGV5quLi8IQT600ifiKSOhTDOK19DnzwVsACAAAAAAlyLTDVkHxbayklD6Qymh3odIK1JHaOtps4f4HR+PcDgAAzU4AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzU5AH0AAAAFZAAgAAAAABmO7QD9vxWMmFjIHz13lyOeV6vHT6mYCsWxF7hb/yOjBXMAIAAAAACT9lmgkiqzuWG24afuzYiCeK9gmJqacmxAruIukd0xEAVsACAAAAAAZa0/FI/GkZR7CtX18Xg9Tn9zfxkD0UoaSt+pIO5t1t4AAzYwAH0AAAAFZAAgAAAAAB89SjLtDJkqEghRGyj6aQ/2qvWLNuMROoXmzbYbCMKMBXMAIAAAAAC8sywgND+CjhVTF6HnRQeay8y9/HnVzDI42dEPah28LQVsACAAAAAAoxv7UKh0RqUAWcOsQvU123zO1qZn73Xfib0qncZCB34AAzYxAH0AAAAFZAAgAAAAABN2alGq9Aats1mwERNGwL/fIwZSvVCe9/8XMHTFlpUpBXMAIAAAAACuDPjJgvvbBYhbLpjMiWUCsVppiYrhvR+yMysNPN8cZAVsACAAAAAAKpADjc4bzIZMi9Q/+oe0EMRJHYQt6dlo1x/lRquagqkAAzYyAH0AAAAFZAAgAAAAAL8YB6VAqGBiWD4CBv16IBscg5J7VQCTZu87n6pj+86KBXMAIAAAAAAmxm8e68geeyAdUjSMWBHzUjneVB0pG9TBXIoE6467hAVsACAAAAAAV76JZAlYpgC/Zl8awx2ArCg1uuyy2XVTSkp0wUMi/7UAAzYzAH0AAAAFZAAgAAAAAL4yLkCTV5Dmxa5toBu4JT8ge/cITAaURIOuFuOtFUkeBXMAIAAAAAAXoFNQOMGkAj7qEJP0wQafmFSXgWGeorDVbwyOxWLIsgVsACAAAAAAc4Un6dtIFe+AQ+RSfNWs3q63RTHhmyc+5GKRRdpWRv8AAzY0AH0AAAAFZAAgAAAAAEU8DoUp46YtYjNFS9kNXwdYxQ9IW27vCTb+VcqqfnKNBXMAIAAAAADe7vBOgYReE8X78k5ARuUnv4GmzPZzg6SbConf4L2G3wVsACAAAAAA78YHWVkp6HbZ0zS4UL2z/2pj9vPDcMDt7zTv6NcRsVsAAzY1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzY2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzY3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzY4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzY5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzcwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzcxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAABbW4A////////7/8BbXgA////////738A", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.yml new file mode 100644 index 0000000000..8a3eb7d17f --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Aggregate.yml @@ -0,0 +1,326 @@ +description: fle2v2-Rangev2-Double-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedDoubleNoPrecision": { $gt: { $numberDouble: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedDoubleNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.json new file mode 100644 index 0000000000..3bffc95191 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.json @@ -0,0 +1,1018 @@ +{ + "description": "fle2v2-Rangev2-Double-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoubleNoPrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberInt": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.yml new file mode 100644 index 0000000000..8b6652bf17 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Correctness.yml @@ -0,0 +1,332 @@ +description: fle2v2-Rangev2-Double-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $in: [$numberDouble: "0.0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gte: { $numberDouble: "0.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "1.0" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lt: { $numberDouble: "1.0" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $lte: { $numberDouble: "1.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0" } } }, + } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "0.0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoubleNoPrecision: { $in: [$numberDouble: "0.0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Int" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDoubleNoPrecision: { $numberInt: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Int" + operations: + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gte: { $numberInt: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.json new file mode 100644 index 0000000000..ac82c52b14 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.json @@ -0,0 +1,795 @@ +{ + "description": "fle2v2-Rangev2-Double-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.yml new file mode 100644 index 0000000000..ff74f89790 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Delete.yml @@ -0,0 +1,259 @@ +description: fle2v2-Rangev2-Double-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedDoubleNoPrecision": { $gt: { $numberDouble: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedDoubleNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.json new file mode 100644 index 0000000000..ce1be99a3a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.json @@ -0,0 +1,1199 @@ +{ + "description": "fle2v2-Rangev2-Double-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.yml new file mode 100644 index 0000000000..636390352a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-FindOneAndUpdate.yml @@ -0,0 +1,325 @@ +description: fle2v2-Rangev2-Double-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDoubleNoPrecision": { $numberDouble: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedDoubleNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedDoubleNoPrecision": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.json new file mode 100644 index 0000000000..cac8bcafea --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.json @@ -0,0 +1,1186 @@ +{ + "description": "fle2v2-Rangev2-Double-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.yml new file mode 100644 index 0000000000..c2d360f4c4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-InsertFind.yml @@ -0,0 +1,320 @@ +description: fle2v2-Rangev2-Double-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedDoubleNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.json new file mode 100644 index 0000000000..938657c91c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.json @@ -0,0 +1,1205 @@ +{ + "description": "fle2v2-Rangev2-Double-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoubleNoPrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoubleNoPrecision": { + "$numberDouble": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoubleNoPrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoubleNoPrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoubleNoPrecision": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.yml new file mode 100644 index 0000000000..6a1a637293 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Double-Update.yml @@ -0,0 +1,332 @@ +description: fle2v2-Rangev2-Double-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoubleNoPrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoubleNoPrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDoubleNoPrecision: { $numberDouble: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedDoubleNoPrecision: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDoubleNoPrecision": { $numberDouble: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoubleNoPrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoubleNoPrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedDoubleNoPrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedDoubleNoPrecision": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.json new file mode 100644 index 0000000000..2046630a7b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.json @@ -0,0 +1,643 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.yml new file mode 100644 index 0000000000..4a088da4fc --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Aggregate.yml @@ -0,0 +1,231 @@ +description: fle2v2-Rangev2-DoublePrecision-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedDoublePrecision": { $gt: { $numberDouble: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedDoublePrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.json new file mode 100644 index 0000000000..939a12c9f8 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.json @@ -0,0 +1,1418 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "-1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 200, + "encryptedDoublePrecision": { + "$numberDouble": "200.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "1.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lte": { + "$numberDouble": "1.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$lt": { + "$numberDouble": "0.0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "200.0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range max" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0.0" + }, + "$lt": { + "$numberDouble": "2.0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$gte": { + "$numberDouble": "0.0" + }, + "$lte": { + "$numberDouble": "200.0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + }, + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1.0" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedDoublePrecision": { + "$in": [ + { + "$numberDouble": "0.0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0.0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Int", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberInt": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Int", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.yml new file mode 100644 index 0000000000..d398c47085 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Correctness.yml @@ -0,0 +1,457 @@ +description: fle2v2-Rangev2-DoublePrecision-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedDoublePrecision: { $numberDouble: "0.0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1.0" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } } } + object: "coll" + expectError: + errorContains: must be less than the range max + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "0.0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedDoublePrecision: { $numberDouble: "1.0" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" }, $lte: { $numberDouble: "200.0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $in: [$numberDouble: "0.0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Insert out of range" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "-1" } } + object: "coll" + expectError: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll" + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedDoublePrecision: { $numberDouble: "200.0" } } + object: "coll" + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc200] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "1.0" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "1.0" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lte: { $numberDouble: "1.0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $lt: { $numberDouble: "0.0" } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "200.0" } } } } + object: "coll" + expectError: + errorContains: must be less than the range max + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $gt: { $numberDouble: "0.0" }, $lt: { $numberDouble: "2.0" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "0.0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $numberDouble: "1.0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { + $match: + { encryptedDoublePrecision: { $gte: { $numberDouble: "0.0" }, $lte: { $numberDouble: "200.0" } } }, + } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedDoublePrecision: { $in: [$numberDouble: "0.0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Int" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedDoublePrecision: { $numberInt: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Int" + operations: + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gte: { $numberInt: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.json new file mode 100644 index 0000000000..db615d6fe3 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.json @@ -0,0 +1,537 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "DQYKAAADcGF5bG9hZACiCQAABGcAjgkAAAMwAH0AAAAFZAAgAAAAAHdJ2Vnb4MMzqVYVssjSdDy8XU4GVzMTfGifGETgQ2mYBXMAIAAAAAD7cFfKJGIXo6PjyeX2ria02CckW7dWFDoY/3FyBdm1NQVsACAAAAAAhEPSNv4M023A3hzNFuy83+hIKuZ2mKRY954N++aEOBUAAzEAfQAAAAVkACAAAAAAlmvfDrZoydUet4eCVMq7z6a58Ea+1HLJOWxN5lNcrWEFcwAgAAAAAEBo5AWZyC41b9ayjWNQSL4iYEAIwR/JG+ssN8bdoK9RBWwAIAAAAACEndE0SLxFSElOrNnqeX0EPmgDio3udZjVREy4JLS3sQADMgB9AAAABWQAIAAAAABbiLaoxAA6rinMJw1hC8ZUiq6UU1AQaPFn/py/Y06WuQVzACAAAAAAhtDasFkvYE7SCNu1je/hxdE9TJtAvvH3NtdEbKzNbCUFbAAgAAAAAIGepU1RSCF8sWODHEpKglsoqw3VBBH4a/URGxgGzbq2AAMzAH0AAAAFZAAgAAAAALORWwSr+tYNxcil2KIGSbNhTHvcPbdj+rLVQNx21S/KBXMAIAAAAAD6diZBkPEJ1cQy06LAxdbNK8Nlxbb44fH4Wk3Y3260nQVsACAAAAAA1eYAZBFHlDiaDAljWi8blGQ2nvvZa5AO5doeo0SFZsgAAzQAfQAAAAVkACAAAAAAG5XMK96PjClNlUvg82j4pMY1YxsznZfj4uNweD394FoFcwAgAAAAAKHgQLdGJHkrfFg9nB93Ac+3VgBw6aU44MTkKIQ91dZoBWwAIAAAAAAPxXmi+SDJ+40A0KdwfRczexlZQrHjIA+D3oUB0EY9tAADNQB9AAAABWQAIAAAAAA6M++b9I0YFemmWBAWAE3glu2Ah3Ta1FBxAQEIWS0toAVzACAAAAAANXYTqPf1Y6X3Ns6YQIX0C3FKCyWUo+Kk+fNcQvc0WSoFbAAgAAAAAA+uJUw1ICYgyeygSRe206VTWVtUnhdci3iHbyP5YtEVAAM2AH0AAAAFZAAgAAAAAKl8bV1riH/uyJ+X0HHd3+18k2cJl2dQFXCdoagutFcaBXMAIAAAAABm8F2Ew9f0VOABdcF+lP0Bi+zWvEUPniWgrxPq/Sx3uwVsACAAAAAAJfFErjZ6BPhsw5LjJLqNtKDLJ4zV0eIZppQpd9b0wZoAAzcAfQAAAAVkACAAAAAAsYZD8JEP6kYsPncFnNZwJxhu4YtUTKPNcjHtv67H+rYFcwAgAAAAAI4LqZcRkvbs/2F62Flu0pixNcor4WmBD0DHGaf039wLBWwAIAAAAAD4wUR3xd9lKltcqqo8LYvdMQWzCRobkV/ppKB/yn5dUgADOAB9AAAABWQAIAAAAAC0vdAi+dmoIXvZ5LqUqvyKV9/tHqSI2SWiSJO5pTnA2wVzACAAAAAAS2qvf9fvfVUH5WtsVxjxmskpGjYTQV34LwvQQw1y9wIFbAAgAAAAAE0+FKuK7HxbypvCeEJzMTcjOWE0ScYOlTBMUNlIv55hAAM5AH0AAAAFZAAgAAAAAH31lb/srBcrOXkzddCwAnclsR5/3QijEVgECs2JjOWBBXMAIAAAAABg7+prDT73YcCvLE5QbuIrqGcjLc5pQD2Miq0d29yrxgVsACAAAAAAetRiPwDSFWBzpWSWkOKWM6fKStRJ8SyObnpc79ux8p0AAzEwAH0AAAAFZAAgAAAAAOK8brUuc2onBNDRtfYMR736dHj4dQqXod8JG7tAMTsDBXMAIAAAAAAW6SrGAL6Bx0s7ZlsYULFfOAiYIGhEWu6md3r+Rk40awVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAAAzExAH0AAAAFZAAgAAAAAFdthRhe2Q8CvxGIhjTJZv0Lk97GkHciTPxZ/mckLoNaBXMAIAAAAAAqOxsAr23LOVB0DIHbPf9UDJJRFXY2YoKbjhRqw5psbQVsACAAAAAA0G2GD8ZQjDBntjLpW4rqwKRS6HiUjL03g1N6chANozcAAzEyAH0AAAAFZAAgAAAAAMWymwwbvIeMqmnKWWifUqoCxOsdpnonM2qdLPyjqJO/BXMAIAAAAAB6IDmmpUhBD2zpRj8/y/kmOSXcjuIU14sNh6GKSsg2uwVsACAAAAAAWMFPNOk3EMSQDS9JGPSMIQP0oNGVugxXKKUrIPPlhHgAAzEzAH0AAAAFZAAgAAAAAPcLmtq+V1e+MRlZ7NHq1+mrRVBQje5zj685ZvdsfKvSBXMAIAAAAABdHz/3w2k5km97QN9m7oLFYJaVJneNlMboIlz5yUASQAVsACAAAAAAWbp8JVJnx8fEVAJFa7WMfMa7wXeP5M3C8MX20J/i9n0AAzE0AH0AAAAFZAAgAAAAAJaRYmo8zqI2BEUzdSwp4tVRpPmVWsfydkYN3UHh6TMuBXMAIAAAAAAeD6mDnQeLlbC9i0sVgE8+RH6y+e94OJQ0tJ0PvblVSgVsACAAAAAAWp4jvretbDEsqEMzP/WLTnwOiJwCtfrCiB6m8k+yEMoAAzE1AH0AAAAFZAAgAAAAAAZZ538coNPwyRjhEwr5P8Xw32oWOJF+R+nfCGgy2qO3BXMAIAAAAACOPLnJlKwGNPDBReRKnHfteq0wFb3ezhrc7BVXs8RUHwVsACAAAAAA+lGesNk3+SyB/60rSvdQ2aN2vfJPR7llJVhufGTNhHkAAzE2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzE3AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzE4AH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYAAQAAAAFtbgAAAAAAAAAAAAFteAAAAAAAAABpQAA=", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.yml new file mode 100644 index 0000000000..83970944a9 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Delete.yml @@ -0,0 +1,213 @@ +description: fle2v2-Rangev2-DoublePrecision-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedDoublePrecision": { $gt: { $numberDouble: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedDoublePrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json new file mode 100644 index 0000000000..a8f87596e8 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.json @@ -0,0 +1,647 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml new file mode 100644 index 0000000000..7dc872fd3e --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate.yml @@ -0,0 +1,230 @@ +description: fle2v2-Rangev2-DoublePrecision-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDoublePrecision": { $numberDouble: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedDoublePrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedDoublePrecision": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.json new file mode 100644 index 0000000000..5e4aa5f1e0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.json @@ -0,0 +1,634 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.yml new file mode 100644 index 0000000000..816f876363 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-InsertFind.yml @@ -0,0 +1,225 @@ +description: fle2v2-Rangev2-DoublePrecision-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedDoublePrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.json new file mode 100644 index 0000000000..10cae6be89 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.json @@ -0,0 +1,653 @@ +{ + "description": "fle2v2-Rangev2-DoublePrecision-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedDoublePrecision": { + "$numberDouble": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedDoublePrecision": { + "$numberDouble": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedDoublePrecision": { + "$gt": { + "$numberDouble": "0" + } + } + }, + "update": { + "$set": { + "encryptedDoublePrecision": { + "$numberDouble": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedDoublePrecision": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedDoublePrecision": { + "$gt": { + "$binary": { + "base64": "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", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedDoublePrecision": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberDouble": "0.0" + }, + "max": { + "$numberDouble": "200.0" + }, + "precision": { + "$numberInt": "2" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.yml new file mode 100644 index 0000000000..7629bf7402 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-DoublePrecision-Update.yml @@ -0,0 +1,238 @@ +description: fle2v2-Rangev2-DoublePrecision-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedDoublePrecision", + "bsonType": "double", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberDouble": "0.0" }, + "max": { "$numberDouble": "200.0" }, + "precision": { "$numberInt": "2" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedDoublePrecision: { $numberDouble: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedDoublePrecision: { $numberDouble: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedDoublePrecision: { $gt: { $numberDouble: "0" } } } + update: { "$set": { "encryptedDoublePrecision": { $numberDouble: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedDoublePrecision": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedDoublePrecision": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedDoublePrecision": + { + "$gt": + { + "$binary": + { + "base64": "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", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedDoublePrecision": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.json new file mode 100644 index 0000000000..77a8f43e9c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.json @@ -0,0 +1,547 @@ +{ + "description": "fle2v2-Rangev2-Int-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.yml new file mode 100644 index 0000000000..c201d29077 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Aggregate.yml @@ -0,0 +1,216 @@ +description: fle2v2-Rangev2-Int-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedInt": { $gt: { $numberInt: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.json new file mode 100644 index 0000000000..dde5ec371b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.json @@ -0,0 +1,1412 @@ +{ + "description": "fle2v2-Rangev2-Int-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "-1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 200, + "encryptedInt": { + "$numberInt": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "1" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lte": { + "$numberInt": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$lt": { + "$numberInt": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "200" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + }, + "$lt": { + "$numberInt": "2" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$numberInt": "1" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$gte": { + "$numberInt": "0" + }, + "$lte": { + "$numberInt": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + }, + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedInt": { + "$in": [ + { + "$numberInt": "0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.yml new file mode 100644 index 0000000000..6a1ec1cb4a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Correctness.yml @@ -0,0 +1,452 @@ +description: fle2v2-Rangev2-Int-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberInt: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "1" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "1" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $lte: { $numberInt: "1" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $lt: { $numberInt: "0" } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "200" } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedInt: { $numberInt: "1" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberInt: "0" }, $lte: { $numberInt: "200" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $in: [$numberInt: "0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Insert out of range" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "-1" } } + object: "coll" + expectError: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll" + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedInt: { $numberInt: "200" } } + object: "coll" + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc200] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: { $numberInt: "0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "1" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "1" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lte: { $numberInt: "1" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $lt: { $numberInt: "0" } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "200" } } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gt: { $numberInt: "0" }, $lt: { $numberInt: "2" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $numberInt: "1" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $gte: { $numberInt: "0" }, $lte: { $numberInt: "200" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedInt: { $in: [$numberInt: "0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Double" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Double" + operations: + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberDouble: "0" } } } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.json new file mode 100644 index 0000000000..1c54c6e0f6 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.json @@ -0,0 +1,483 @@ +{ + "description": "fle2v2-Rangev2-Int-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.yml new file mode 100644 index 0000000000..7425b97138 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Delete.yml @@ -0,0 +1,205 @@ +description: fle2v2-Rangev2-Int-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedInt": { $gt: { $numberInt: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.json new file mode 100644 index 0000000000..265a0c6f0d --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.json @@ -0,0 +1,551 @@ +{ + "description": "fle2v2-Rangev2-Int-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.yml new file mode 100644 index 0000000000..dee2befd8e --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-FindOneAndUpdate.yml @@ -0,0 +1,215 @@ +description: fle2v2-Rangev2-Int-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + update: { "$set": { "encryptedInt": { $numberInt: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedInt": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.json new file mode 100644 index 0000000000..08b6d2c2a5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.json @@ -0,0 +1,538 @@ +{ + "description": "fle2v2-Rangev2-Int-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.yml new file mode 100644 index 0000000000..d0163c86e1 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-InsertFind.yml @@ -0,0 +1,210 @@ +description: fle2v2-Rangev2-Int-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.json new file mode 100644 index 0000000000..9f28f768bb --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.json @@ -0,0 +1,557 @@ +{ + "description": "fle2v2-Rangev2-Int-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": { + "$numberInt": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedInt": { + "$gt": { + "$numberInt": "0" + } + } + }, + "update": { + "$set": { + "encryptedInt": { + "$numberInt": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedInt": { + "$gt": { + "$binary": { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedInt": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.yml new file mode 100644 index 0000000000..4e7ee9c64f --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Int-Update.yml @@ -0,0 +1,223 @@ +description: fle2v2-Rangev2-Int-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedInt: { $numberInt: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedInt: { $numberInt: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedInt: { $gt: { $numberInt: "0" } } } + update: { "$set": { "encryptedInt": { $numberInt: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedInt": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedInt": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedInt": + { + "$gt": + { + "$binary": + { + "base64": "DW0FAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAAQbW4AAAAAABBteADIAAAAAA==", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedInt": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.json new file mode 100644 index 0000000000..01ff139a55 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.json @@ -0,0 +1,547 @@ +{ + "description": "fle2v2-Rangev2-Long-Aggregate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "default", + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.yml new file mode 100644 index 0000000000..3ce63b0aa5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Aggregate.yml @@ -0,0 +1,216 @@ +description: fle2v2-Rangev2-Long-Aggregate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: aggregate + arguments: + pipeline: [$match: { "encryptedLong": { $gt: { $numberLong: "0" } } }] + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + [ + "$match": + { + "encryptedLong": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + }, + ] + cursor: {} + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: aggregate diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.json new file mode 100644 index 0000000000..cc5388b1f0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.json @@ -0,0 +1,1412 @@ +{ + "description": "fle2v2-Rangev2-Long-Correctness", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Find with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Find with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Find with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Find with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Find with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Insert out of range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "-1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "value must be greater than or equal to the minimum value" + } + } + ] + }, + { + "description": "Insert min and max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 200, + "encryptedLong": { + "$numberLong": "200" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $gt with no results", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [] + } + ] + }, + { + "description": "Aggregate with $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "1" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lte", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lte": { + "$numberLong": "1" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $lt below min", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$lt": { + "$numberLong": "0" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be greater than the range minimum" + } + } + ] + }, + { + "description": "Aggregate with $gt above max", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "200" + } + } + } + } + ] + }, + "object": "coll", + "expectError": { + "errorContains": "must be less than the range maximum" + } + } + ] + }, + { + "description": "Aggregate with $gt and $lt", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + }, + "$lt": { + "$numberLong": "2" + } + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with equality", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "0" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$numberLong": "1" + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with full range", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$gte": { + "$numberLong": "0" + }, + "$lte": { + "$numberLong": "200" + } + } + } + }, + { + "$sort": { + "_id": 1 + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + }, + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $in", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "encryptedLong": { + "$in": [ + { + "$numberLong": "0" + } + ] + } + } + } + ] + }, + "object": "coll", + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + ] + } + ] + }, + { + "description": "Wrong type: Insert Double", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberDouble": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gte": { + "$numberDouble": "0" + } + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.yml new file mode 100644 index 0000000000..65359a679b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Correctness.yml @@ -0,0 +1,451 @@ +description: fle2v2-Rangev2-Long-Correctness +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberLong: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "1" } } } + object: "coll" + expectResult: [] + + - description: "Find with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "1" } } } + object: "coll" + expectResult: [*doc0] + + - description: "Find with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $lte: { $numberLong: "1" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $lt: { $numberLong: "0" } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Find with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "200" } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Find with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "0" } } + object: "coll" + expectResult: [*doc0] + - name: find + arguments: + filter: { encryptedLong: { $numberLong: "1" } } + object: "coll" + expectResult: [*doc1] + + - description: "Find with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberLong: "0" }, $lte: { $numberLong: "200" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Find with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $in: [$numberLong: "0"] } } + object: "coll" + expectResult: [*doc0] + + - description: "Insert out of range" + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "-1" } } + object: "coll" + expectError: + errorContains: value must be greater than or equal to the minimum value + + - description: "Insert min and max" + operations: + - name: insertOne + arguments: + document: *doc0 + object: "coll" + - name: insertOne + arguments: + document: &doc200 { _id: 200, encryptedLong: { $numberLong: "200" } } + object: "coll" + - name: find + arguments: + filter: {} + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectResult: [*doc0, *doc200] + + - description: "Aggregate with $gte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: { $numberLong: "0" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $gt with no results" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "1" } } } } + object: "coll" + expectResult: [] + + - description: "Aggregate with $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "1" } } } } + object: "coll" + expectResult: [*doc0] + + - description: "Aggregate with $lte" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lte: { $numberLong: "1" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $lt below min" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $lt: { $numberLong: "0" } } } } + object: "coll" + expectError: + errorContains: must be greater than the range minimum + + - description: "Aggregate with $gt above max" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "200" } } } } + object: "coll" + expectError: + errorContains: must be less than the range maximum + + - description: "Aggregate with $gt and $lt" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gt: { $numberLong: "0" }, $lt: { $numberLong: "2" } } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with equality" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "0" } } } + object: "coll" + expectResult: [*doc0] + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $numberLong: "1" } } } + object: "coll" + expectResult: [*doc1] + + - description: "Aggregate with full range" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $gte: { $numberLong: "0" }, $lte: { $numberLong: "200" } } } } + # sort so results from range queries are ordered. + - { $sort: { _id: 1 } } + object: "coll" + expectResult: [*doc0, *doc1] + + - description: "Aggregate with $in" + operations: + - name: insertOne + arguments: { document: *doc0 } + object: "coll" + - name: insertOne + arguments: { document: *doc1 } + object: "coll" + - name: aggregate + arguments: + pipeline: + - { $match: { encryptedLong: { $in: [$numberLong: "0"] } } } + object: "coll" + expectResult: [*doc0] + + - description: "Wrong type: Insert Double" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedLong: { $numberDouble: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Double" + operations: + - name: find + arguments: + filter: { encryptedLong: { $gte: { $numberDouble: "0" } } } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.json new file mode 100644 index 0000000000..0a8580110c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.json @@ -0,0 +1,483 @@ +{ + "description": "fle2v2-Rangev2-Long-Delete", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "deleteOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "object": "coll", + "expectResult": { + "deletedCount": 1 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "default", + "deletes": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "delete" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.yml new file mode 100644 index 0000000000..5a828d020c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Delete.yml @@ -0,0 +1,205 @@ +description: fle2v2-Rangev2-Long-Delete +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: deleteOne + arguments: + filter: { "encryptedLong": { $gt: { $numberLong: "0" } } } + object: "coll" + expectResult: + deletedCount: 1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": + [ + "$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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + delete: *collection_name + deletes: + [ + { + "q": + { + "encryptedLong": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + }, + "limit": 1, + }, + ] + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: delete diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.json new file mode 100644 index 0000000000..f014e1a4ac --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.json @@ -0,0 +1,551 @@ +{ + "description": "fle2v2-Rangev2-Long-FindOneAndUpdate", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + }, + "returnDocument": "Before" + }, + "object": "coll", + "expectResult": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "default", + "query": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "findAndModify" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.yml new file mode 100644 index 0000000000..27a061e222 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-FindOneAndUpdate.yml @@ -0,0 +1,215 @@ +description: fle2v2-Rangev2-Long-FindOneAndUpdate +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: findOneAndUpdate + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + update: { "$set": { "encryptedLong": { $numberLong: "2" } } } + returnDocument: Before + object: "coll" + expectResult: *doc1 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + findAndModify: *collection_name + query: + { + "encryptedLong": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + update: { "$set": { "encryptedLong": { $$type: "binData" } } } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: findAndModify diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.json new file mode 100644 index 0000000000..2896df0032 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.json @@ -0,0 +1,538 @@ +{ + "description": "fle2v2-Rangev2-Long-InsertFind", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.yml new file mode 100644 index 0000000000..2908305f4c --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-InsertFind.yml @@ -0,0 +1,210 @@ +description: fle2v2-Rangev2-Long-InsertFind +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: &doc1 { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: find + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + object: "coll" + expectResult: [*doc1] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: + "encryptedLong": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + } + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.json new file mode 100644 index 0000000000..4f8cd1d80d --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.json @@ -0,0 +1,557 @@ +{ + "description": "fle2v2-Rangev2-Long-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedLong", + "bsonType": "long", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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.", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedLong": { + "$numberLong": "0" + } + } + }, + "object": "coll" + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedLong": { + "$numberLong": "1" + } + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedLong": { + "$gt": { + "$numberLong": "0" + } + } + }, + "update": { + "$set": { + "encryptedLong": { + "$numberLong": "2" + } + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 0, + "encryptedLong": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$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": "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" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "ordered": true, + "updates": [ + { + "q": { + "encryptedLong": { + "$gt": { + "$binary": { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedLong": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberInt": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberLong": "0" + }, + "max": { + "$numberLong": "200" + } + } + } + ] + } + } + }, + "$db": "default" + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.yml new file mode 100644 index 0000000000..6c8c6a5f53 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-Long-Update.yml @@ -0,0 +1,223 @@ +description: fle2v2-Rangev2-Long-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" # Requires 8.0.0-rc14 for SERVER-91889. + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &encrypted_fields { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedLong", + "bsonType": "long", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberInt": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberLong": "0" }, + "max": { "$numberLong": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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." + operations: + - name: insertOne + arguments: + document: { _id: 0, encryptedLong: { $numberLong: "0" } } + object: "coll" + - name: insertOne + arguments: + document: { _id: 1, encryptedLong: { $numberLong: "1" } } + object: "coll" + - name: updateOne + arguments: + filter: { encryptedLong: { $gt: { $numberLong: "0" } } } + update: { "$set": { "encryptedLong": { $numberLong: "2" } } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 0, + "encryptedLong": { $$type: "binData" }, + "__safeContent__": + [ + "$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": "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" }, + ], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 0, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedLong": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + default.default: + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: "default" + ordered: true + updates: + - q: + { + "encryptedLong": + { + "$gt": + { + "$binary": + { + "base64": "DXUFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAALGGQ/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/XU9aCxAkp1IwAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAEAAAASbW4AAAAAAAAAAAASbXgAyAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedLong": { $$type: "binData" } } } + multi: false + upsert: false + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + "$db": "default" + + commandName: update diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.json b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.json new file mode 100644 index 0000000000..03681947ce --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.json @@ -0,0 +1,204 @@ +{ + "description": "fle2v2-Rangev2-WrongType", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "8.0.0", + "maxServerVersion": "8.99.99", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "range", + "contention": { + "$numberLong": "0" + }, + "trimFactor": { + "$numberLong": "1" + }, + "sparsity": { + "$numberLong": "1" + }, + "min": { + "$numberInt": "0" + }, + "max": { + "$numberInt": "200" + } + } + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberDouble": "0" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot encrypt element" + } + } + ] + }, + { + "description": "Wrong type: Find Double", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 0, + "encryptedInt": { + "$numberInt": "0" + } + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "encryptedInt": { + "$gte": { + "$numberDouble": "0" + } + } + }, + "sort": { + "_id": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "field type is not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.yml b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.yml new file mode 100644 index 0000000000..2afe144f4a --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Rangev2-WrongType.yml @@ -0,0 +1,107 @@ +description: fle2v2-Rangev2-WrongType +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "8.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + maxServerVersion: "8.99.99" + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + { + "fields": + [ + { + "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, + "path": "encryptedInt", + "bsonType": "int", + "queries": + { + "queryType": "range", + "contention": { "$numberLong": "0" }, + "trimFactor": { "$numberLong": "1" }, + "sparsity": { "$numberLong": "1" }, + "min": { "$numberInt": "0" }, + "max": { "$numberInt": "200" }, + }, + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberDouble: "0" } } } + object: "coll" + expectError: + # Expect an error from mongocryptd. + errorContains: "Cannot encrypt element" + + - description: "Wrong type: Find Double" + operations: + - name: insertOne + arguments: { document: { _id: 0, encryptedInt: { $numberInt: "0" } } } + object: "coll" + - name: find + arguments: + filter: { encryptedInt: { $gte: { $numberDouble: "0" } } } + # sort so results from range queries are ordered. + sort: { _id: 1 } + object: "coll" + expectError: + # expect an error from libmongocrypt. + errorContains: "field type is not supported" diff --git a/source/client-side-encryption/tests/unified/fle2v2-Update.json b/source/client-side-encryption/tests/unified/fle2v2-Update.json new file mode 100644 index 0000000000..9c39c4d83d --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Update.json @@ -0,0 +1,633 @@ +{ + "description": "fle2v2-Update", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "encryptedFields": { + "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" + } + ] + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "foo": "bar" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "foo": "bar", + "__safeContent__": [ + { + "$binary": { + "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "foo": "bar" + } + }, + "multi": false, + "upsert": false + } + ], + "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" + } + ] + } + } + } + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "Update can modify an FLE2 indexed field", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedIndexed": "value123" + } + }, + "object": "coll" + }, + { + "name": "updateOne", + "arguments": { + "filter": { + "encryptedIndexed": "value123" + }, + "update": { + "$set": { + "encryptedIndexed": "value456" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "encryptedIndexed": "value456" + } + ] + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedIndexed": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", + "subType": "00" + } + } + ] + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "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" + } + ] + } + } + } + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encryptedIndexed": { + "$eq": { + "$binary": { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encryptedIndexed": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "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" + } + ] + } + } + } + }, + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": { + "$eq": 1 + } + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-Update.yml b/source/client-side-encryption/tests/unified/fle2v2-Update.yml new file mode 100644 index 0000000000..3451a4fc7b --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-Update.yml @@ -0,0 +1,281 @@ +description: fle2v2-Update +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + encryptedFields: + &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", + }, + ], + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: { "_id": 1, "encryptedIndexed": "value123" } + object: "coll" + - name: updateOne + arguments: + filter: { "encryptedIndexed": "value123" } + update: { "$set": { "foo": "bar" } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { "$$type": "binData" }, + "foo": "bar", + "__safeContent__": + ["$binary": { "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "foo": "bar" } } + multi: false + upsert: false + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: update + - description: "Update can modify an FLE2 indexed field" + operations: + - name: insertOne + arguments: + document: { "_id": 1, "encryptedIndexed": "value123" } + object: "coll" + - name: updateOne + arguments: + filter: { "encryptedIndexed": "value123" } + update: { "$set": { "encryptedIndexed": "value456" } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + arguments: + filter: { "_id": 1 } + object: "coll" + expectResult: ["encryptedIndexed": "value456"] + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + "_id": 1, + "encryptedIndexed": { "$$type": "binData" }, + "__safeContent__": + ["$binary": { "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", "subType": "00" }], + } + expectEvents: + - client: "client0" + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { "_id": 1, "encryptedIndexed": { $$type: "binData" } } + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: insert + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + "encryptedIndexed": + { + "$eq": + { + "$binary": + { + "base64": "DIkAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVsACAAAAAAaZ9s3G+4znfxStxeOZwcZy1OhzjMGc5hjmdMN+b/w6kSY20AAAAAAAAAAAAA", + "subType": "06", + }, + }, + }, + } + u: { "$set": { "encryptedIndexed": { "$$type": "binData" } } } + multi: false + upsert: false + ordered: true + encryptionInformation: + type: 1 + schema: + "default.default": + # libmongocrypt applies escCollection and ecocCollection to outgoing command. + escCollection: "enxcol_.default.esc" + ecocCollection: "enxcol_.default.ecoc" + <<: *encrypted_fields + commandName: update + - commandStartedEvent: + command: + find: *collection_name + filter: { "_id": { "$eq": 1 } } + commandName: find diff --git a/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.json b/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.json new file mode 100644 index 0000000000..8d69464400 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.json @@ -0,0 +1,304 @@ +{ + "description": "fle2v2-validatorAndPartialFieldExpression", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "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" + } + ] + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + } + ], + "tests": [ + { + "description": "create with a validator on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + } + ] + }, + { + "description": "create with a validator on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "encryptedIndexed": "foo" + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "collMod with a validator on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + } + } + ] + }, + { + "description": "collMod with a validator on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "encryptedCollection", + "validator": { + "encryptedIndexed": "foo" + } + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "createIndexes", + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "unencrypted_string": "foo" + } + } + ] + } + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "name" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "createIndexes", + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "encryptedIndexed": "foo" + } + } + ] + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.yml b/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.yml new file mode 100644 index 0000000000..c99e51b200 --- /dev/null +++ b/source/client-side-encryption/tests/unified/fle2v2-validatorAndPartialFieldExpression.yml @@ -0,0 +1,178 @@ +description: fle2v2-validatorAndPartialFieldExpression +schemaVersion: "1.25" +runOnRequirements: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: + "7.0.0" + # Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol. + # FLE 2 Encrypted collections are not supported on standalone. + topologies: ["replicaset", "sharded", "load-balanced"] + + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + 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", + }, + ], + } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] +tests: + - description: "create with a validator on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: &database_name "default" + collectionName: "encryptedCollection" + - description: "create with a validator on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + validator: + encryptedIndexed: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" + - description: "collMod with a validator on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: collMod + command: + collMod: "encryptedCollection" + validator: + unencrypted_string: "foo" + - description: "collMod with a validator on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: collMod + command: + collMod: "encryptedCollection" + validator: + encryptedIndexed: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" + - description: "createIndexes with a partialFilterExpression on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: createIndexes + command: + createIndexes: "encryptedCollection" + indexes: + - name: "name" + key: { name: 1 } + partialFilterExpression: + unencrypted_string: "foo" + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: "encryptedCollection" + indexName: name + - description: "createIndexes with a partialFilterExpression on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: createIndexes + command: + createIndexes: "encryptedCollection" + indexes: + - name: "name" + key: { name: 1 } + partialFilterExpression: + encryptedIndexed: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" diff --git a/source/client-side-encryption/tests/unified/gcpKMS.json b/source/client-side-encryption/tests/unified/gcpKMS.json new file mode 100644 index 0000000000..6468b5b6ce --- /dev/null +++ b/source/client-side-encryption/tests/unified/gcpKMS.json @@ -0,0 +1,292 @@ +{ + "description": "gcpKMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_gcp": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "GCP+AAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_gcp": { + "$binary": { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/gcpKMS.yml b/source/client-side-encryption/tests/unified/gcpKMS.yml new file mode 100644 index 0000000000..a20cdd2286 --- /dev/null +++ b/source/client-side-encryption/tests/unified/gcpKMS.yml @@ -0,0 +1,170 @@ +description: gcpKMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + }, + }, + "encrypted_string_kmip_delegated": + { + "encrypt": + { + "keyId": ["$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6"], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string_gcp: "string0" } + object: "coll" + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string_gcp: + { + "$binary": + { + "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + $or: + [ + _id: { $in: ["$binary": { "base64": "GCP+AAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + keyAltNames: { $in: [] }, + ], + } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/getKey.json b/source/client-side-encryption/tests/unified/getKey.json new file mode 100644 index 0000000000..2ea3fe7358 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/getKey.yml b/source/client-side-encryption/tests/unified/getKey.yml new file mode 100644 index 0000000000..f12fc4d5e4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/getKey.yml @@ -0,0 +1,119 @@ +description: getKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - &aws_key_doc + _id: &aws_key_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 + - &local_key_doc + _id: &local_key_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: + # *aws_key_id with first three letters replaced with 'A' (value: "3awsawsawsawsa"). + id: &non_existent_id { $binary: { base64: AAAzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + expectResult: { $$unsetOrMatches: null } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { _id: *non_existent_id } + readConcern: { level: majority } + + - description: get existing AWS data key + operations: + - name: getKey + object: *clientEncryption0 + arguments: + id: *aws_key_id + expectResult: *aws_key_doc + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { _id: *aws_key_id } + readConcern: { level: majority } + + - description: get existing local data key + operations: + - name: getKey + object: *clientEncryption0 + arguments: + id: *local_key_id + expectResult: *local_key_doc + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { _id: *local_key_id } + readConcern: { level: majority } diff --git a/source/client-side-encryption/tests/unified/getKeyByAltName.json b/source/client-side-encryption/tests/unified/getKeyByAltName.json new file mode 100644 index 0000000000..2505abc16e --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/getKeyByAltName.yml b/source/client-side-encryption/tests/unified/getKeyByAltName.yml new file mode 100644 index 0000000000..ca8c33660b --- /dev/null +++ b/source/client-side-encryption/tests/unified/getKeyByAltName.yml @@ -0,0 +1,118 @@ +description: getKeyByAltName + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - &aws_key_doc + _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 + - &local_key_doc + _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: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: does_not_exist } + readConcern: { level: majority } + + - description: get existing AWS data key + operations: + - name: getKeyByAltName + object: *clientEncryption0 + arguments: + keyAltName: aws_key + expectResult: *aws_key_doc + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: aws_key } + readConcern: { level: majority } + + - description: get existing local data key + operations: + - name: getKeyByAltName + object: *clientEncryption0 + arguments: + keyAltName: local_key + expectResult: *local_key_doc + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: local_key } + readConcern: { level: majority } diff --git a/source/client-side-encryption/tests/unified/getKeys.json b/source/client-side-encryption/tests/unified/getKeys.json new file mode 100644 index 0000000000..d944712357 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/getKeys.yml b/source/client-side-encryption/tests/unified/getKeys.yml new file mode 100644 index 0000000000..f396439431 --- /dev/null +++ b/source/client-side-encryption/tests/unified/getKeys.yml @@ -0,0 +1,122 @@ +description: getKeys + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: getKeys with zero key documents + operations: + - name: getKeys + object: *clientEncryption0 + expectResult: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + 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: *database0Name + command: + find: *collection0Name + 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: + # Cannot expect deterministic order of results, so only assert that + # exactly two key documents are returned. + - _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: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } diff --git a/source/client-side-encryption/tests/unified/getMore.json b/source/client-side-encryption/tests/unified/getMore.json new file mode 100644 index 0000000000..adaa59b01e --- /dev/null +++ b/source/client-side-encryption/tests/unified/getMore.json @@ -0,0 +1,321 @@ +{ + "description": "getMore", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "getMore with encryption", + "operations": [ + { + "name": "find", + "arguments": { + "batchSize": 2, + "filter": {} + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + }, + { + "_id": 2, + "encrypted_string": "string1" + }, + { + "_id": 3, + "encrypted_string": "string2" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "batchSize": 2 + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "default", + "batchSize": 2 + }, + "commandName": "getMore" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/getMore.yml b/source/client-side-encryption/tests/unified/getMore.yml new file mode 100644 index 0000000000..74c7d87afc --- /dev/null +++ b/source/client-side-encryption/tests/unified/getMore.yml @@ -0,0 +1,185 @@ +description: getMore +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + - &doc2_encrypted { + _id: 3, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "getMore with encryption" + operations: + - name: find + arguments: + batchSize: 2 + filter: {} + object: "coll" + expectResult: + - { _id: 1, encrypted_string: "string0" } + - { _id: 2, encrypted_string: "string1" } + - { _id: 3, encrypted_string: "string2" } + outcome: + - documents: + - *doc0_encrypted + - *doc1_encrypted + - *doc2_encrypted + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: *collection_name + batchSize: 2 + commandName: find + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection_name + batchSize: 2 + commandName: getMore diff --git a/source/client-side-encryption/tests/unified/insert.json b/source/client-side-encryption/tests/unified/insert.json new file mode 100644 index 0000000000..23e4e6c2ae --- /dev/null +++ b/source/client-side-encryption/tests/unified/insert.json @@ -0,0 +1,421 @@ +{ + "description": "insert", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "insertOne with encryption", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "object": "coll" + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "insertMany with encryption", + "operations": [ + { + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + }, + { + "_id": 2, + "encrypted_string": "string1" + } + ] + }, + "object": "coll" + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/insert.yml b/source/client-side-encryption/tests/unified/insert.yml new file mode 100644 index 0000000000..62e8be065e --- /dev/null +++ b/source/client-side-encryption/tests/unified/insert.yml @@ -0,0 +1,220 @@ +description: insert +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "insertOne with encryption" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + object: "coll" + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - description: "insertMany with encryption" + operations: + - name: insertMany + arguments: + documents: + - *doc0 + - { _id: 2, encrypted_string: "string1" } + object: "coll" + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - *doc0_encrypted + - &doc1_encrypted { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + - *doc1_encrypted + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/keyAltName.json b/source/client-side-encryption/tests/unified/keyAltName.json new file mode 100644 index 0000000000..826f43df22 --- /dev/null +++ b/source/client-side-encryption/tests/unified/keyAltName.json @@ -0,0 +1,299 @@ +{ + "description": "keyAltName", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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 with encryption using key alt name", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_w_altname": "string0", + "altname": "altname" + } + }, + "object": "coll" + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_w_altname": { + "$$type": "binData" + }, + "altname": "altname" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [] + } + }, + { + "keyAltNames": { + "$in": [ + "altname" + ] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_w_altname": { + "$$type": "binData" + }, + "altname": "altname" + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Replace with key alt name fails", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "encrypted_w_altname": "string0" + } + }, + "upsert": true + }, + "object": "coll", + "expectError": { + "errorContains": "A non-static (JSONPointer) keyId is not supported" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/keyAltName.yml b/source/client-side-encryption/tests/unified/keyAltName.yml new file mode 100644 index 0000000000..1acf6cc29b --- /dev/null +++ b/source/client-side-encryption/tests/unified/keyAltName.yml @@ -0,0 +1,159 @@ +description: keyAltName +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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 with encryption using key alt name" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_w_altname: "string0", altname: "altname" } + object: "coll" + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - &doc0_encrypted { _id: 1, encrypted_w_altname: { $$type: "binData" }, altname: "altname" } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: { $or: [_id: { $in: [] }, keyAltNames: { $in: ["altname"] }] } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - description: "Replace with key alt name fails" + operations: + - name: updateOne + arguments: + filter: {} + update: { $set: { encrypted_w_altname: "string0" } } + upsert: true + object: "coll" + expectError: + errorContains: "A non-static (JSONPointer) keyId is not supported" + outcome: + - documents: [] + collectionName: *collection_name + databaseName: *database_name diff --git a/source/client-side-encryption/tests/unified/keyCache.json b/source/client-side-encryption/tests/unified/keyCache.json new file mode 100644 index 0000000000..a39701e286 --- /dev/null +++ b/source/client-side-encryption/tests/unified/keyCache.json @@ -0,0 +1,198 @@ +{ + "description": "keyCache-explicit", + "schemaVersion": "1.22", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "OCTP9uKPPmvuqpHlqq83gPk4U6rUPxKVRRyVtrjFmVjdoa4Xzm1SzUbr7aIhNI42czkUBmrCtZKF31eaaJnxEBkqf0RFukA9Mo3NEHQWgAQ2cn9duOcRbaFUQo2z0/rB" + } + }, + "keyExpirationMS": 1 + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "a+YWzdygTAG62/cNUkqZiQ==", + "subType": "04" + } + }, + "keyAltNames": [], + "keyMaterial": { + "$binary": { + "base64": "iocBkhO3YBokiJ+FtxDTS71/qKXQ7tSWhWbcnFTXBcMjarsepvALeJ5li+SdUd9ePuatjidxAdMo7vh1V2ZESLMkQWdpPJ9PaJjA67gKQKbbbB4Ik5F2uKjULvrMBnFNVRMup4JNUwWFQJpqbfMveXnUVcD06+pUpAkml/f+DSXrV3e5rxciiNVtz03dAG8wJrsKsFXWj6vTjFhsfknyBA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "decrypt, wait, and decrypt again", + "operations": [ + { + "name": "decrypt", + "object": "clientEncryption0", + "arguments": { + "value": { + "$binary": { + "base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", + "subType": "06" + } + } + }, + "expectResult": "foobar" + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 50 + } + }, + { + "name": "decrypt", + "object": "clientEncryption0", + "arguments": { + "value": { + "$binary": { + "base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", + "subType": "06" + } + } + }, + "expectResult": "foobar" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "a+YWzdygTAG62/cNUkqZiQ==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "a+YWzdygTAG62/cNUkqZiQ==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/keyCache.yml b/source/client-side-encryption/tests/unified/keyCache.yml new file mode 100644 index 0000000000..4166e5e6a7 --- /dev/null +++ b/source/client-side-encryption/tests/unified/keyCache.yml @@ -0,0 +1,117 @@ +description: keyCache-explicit + +schemaVersion: "1.22" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "local": + { + key: "OCTP9uKPPmvuqpHlqq83gPk4U6rUPxKVRRyVtrjFmVjdoa4Xzm1SzUbr7aIhNI42czkUBmrCtZKF31eaaJnxEBkqf0RFukA9Mo3NEHQWgAQ2cn9duOcRbaFUQo2z0/rB", + } + keyExpirationMS: 1 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { + "_id": { "$binary": { "base64": "a+YWzdygTAG62/cNUkqZiQ==", "subType": "04" } }, + "keyAltNames": [], + "keyMaterial": + { + "$binary": + { + "base64": "iocBkhO3YBokiJ+FtxDTS71/qKXQ7tSWhWbcnFTXBcMjarsepvALeJ5li+SdUd9ePuatjidxAdMo7vh1V2ZESLMkQWdpPJ9PaJjA67gKQKbbbB4Ik5F2uKjULvrMBnFNVRMup4JNUwWFQJpqbfMveXnUVcD06+pUpAkml/f+DSXrV3e5rxciiNVtz03dAG8wJrsKsFXWj6vTjFhsfknyBA==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "local" }, + } + +tests: + - description: decrypt, wait, and decrypt again + operations: + - name: decrypt + object: *clientEncryption0 + arguments: + value: + { + "$binary": + { + "base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", + "subType": "06", + }, + } + expectResult: "foobar" + - name: wait + object: testRunner + arguments: + ms: 50 # Wait long enough to account for coarse time resolution on Windows (CDRIVER-4526). + - name: decrypt + object: *clientEncryption0 + arguments: + value: + { + "$binary": + { + "base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", + "subType": "06", + }, + } + expectResult: "foobar" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + { + "_id": { "$in": [{ "$binary": { "base64": "a+YWzdygTAG62/cNUkqZiQ==", "subType": "04" } }] }, + }, + { "keyAltNames": { "$in": [] } }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + { + "_id": { "$in": [{ "$binary": { "base64": "a+YWzdygTAG62/cNUkqZiQ==", "subType": "04" } }] }, + }, + { "keyAltNames": { "$in": [] } }, + ], + } + $db: keyvault + readConcern: { level: "majority" } diff --git a/source/client-side-encryption/tests/unified/kmipKMS.json b/source/client-side-encryption/tests/unified/kmipKMS.json new file mode 100644 index 0000000000..e19f85882b --- /dev/null +++ b/source/client-side-encryption/tests/unified/kmipKMS.json @@ -0,0 +1,415 @@ +{ + "description": "kmipKMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "kmip": { + "endpoint": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + }, + "encrypted_string_kmip_delegated": { + "encrypt": { + "keyId": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_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" + ] + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + }, + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1634220190041" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": "11" + }, + "keyAltNames": [ + "delegated" + ] + } + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using KMIP KMS provider", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "dBHpr8aITfeBQ15grpbLpQ==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + }, + { + "description": "Insert a document with auto encryption using KMIP delegated KMS provider", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string_kmip_delegated": "string0" + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string_kmip_delegated": { + "$binary": { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/kmipKMS.yml b/source/client-side-encryption/tests/unified/kmipKMS.yml new file mode 100644 index 0000000000..30177cc2f5 --- /dev/null +++ b/source/client-side-encryption/tests/unified/kmipKMS.yml @@ -0,0 +1,224 @@ +description: kmipKMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + kmip: { endpoint: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + }, + }, + "encrypted_string_kmip_delegated": + { + "encrypt": + { + "keyId": ["$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6"], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_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"], + }, + { + "_id": { "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6" }, + "keyMaterial": + { + "$binary": + { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1634220190041" } }, + "updateDate": { "$date": { "$numberLong": "1634220190041" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "kmip", "delegated": true, "keyId": "11" }, + "keyAltNames": ["delegated"], + }, + ] +tests: + - description: "Insert a document with auto encryption using KMIP KMS provider" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string_kmip: "string0" } + object: "coll" + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string_kmip: + { + "$binary": + { + "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + $or: + [ + _id: { $in: ["$binary": { "base64": "dBHpr8aITfeBQ15grpbLpQ==", "subType": "04" }] }, + keyAltNames: { $in: [] }, + ], + } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - description: "Insert a document with auto encryption using KMIP delegated KMS provider" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string_kmip_delegated: "string0" } + object: "coll" + outcome: + - documents: + - &doc1_encrypted { + _id: 1, + encrypted_string_kmip_delegated: + { + "$binary": + { + "base64": "AXQR6a/GiE33gUNeYK6Wy6YCkB+8NVfAAjIbvLqyXIg6g1a8tXrym92DPoqmxpcdQyH0vQM3aFNMz7tZwQBimKs29ztZV/LWjM633HhO5ACl9A==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { $or: [_id: { $in: ["$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba6"] }, keyAltNames: { $in: [] }] } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc1_encrypted + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/localKMS.json b/source/client-side-encryption/tests/unified/localKMS.json new file mode 100644 index 0000000000..03b8486484 --- /dev/null +++ b/source/client-side-encryption/tests/unified/localKMS.json @@ -0,0 +1,261 @@ +{ + "description": "localKMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "properties": { + "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" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local" + } + } + ] + } + ], + "tests": [ + { + "description": "Insert a document with auto encryption using local KMS provider", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "object": "coll" + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault" + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/localKMS.yml b/source/client-side-encryption/tests/unified/localKMS.yml new file mode 100644 index 0000000000..19f1dac868 --- /dev/null +++ b/source/client-side-encryption/tests/unified/localKMS.yml @@ -0,0 +1,140 @@ +description: localKMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "properties": + { + "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", + }, + }, + }, + "bsonType": "object", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": { "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }, + "keyMaterial": + { + "$binary": + { + "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "local" }, + }, + ] +tests: + - description: "Insert a document with auto encryption using local KMS provider" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string: "string0", random: "abc" } + object: "coll" + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + $or: + [ + _id: { $in: ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + keyAltNames: { $in: [] }, + ], + } + $db: keyvault + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/localSchema.json b/source/client-side-encryption/tests/unified/localSchema.json new file mode 100644 index 0000000000..685ee39d7c --- /dev/null +++ b/source/client-side-encryption/tests/unified/localSchema.json @@ -0,0 +1,337 @@ +{ + "description": "localSchema", + "schemaVersion": "1.23", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "schemaMap": { + "default.default": { + "properties": { + "encrypted_w_altname": { + "encrypt": { + "keyId": "/altname", + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "OyQRAeK7QlWMr0E2xWapYg==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "random": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "OyQRAeK7QlWMr0E2xWapYg==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + } + }, + "encrypted_string_equivalent": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "OyQRAeK7QlWMr0E2xWapYg==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "client": { + "id": "client1", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "test": { + "bsonType": "string" + } + }, + "bsonType": "object", + "required": [ + "test" + ] + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "encryptedDB", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "encryptedColl", + "database": "encryptedDB", + "collectionName": "default" + } + }, + { + "database": { + "id": "encryptedDB2", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "encryptedColl2", + "database": "encryptedDB2", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "status": 1, + "_id": { + "$binary": { + "base64": "OyQRAeK7QlWMr0E2xWapYg==", + "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" + ] + } + ] + }, + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + } + ], + "tests": [ + { + "description": "A local schema should override", + "operations": [ + { + "object": "encryptedColl", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "object": "encryptedColl", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "commandName": "find", + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "OyQRAeK7QlWMr0E2xWapYg==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "ATskEQHiu0JVjK9BNsVmqWIClDjVEWlpmVRN76InSQuFW2piVbYFkh0QhZCKyx9DdvFBUG+FWluh0kXyhdq3b2Vt/nqNWjXn2y0+JPhrc4W+wQ==", + "subType": "06" + } + } + } + ], + "ordered": true + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "default", + "filter": { + "_id": 1 + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "default", + "databaseName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "ATskEQHiu0JVjK9BNsVmqWIClDjVEWlpmVRN76InSQuFW2piVbYFkh0QhZCKyx9DdvFBUG+FWluh0kXyhdq3b2Vt/nqNWjXn2y0+JPhrc4W+wQ==", + "subType": "06" + } + } + } + ] + } + ] + }, + { + "description": "A local schema with no encryption is an error", + "operations": [ + { + "object": "encryptedColl2", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "expectError": { + "isError": true, + "errorContains": "JSON schema keyword 'required' is only allowed with a remote schema" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/localSchema.yml b/source/client-side-encryption/tests/unified/localSchema.yml new file mode 100644 index 0000000000..e1e70bc6f0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/localSchema.yml @@ -0,0 +1,104 @@ +description: localSchema + +schemaVersion: "1.23" + +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: true + +createEntities: + - client: + id: &client0 client0 + autoEncryptOpts: + schemaMap: + "default.default": {'properties': {'encrypted_w_altname': {'encrypt': {'keyId': '/altname', 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}, 'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'OyQRAeK7QlWMr0E2xWapYg==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}, 'random': {'encrypt': {'keyId': [{'$binary': {'base64': 'OyQRAeK7QlWMr0E2xWapYg==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'}}, 'encrypted_string_equivalent': {'encrypt': {'keyId': [{'$binary': {'base64': 'OyQRAeK7QlWMr0E2xWapYg==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: [commandStartedEvent] + - client: + id: &client1 client1 + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { "properties": { "test": { "bsonType": "string" } }, "bsonType": "object", "required": ["test"] } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: [commandStartedEvent] + - database: + id: &encryptedDB encryptedDB + client: *client0 + databaseName: &encryptedDBName default + - collection: + id: &encryptedColl encryptedColl + database: *encryptedDB + collectionName: &encryptedCollName default + # intentionally the same DB and collection name as encryptedDB/Coll + - database: + id: &encryptedDB2 encryptedDB2 + client: *client1 + databaseName: *encryptedDBName + - collection: + id: &encryptedColl2 encryptedColl2 + database: *encryptedDB2 + collectionName: *encryptedDBName + +initialData: + - databaseName: &keyvaultDBName keyvault + collectionName: &datakeysCollName datakeys + documents: + - {'status': 1, '_id': {'$binary': {'base64': 'OyQRAeK7QlWMr0E2xWapYg==', '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']} + - databaseName: *encryptedDBName + collectionName: *encryptedCollName + documents: [] + +tests: + - description: "A local schema should override" + operations: + - object: *encryptedColl + name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - object: *encryptedColl + name: find + arguments: + filter: { _id: 1 } + expectResult: [*doc0] + expectEvents: + # Then key is fetched from the key vault. + - client: *client0 + events: + - commandStartedEvent: + databaseName: *keyvaultDBName + commandName: find + command: + find: *datakeysCollName + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'OyQRAeK7QlWMr0E2xWapYg==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + readConcern: { level: "majority" } + - commandStartedEvent: + commandName: insert + command: + insert: *encryptedCollName + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'ATskEQHiu0JVjK9BNsVmqWIClDjVEWlpmVRN76InSQuFW2piVbYFkh0QhZCKyx9DdvFBUG+FWluh0kXyhdq3b2Vt/nqNWjXn2y0+JPhrc4W+wQ==', 'subType': '06'}} } + ordered: true + - commandStartedEvent: + commandName: find + command: + find: *encryptedCollName + filter: { _id: 1 } + outcome: + - collectionName: *encryptedCollName + databaseName: *encryptedDBName + documents: + - *doc0_encrypted + - description: "A local schema with no encryption is an error" + operations: + - object: *encryptedColl2 + name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + expectError: + isError: true + errorContains: "JSON schema keyword 'required' is only allowed with a remote schema" diff --git a/source/client-side-encryption/tests/unified/malformedCiphertext.json b/source/client-side-encryption/tests/unified/malformedCiphertext.json new file mode 100644 index 0000000000..550928f1e0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/malformedCiphertext.json @@ -0,0 +1,241 @@ +{ + "description": "malformedCiphertext", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "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" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "00" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQ==", + "subType": "06" + } + } + }, + { + "_id": 3, + "encrypted_string": { + "$binary": { + "base64": "AQAAa2V2aW4gYWxiZXJ0c29uCg==", + "subType": "06" + } + } + } + ] + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "Wrong subtype", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "Empty data", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 2 + } + }, + "object": "coll", + "expectError": { + "errorContains": "malformed ciphertext" + } + } + ] + }, + { + "description": "Malformed data", + "operations": [ + { + "name": "find", + "arguments": { + "filter": { + "_id": 3 + } + }, + "object": "coll", + "expectError": { + "errorContains": "not all keys requested were satisfied" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/malformedCiphertext.yml b/source/client-side-encryption/tests/unified/malformedCiphertext.yml new file mode 100644 index 0000000000..d1136789db --- /dev/null +++ b/source/client-side-encryption/tests/unified/malformedCiphertext.yml @@ -0,0 +1,143 @@ +description: malformedCiphertext +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "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", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - &doc0 + _id: 1 + encrypted_string: + $binary: + base64: AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg== + subType: "00" + - _id: 2 + encrypted_string: + $binary: + base64: "AQ==" + subType: "06" + - _id: 3 + encrypted_string: + $binary: + base64: "AQAAa2V2aW4gYWxiZXJ0c29uCg==" + subType: "06" + + # Since test requires invalid data to be inserted, use a local schema. + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "Wrong subtype" + operations: + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: + # gets returned without decryption + - *doc0 + - description: "Empty data" + operations: + - name: find + arguments: + filter: { _id: 2 } + object: "coll" + expectError: + errorContains: "malformed ciphertext" + - description: "Malformed data" + operations: + - name: find + arguments: + filter: { _id: 3 } + object: "coll" + expectError: + # ciphertext can only validate subtype (which is correct) + # but takes the 16 byte UUID to look up key. Fails to find. + errorContains: "not all keys requested were satisfied" diff --git a/source/client-side-encryption/tests/unified/maxWireVersion.json b/source/client-side-encryption/tests/unified/maxWireVersion.json new file mode 100644 index 0000000000..f7a5f0b7db --- /dev/null +++ b/source/client-side-encryption/tests/unified/maxWireVersion.json @@ -0,0 +1,108 @@ +{ + "description": "maxWireVersion", + "schemaVersion": "1.23", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99", + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + }, + "extraOptions": { + "mongocryptdBypassSpawn": true + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "operation fails with maxWireVersion < 8", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "encrypted_string": "string0" + } + }, + "expectError": { + "errorContains": "Auto-encryption requires a minimum MongoDB version of 4.2" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/maxWireVersion.yml b/source/client-side-encryption/tests/unified/maxWireVersion.yml new file mode 100644 index 0000000000..b39c520993 --- /dev/null +++ b/source/client-side-encryption/tests/unified/maxWireVersion.yml @@ -0,0 +1,61 @@ +description: maxWireVersion + +schemaVersion: "1.23" + +runOnRequirements: + - maxServerVersion: "4.0.99" + csfle: true + +createEntities: + - client: + id: &client0 client0 + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + extraOptions: + mongocryptdBypassSpawn: true # mongocryptd probably won't be on the path. mongocryptd was introduced in server 4.2. + - database: + id: &database0 database0 + client: *client0 + databaseName: default + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: default + +initialData: + - databaseName: keyvault + collectionName: datakeys + documents: + - { + "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: "operation fails with maxWireVersion < 8" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { encrypted_string: "string0" } + expectError: + errorContains: "Auto-encryption requires a minimum MongoDB version of 4.2" diff --git a/source/client-side-encryption/tests/unified/missingKey.json b/source/client-side-encryption/tests/unified/missingKey.json new file mode 100644 index 0000000000..af0fd5812a --- /dev/null +++ b/source/client-side-encryption/tests/unified/missingKey.json @@ -0,0 +1,233 @@ +{ + "description": "missingKey", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.different", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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 with encryption on a missing key", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "object": "coll", + "expectError": { + "errorContains": "not all keys requested were satisfied" + } + } + ], + "outcome": [ + { + "documents": [], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "different", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/missingKey.yml b/source/client-side-encryption/tests/unified/missingKey.yml new file mode 100644 index 0000000000..5d7184b627 --- /dev/null +++ b/source/client-side-encryption/tests/unified/missingKey.yml @@ -0,0 +1,135 @@ +description: missingKey +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.different + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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 with encryption on a missing key" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_string: "string0", random: "abc" } + object: "coll" + expectError: + errorContains: "not all keys requested were satisfied" + outcome: + - documents: [] + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: different + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find diff --git a/source/client-side-encryption/tests/unified/namedKMS-createDataKey.json b/source/client-side-encryption/tests/unified/namedKMS-createDataKey.json new file mode 100644 index 0000000000..4d75e4cf51 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-createDataKey.json @@ -0,0 +1,396 @@ +{ + "description": "namedKMS-createDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "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 named AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws:name1", + "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:name1", + "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 named Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure:name1", + "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:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp:name1", + "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:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip:name1" + }, + "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:name1", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local:name1" + }, + "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:name1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/namedKMS-createDataKey.yml b/source/client-side-encryption/tests/unified/namedKMS-createDataKey.yml new file mode 100644 index 0000000000..e67bb21462 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-createDataKey.yml @@ -0,0 +1,176 @@ +description: namedKMS-createDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": + { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: create data key with named AWS KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "aws:name1" + opts: + masterKey: &new_aws_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: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "aws:name1" + <<: *new_aws_masterkey + writeConcern: { w: majority } + + - description: create datakey with named Azure KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "azure:name1" + opts: + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "azure:name1" + <<: *new_azure_masterkey + writeConcern: { w: majority } + + - description: create datakey with named GCP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "gcp:name1" + opts: + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "gcp:name1" + <<: *new_gcp_masterkey + writeConcern: { w: majority } + + - description: create datakey with named KMIP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "kmip:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "kmip:name1" + keyId: { $$type: string } + writeConcern: { w: majority } + + - description: create datakey with named local KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "local:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "local:name1" + writeConcern: { w: majority } diff --git a/source/client-side-encryption/tests/unified/namedKMS-explicit.json b/source/client-side-encryption/tests/unified/namedKMS-explicit.json new file mode 100644 index 0000000000..e28d7e8b30 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-explicit.json @@ -0,0 +1,130 @@ +{ + "description": "namedKMS-explicit", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name2" + ], + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ] + } + ], + "tests": [ + { + "description": "can explicitly encrypt with a named KMS provider", + "operations": [ + { + "name": "encrypt", + "object": "clientEncryption0", + "arguments": { + "value": "foobar", + "opts": { + "keyAltName": "local:name2", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "expectResult": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + } + ] + }, + { + "description": "can explicitly decrypt with a named KMS provider", + "operations": [ + { + "name": "decrypt", + "object": "clientEncryption0", + "arguments": { + "value": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + }, + "expectResult": "foobar" + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/namedKMS-explicit.yml b/source/client-side-encryption/tests/unified/namedKMS-explicit.yml new file mode 100644 index 0000000000..63204c6bf0 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-explicit.yml @@ -0,0 +1,85 @@ +description: namedKMS-explicit + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "local:name2": + { + key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { + "_id": { "$binary": { "base64": "local+name2+AAAAAAAAAA==", "subType": "04" } }, + "keyAltNames": ["local:name2"], + "keyMaterial": + { + "$binary": + { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "local:name2" }, + } + +tests: + - description: can explicitly encrypt with a named KMS provider + operations: + - name: encrypt + object: *clientEncryption0 + arguments: + value: "foobar" + opts: + keyAltName: "local:name2" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + expectResult: + { + "$binary": + { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06", + }, + } + + - description: can explicitly decrypt with a named KMS provider + operations: + - name: decrypt + object: *clientEncryption0 + arguments: + value: + { + "$binary": + { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06", + }, + } + expectResult: "foobar" diff --git a/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json b/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json new file mode 100644 index 0000000000..b3b9bd2477 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json @@ -0,0 +1,1385 @@ +{ + "description": "namedKMS-rewrapManyDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "key": { + "$$placeholder": 1 + } + }, + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + }, + "aws:name2": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$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:name1_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:name1", + "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:name1_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:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp:name1_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:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip:name1", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local:name1" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap to aws:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name1", + "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:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "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:name1", + "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:name1", + "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:name1", + "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 to azure:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure:name1_key" + } + }, + "opts": { + "provider": "azure:name1", + "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:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "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:name1", + "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:name1", + "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:name1", + "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 to gcp:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp:name1_key" + } + }, + "opts": { + "provider": "gcp:name1", + "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:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "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:name1", + "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:name1", + "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:name1", + "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 to kmip:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip:name1_key" + } + }, + "opts": { + "provider": "kmip:name1" + } + }, + "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:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to local:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local:name1_key" + } + }, + "opts": { + "provider": "local:name1" + } + }, + "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:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from local:name1 to local:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "opts": { + "provider": "local:name2" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name2" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from aws:name1 to aws:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name2", + "masterKey": { + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name2", + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml b/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml new file mode 100644 index 0000000000..65ca0e1cdd --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml @@ -0,0 +1,626 @@ +description: namedKMS-rewrapManyDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": + { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + # Use a different local master key for `local:name2`. + "local:name2": + { + key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + } + # Use a different AWS account to test wrapping between different AWS accounts. + "aws:name2": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: &aws:name1_key_id { $binary: { base64: YXdzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + keyAltNames: ["aws:name1_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: &aws:name1_masterkey + provider: aws:name1 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + - _id: &azure:name1_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } + keyAltNames: ["azure:name1_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: &azure:name1_masterkey + provider: "azure:name1" + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + - _id: &gcp:name1_key_id { $binary: { base64: Z2NwZ2NwZ2NwZ2NwZ2NwZw==, subType: "04" } } + keyAltNames: ["gcp:name1_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: &gcp:name1_masterkey + provider: "gcp:name1" + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + - _id: &kmip:name1_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } + keyAltNames: ["kmip:name1_key"] + keyMaterial: + { + $binary: + { + base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, + subType: "00", + }, + } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip:name1_masterkey + provider: "kmip:name1" + keyId: "1" + - _id: &local:name1_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltNames: ["local:name1_key"] + keyMaterial: + { + $binary: + { + base64: ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==, + subType: "00", + }, + } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &local:name1_masterkey + provider: "local:name1" + +tests: + - description: "rewrap to aws:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "aws:name1_key" } } + opts: + provider: "aws:name1" + # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. + masterKey: &new_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: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to azure:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: azure:name1_key } } + opts: + provider: "azure:name1" + masterKey: &new_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: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: azure:name1_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to gcp:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: gcp:name1_key } } + opts: + provider: "gcp:name1" + masterKey: &new_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: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "gcp:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to kmip:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + opts: + provider: "kmip:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to local:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "local:name1_key" } } + opts: + provider: "local:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from local:name1 to local:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "local:name1_key" } } + opts: + provider: "local:name2" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: { masterKey: { provider: "local:name2" }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from aws:name1 to aws:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "aws:name1_key" } } + opts: + provider: "aws:name2" + masterKey: &new_awsname2_masterkey # aws:name1 account does not have permissions to access this key. + key: "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: "aws:name2", <<: *new_awsname2_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } diff --git a/source/client-side-encryption/tests/unified/namedKMS.json b/source/client-side-encryption/tests/unified/namedKMS.json new file mode 100644 index 0000000000..5e203865fd --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS.json @@ -0,0 +1,241 @@ +{ + "description": "namedKMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ] + } + ], + "tests": [ + { + "description": "Automatically encrypt and decrypt with a named KMS provider", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + }, + "object": "coll" + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "object": "coll", + "expectResult": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/namedKMS.yml b/source/client-side-encryption/tests/unified/namedKMS.yml new file mode 100644 index 0000000000..b330b81418 --- /dev/null +++ b/source/client-side-encryption/tests/unified/namedKMS.yml @@ -0,0 +1,129 @@ +description: namedKMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local:name2: + key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + createOptions: + validator: + $jsonSchema: + { + "properties": + { + "encrypted_string": + { + "encrypt": + { + "keyId": ["$binary": { "base64": "local+name2+AAAAAAAAAA==", "subType": "04" }], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "_id": { "$binary": { "base64": "local+name2+AAAAAAAAAA==", "subType": "04" } }, + "keyMaterial": + { + "$binary": + { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00", + }, + }, + "creationDate": { "$date": { "$numberLong": "1552949630483" } }, + "updateDate": { "$date": { "$numberLong": "1552949630483" } }, + "status": { "$numberInt": "0" }, + "masterKey": { "provider": "local:name2" }, + }, + ] +tests: + - description: "Automatically encrypt and decrypt with a named KMS provider" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + object: "coll" + - name: find + arguments: + filter: { _id: 1 } + object: "coll" + expectResult: [*doc0] + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "local+name2+AAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find diff --git a/source/client-side-encryption/tests/unified/noSchema.json b/source/client-side-encryption/tests/unified/noSchema.json new file mode 100644 index 0000000000..c18afa4ed4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/noSchema.json @@ -0,0 +1,115 @@ +{ + "description": "noSchema", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "unencrypted" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "unencrypted", + "documents": [] + } + ], + "tests": [ + { + "description": "Insert on an unencrypted collection", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + }, + "object": "coll" + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1 + } + ], + "collectionName": "unencrypted", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "unencrypted" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "unencrypted", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true + }, + "commandName": "insert" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/noSchema.yml b/source/client-side-encryption/tests/unified/noSchema.yml new file mode 100644 index 0000000000..919d4ec754 --- /dev/null +++ b/source/client-side-encryption/tests/unified/noSchema.yml @@ -0,0 +1,56 @@ +description: noSchema +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: unencrypted +initialData: + - databaseName: default + collectionName: unencrypted + documents: [] +tests: + - description: "Insert on an unencrypted collection" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1 } + object: "coll" + outcome: + - documents: + - *doc0 + collectionName: &collection_name "unencrypted" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0 + ordered: true + commandName: insert diff --git a/source/client-side-encryption/tests/unified/removeKeyAltName.json b/source/client-side-encryption/tests/unified/removeKeyAltName.json new file mode 100644 index 0000000000..1b7077077a --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/removeKeyAltName.yml b/source/client-side-encryption/tests/unified/removeKeyAltName.yml new file mode 100644 index 0000000000..0449ffb11d --- /dev/null +++ b/source/client-side-encryption/tests/unified/removeKeyAltName.yml @@ -0,0 +1,244 @@ +description: removeKeyAltName + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - &local_key_doc + _id: &local_key_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: + # First 3 letters of local_key_id replaced with 'A' (value: "#alkeylocalkey"). + id: &non_existent_id { $binary: { base64: AAAjYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltName: does_not_exist + expectResult: { $$unsetOrMatches: null } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *non_existent_id } + update: + [ + { + $set: + { + keyAltNames: + { + $cond: + [ + { $eq: [$keyAltNames, [does_not_exist]] }, + $$REMOVE, + { $filter: { input: $keyAltNames, cond: { $ne: [$$this, does_not_exist] } } }, + ], + }, + }, + }, + ] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *local_key_doc + + - description: remove non-existent keyAltName from existing data key + operations: + - name: removeKeyAltName + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: does_not_exist + expectResult: *local_key_doc + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: + [ + { + $set: + { + keyAltNames: + { + $cond: + [ + { $eq: [$keyAltNames, [does_not_exist]] }, + $$REMOVE, + { $filter: { input: $keyAltNames, cond: { $ne: [$$this, does_not_exist] } } }, + ], + }, + }, + }, + ] + writeConcern: { w: majority } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *local_key_doc + + - description: remove an existing keyAltName from an existing data key + operations: + - name: removeKeyAltName + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: alternate_name + expectResult: *local_key_doc + - name: find + object: *collection0 + arguments: + filter: {} + projection: { _id: 0, keyAltNames: 1 } + expectResult: + - keyAltNames: [local_key] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + 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: *local_key_id + keyAltName: alternate_name + expectResult: *local_key_doc + - name: removeKeyAltName + object: *clientEncryption0 + arguments: + id: *local_key_id + keyAltName: local_key + expectResult: + _id: *local_key_id + keyAltNames: [local_key] + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: 1 + masterKey: + provider: local + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: + [ + { + $set: + { + keyAltNames: + { + $cond: + [ + { $eq: [$keyAltNames, [alternate_name]] }, + $$REMOVE, + { $filter: { input: $keyAltNames, cond: { $ne: [$$this, alternate_name] } } }, + ], + }, + }, + }, + ] + writeConcern: { w: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + findAndModify: *collection0Name + query: { _id: *local_key_id } + update: + [ + { + $set: + { + keyAltNames: + { + $cond: + [ + { $eq: [$keyAltNames, [local_key]] }, + $$REMOVE, + { $filter: { input: $keyAltNames, cond: { $ne: [$$this, local_key] } } }, + ], + }, + }, + }, + ] diff --git a/source/client-side-encryption/tests/unified/replaceOne.json b/source/client-side-encryption/tests/unified/replaceOne.json new file mode 100644 index 0000000000..a093e238ba --- /dev/null +++ b/source/client-side-encryption/tests/unified/replaceOne.json @@ -0,0 +1,316 @@ +{ + "description": "replaceOne", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "replaceOne with encryption", + "operations": [ + { + "name": "replaceOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "replacement": { + "encrypted_string": "string1", + "random": "abc" + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + "multi": false, + "upsert": false + } + ], + "ordered": true + }, + "commandName": "update" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/replaceOne.yml b/source/client-side-encryption/tests/unified/replaceOne.yml new file mode 100644 index 0000000000..e461958759 --- /dev/null +++ b/source/client-side-encryption/tests/unified/replaceOne.yml @@ -0,0 +1,206 @@ +description: replaceOne +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "replaceOne with encryption" + operations: + - name: replaceOne + arguments: + filter: { encrypted_string: "string0" } + replacement: { encrypted_string: "string1", random: "abc" } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + u: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + multi: false + upsert: false + ordered: true + commandName: update diff --git a/source/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json b/source/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.json new file mode 100644 index 0000000000..4c7d4e8048 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml b/source/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml new file mode 100644 index 0000000000..d90ece056c --- /dev/null +++ b/source/client-side-encryption/tests/unified/rewrapManyDataKey-decrypt_failure.yml @@ -0,0 +1,76 @@ +description: rewrapManyDataKey-decrypt_failure + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 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 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + 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 + # "us-east-1" changed to "us-east-2" in both key and region. + 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: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } diff --git a/source/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json b/source/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.json new file mode 100644 index 0000000000..cd2d20c255 --- /dev/null +++ b/source/client-side-encryption/tests/unified/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/source/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml b/source/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml new file mode 100644 index 0000000000..1627c7f2fc --- /dev/null +++ b/source/client-side-encryption/tests/unified/rewrapManyDataKey-encrypt_failure.yml @@ -0,0 +1,129 @@ +description: rewrapManyDataKey-encrypt_failure + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 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 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + 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: + # "us-east-1" changed to "us-east-2" in both key and region. + 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: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } + + - description: "rewrap with invalid masterKey for Azure KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: {} + opts: + provider: azure + masterKey: + # "key" changed to "invalid" in both keyVaultEndpoint and keyName. + keyVaultEndpoint: invalid-vault-csfle.vault.azure.net + keyName: invalid-name-csfle + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: find + databaseName: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } + + - description: "rewrap with invalid masterKey for GCP KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: {} + opts: + provider: gcp + masterKey: + # "key" changed to "invalid" in both keyRing and keyName. + projectId: devprod-drivers + location: global + keyRing: invalid-ring-csfle + keyName: invalid-name-csfle + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: find + databaseName: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } diff --git a/source/client-side-encryption/tests/unified/rewrapManyDataKey.json b/source/client-side-encryption/tests/unified/rewrapManyDataKey.json new file mode 100644 index 0000000000..8803491dbe --- /dev/null +++ b/source/client-side-encryption/tests/unified/rewrapManyDataKey.json @@ -0,0 +1,1922 @@ +{ + "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" + } + }, + { + "_id": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba5" + }, + "keyAltNames": [ + "kmip_delegated_key" + ], + "keyMaterial": { + "$binary": { + "base64": "5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip", + "keyId": "11", + "delegated": true + } + } + ] + } + ], + "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": 5, + "modifiedCount": 5, + "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 + } + }, + { + "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": 5, + "modifiedCount": 5, + "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 + } + }, + { + "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": 5, + "modifiedCount": 5, + "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 + } + }, + { + "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": 5, + "modifiedCount": 5, + "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 + } + }, + { + "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 KMIP delegated KMS provider", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip_delegated_key" + } + }, + "opts": { + "provider": "kmip", + "masterKey": { + "delegated": true + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 5, + "modifiedCount": 5, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip_delegated_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip", + "delegated": true, + "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": 5, + "modifiedCount": 5, + "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 + } + }, + { + "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": 6, + "modifiedCount": 6, + "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": { + "$uuid": "7411e9af-c688-4df7-8143-5e60ae96cba5" + }, + "masterKey": { + "provider": "kmip", + "keyId": "11", + "delegated": true + } + }, + { + "_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 + } + }, + { + "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/source/client-side-encryption/tests/unified/rewrapManyDataKey.yml b/source/client-side-encryption/tests/unified/rewrapManyDataKey.yml new file mode 100644 index 0000000000..7fa5cdda7a --- /dev/null +++ b/source/client-side-encryption/tests/unified/rewrapManyDataKey.yml @@ -0,0 +1,785 @@ +# To ensure consistent ordering for expectResult matching purposes, find +# commands sort the resulting documents in ascending order by the single-element +# keyAltNames array to ensure alphabetic order by original KMS provider as +# defined in initialData. +description: rewrapManyDataKey + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 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 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: &aws_key_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: &aws_masterkey + provider: aws + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + - _id: &azure_key_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: &azure_masterkey + provider: azure + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + - _id: &gcp_key_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: &gcp_masterkey + provider: gcp + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + - _id: &kmip_key_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: &kmip_masterkey + provider: kmip + keyId: "1" + - _id: &local_key_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: &local_masterkey + provider: local + - _id: &kmip_delegated_key_id { $uuid: "7411e9af-c688-4df7-8143-5e60ae96cba5" } + keyAltNames: ["kmip_delegated_key"] + keyMaterial: + { + $binary: + { + base64: 5TLMFWlguBWe5GUESTvOVtkdBsCrynhnV72XRyZ66/nk+EP9/1oEp1t1sg0+vwCTqULHjBiUE6DRx2mYD/Eup1+u2Jgz9/+1sV1drXeOPALNPkSgiZiDbIb67zRi+wTABEcKcegJH+FhmSGxwUoQAiHCsCbcvia5P8tN1lt98YQ=, + subType: "00", + }, + } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip_delegated_masterkey + provider: kmip + keyId: "11" + delegated: true + +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: + # If no bulk write operation, then no bulk write result. + bulkWriteResult: { $$exists: false } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + 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 + # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. + masterKey: &new_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: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: aws_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: aws, <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: aws, <<: *new_aws_masterkey }, 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: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: azure_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: azure, <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: azure, <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: azure, <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: azure, <<: *new_azure_masterkey }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: azure, <<: *new_azure_masterkey }, + 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: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: gcp_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { masterKey: { provider: gcp, <<: *new_gcp_masterkey }, 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: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: kmip_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + 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 } + - 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 KMIP delegated KMS provider" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: kmip_delegated_key } } + opts: + provider: kmip + masterKey: + delegated: true + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: kmip_delegated_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: kmip, delegated: true, keyId: { $$type: string } }, + keyMaterial: { $$type: binData }, + }, + $currentDate: { updateDate: true }, + } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: + { + $set: + { + masterKey: { provider: kmip, delegated: true, 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: 5 + modifiedCount: 5 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: local_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + 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 } + - 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: 6 + modifiedCount: 6 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + - name: find + object: *collection0 + arguments: + filter: {} + projection: { masterKey: 1 } + sort: { keyAltNames: 1 } + expectResult: + - { _id: *aws_key_id, masterKey: *aws_masterkey } + - { _id: *azure_key_id, masterKey: *azure_masterkey } + - { _id: *gcp_key_id, masterKey: *gcp_masterkey } + - { _id: *kmip_delegated_key_id, masterKey: *kmip_delegated_masterkey } + - { _id: *kmip_key_id, masterKey: *kmip_masterkey } + - { _id: *local_key_id, masterKey: *local_masterkey } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: {} + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + 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 } + - 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/source/client-side-encryption/tests/unified/timeoutMS.json b/source/client-side-encryption/tests/unified/timeoutMS.json new file mode 100644 index 0000000000..98dc50e98a --- /dev/null +++ b/source/client-side-encryption/tests/unified/timeoutMS.json @@ -0,0 +1,270 @@ +{ + "description": "timeoutMS", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "timeoutMS": 500 + } + } + }, + { + "client": { + "id": "client1", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "cse-timeouts-db" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "cse-timeouts-coll" + } + } + ], + "initialData": [ + { + "databaseName": "cse-timeouts-db", + "collectionName": "cse-timeouts-coll", + "documents": [], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "object": "coll", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "cse-timeouts-coll" + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + }, + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "remaining timeoutMS applied to find to get keyvault data", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections", + "find" + ], + "blockConnection": true, + "blockTimeMS": 300 + } + } + } + }, + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0", + "random": "abc" + } + }, + "object": "coll", + "expectError": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/timeoutMS.yml b/source/client-side-encryption/tests/unified/timeoutMS.yml new file mode 100644 index 0000000000..c52fdcf3ea --- /dev/null +++ b/source/client-side-encryption/tests/unified/timeoutMS.yml @@ -0,0 +1,160 @@ +description: timeoutMS +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.4" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + uriOptions: + timeoutMS: 500 + - client: + id: "client1" + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: cse-timeouts-db + - collection: + id: "coll" + database: "db" + collectionName: cse-timeouts-coll +initialData: + - databaseName: cse-timeouts-db + collectionName: cse-timeouts-coll + documents: [] + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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" + operations: + - name: failPoint + object: testRunner + arguments: + client: "client1" + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + blockConnection: true + blockTimeMS: 600 + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0", random: "abc" } + object: "coll" + expectError: + isTimeoutError: true + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: &collection_name "cse-timeouts-coll" + + maxTimeMS: { $$type: ["int", "long"] } + commandName: listCollections + - description: "remaining timeoutMS applied to find to get keyvault data" + operations: + - name: failPoint + object: testRunner + arguments: + client: "client1" + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + - find + blockConnection: true + blockTimeMS: 300 + - name: insertOne + arguments: + document: *doc0 + object: "coll" + expectError: + isTimeoutError: true diff --git a/source/client-side-encryption/tests/unified/types.json b/source/client-side-encryption/tests/unified/types.json new file mode 100644 index 0000000000..3bb49f2a64 --- /dev/null +++ b/source/client-side-encryption/tests/unified/types.json @@ -0,0 +1,2262 @@ +{ + "description": "types", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_objectId": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "objectId", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db0", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll0", + "database": "db0", + "collectionName": "default" + } + }, + { + "client": { + "id": "client1", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_symbol": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "symbol", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db1", + "client": "client1", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll1", + "database": "db1", + "collectionName": "default" + } + }, + { + "client": { + "id": "client2", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_int": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "int", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db2", + "client": "client2", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll2", + "database": "db2", + "collectionName": "default" + } + }, + { + "client": { + "id": "client3", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_double": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "double", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db3", + "client": "client3", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll3", + "database": "db3", + "collectionName": "default" + } + }, + { + "client": { + "id": "client4", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_decimal": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "decimal", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db4", + "client": "client4", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll4", + "database": "db4", + "collectionName": "default" + } + }, + { + "client": { + "id": "client5", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_binData": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "binData", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db5", + "client": "client5", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll5", + "database": "db5", + "collectionName": "default" + } + }, + { + "client": { + "id": "client6", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_javascript": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "javascript", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db6", + "client": "client6", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll6", + "database": "db6", + "collectionName": "default" + } + }, + { + "client": { + "id": "client7", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_javascriptWithScope": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "javascriptWithScope", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db7", + "client": "client7", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll7", + "database": "db7", + "collectionName": "default" + } + }, + { + "client": { + "id": "client8", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_object": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "object", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db8", + "client": "client8", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll8", + "database": "db8", + "collectionName": "default" + } + }, + { + "client": { + "id": "client9", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_timestamp": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "timestamp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db9", + "client": "client9", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll9", + "database": "db9", + "collectionName": "default" + } + }, + { + "client": { + "id": "client10", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_regex": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "regex", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db10", + "client": "client10", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll10", + "database": "db10", + "collectionName": "default" + } + }, + { + "client": { + "id": "client11", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_date": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "date", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db11", + "client": "client11", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll11", + "database": "db11", + "collectionName": "default" + } + }, + { + "client": { + "id": "client12", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_minKey": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "minKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db12", + "client": "client12", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll12", + "database": "db12", + "collectionName": "default" + } + }, + { + "client": { + "id": "client13", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_maxKey": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "maxKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db13", + "client": "client13", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll13", + "database": "db13", + "collectionName": "default" + } + }, + { + "client": { + "id": "client14", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_undefined": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "undefined", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db14", + "client": "client14", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll14", + "database": "db14", + "collectionName": "default" + } + }, + { + "client": { + "id": "client15", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_array": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "array", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db15", + "client": "client15", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll15", + "database": "db15", + "collectionName": "default" + } + }, + { + "client": { + "id": "client16", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_bool": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "bool", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db16", + "client": "client16", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll16", + "database": "db16", + "collectionName": "default" + } + }, + { + "client": { + "id": "client17", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "schemaMap": { + "default.default": { + "properties": { + "encrypted_null": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "null", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + } + }, + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db17", + "client": "client17", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll17", + "database": "db17", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "type=objectId", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_objectId": { + "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" + } + } + }, + "object": "coll0" + }, + { + "name": "find", + "object": "coll0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_objectId": { + "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_objectId": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_objectId": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=symbol", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_symbol": { + "$symbol": "test" + } + } + }, + "object": "coll1" + }, + { + "name": "find", + "object": "coll1", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_symbol": { + "$symbol": "test" + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_symbol": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_symbol": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=int", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_int": { + "$numberInt": "123" + } + } + }, + "object": "coll2" + }, + { + "name": "find", + "object": "coll2", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_int": { + "$numberInt": "123" + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_int": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client2", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_int": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=double", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_double": { + "$numberDouble": "1.23" + } + } + }, + "object": "coll3", + "expectError": { + "errorContains": "element of type: double" + } + } + ] + }, + { + "description": "type=decimal", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_decimal": { + "$numberDecimal": "1.23" + } + } + }, + "object": "coll4", + "expectError": { + "errorContains": "element of type: decimal" + } + } + ] + }, + { + "description": "type=binData", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AAAA", + "subType": "00" + } + } + } + }, + "object": "coll5" + }, + { + "name": "find", + "object": "coll5", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AAAA", + "subType": "00" + } + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client5", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_binData": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=javascript", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_javascript": { + "$code": "var x = 1;" + } + } + }, + "object": "coll6" + }, + { + "name": "find", + "object": "coll6", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_javascript": { + "$code": "var x = 1;" + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_javascript": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client6", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_javascript": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=javascriptWithScope", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_javascriptWithScope": { + "$code": "var x = 1;", + "$scope": {} + } + } + }, + "object": "coll7", + "expectError": { + "errorContains": "element of type: javascriptWithScope" + } + } + ] + }, + { + "description": "type=object", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_object": {} + } + }, + "object": "coll8", + "expectError": { + "errorContains": "element of type: object" + } + } + ] + }, + { + "description": "type=timestamp", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_timestamp": { + "$timestamp": { + "t": 123, + "i": 456 + } + } + } + }, + "object": "coll9" + }, + { + "name": "find", + "object": "coll9", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_timestamp": { + "$timestamp": { + "t": 123, + "i": 456 + } + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_timestamp": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client9", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_timestamp": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=regex", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_regex": { + "$regularExpression": { + "pattern": "test", + "options": "" + } + } + } + }, + "object": "coll10" + }, + { + "name": "find", + "object": "coll10", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_regex": { + "$regularExpression": { + "pattern": "test", + "options": "" + } + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_regex": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client10", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_regex": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=date", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_date": { + "$date": { + "$numberLong": "123" + } + } + } + }, + "object": "coll11" + }, + { + "name": "find", + "object": "coll11", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "encrypted_date": { + "$date": { + "$numberLong": "123" + } + } + } + ] + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_date": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client11", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_date": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "type=minKey", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_minKey": { + "$minKey": 1 + } + } + }, + "object": "coll12", + "expectError": { + "errorContains": "Cannot encrypt element of type: minKey" + } + } + ] + }, + { + "description": "type=maxKey", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_maxKey": { + "$maxKey": 1 + } + } + }, + "object": "coll13", + "expectError": { + "errorContains": "Cannot encrypt element of type: maxKey" + } + } + ] + }, + { + "description": "type=undefined", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_undefined": { + "$undefined": true + } + } + }, + "object": "coll14", + "expectError": { + "errorContains": "Cannot encrypt element of type: undefined" + } + } + ] + }, + { + "description": "type=array", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_array": [] + } + }, + "object": "coll15", + "expectError": { + "errorContains": "element of type: array" + } + } + ] + }, + { + "description": "type=bool", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_bool": true + } + }, + "object": "coll16", + "expectError": { + "errorContains": "element of type: bool" + } + } + ] + }, + { + "description": "type=null", + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_null": true + } + }, + "object": "coll17", + "expectError": { + "errorContains": "Cannot encrypt element of type: null" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/types.yml b/source/client-side-encryption/tests/unified/types.yml new file mode 100644 index 0000000000..3a72ce17f4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/types.yml @@ -0,0 +1,1191 @@ +description: types +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + # Create separate client entities for each tested type to isolate encryption schemas. mongocryptd/crypt_shared returns an error on schemas for unsupported types (e.g. double). + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_objectId": + { + "encrypt": + { + "keyId": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }], + "bsonType": "objectId", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db0" + client: "client0" + databaseName: default + - collection: + id: "coll0" + database: "db0" + collectionName: default + - client: + id: "client1" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_symbol": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "symbol", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db1" + client: "client1" + databaseName: default + - collection: + id: "coll1" + database: "db1" + collectionName: default + - client: + id: "client2" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_int": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "int", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db2" + client: "client2" + databaseName: default + - collection: + id: "coll2" + database: "db2" + collectionName: default + - client: + id: "client3" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_double": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "double", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db3" + client: "client3" + databaseName: default + - collection: + id: "coll3" + database: "db3" + collectionName: default + - client: + id: "client4" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_decimal": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "decimal", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db4" + client: "client4" + databaseName: default + - collection: + id: "coll4" + database: "db4" + collectionName: default + - client: + id: "client5" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_binData": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "binData", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db5" + client: "client5" + databaseName: default + - collection: + id: "coll5" + database: "db5" + collectionName: default + - client: + id: "client6" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_javascript": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "javascript", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db6" + client: "client6" + databaseName: default + - collection: + id: "coll6" + database: "db6" + collectionName: default + - client: + id: "client7" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_javascriptWithScope": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "javascriptWithScope", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db7" + client: "client7" + databaseName: default + - collection: + id: "coll7" + database: "db7" + collectionName: default + - client: + id: "client8" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_object": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "object", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db8" + client: "client8" + databaseName: default + - collection: + id: "coll8" + database: "db8" + collectionName: default + - client: + id: "client9" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_timestamp": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "timestamp", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db9" + client: "client9" + databaseName: default + - collection: + id: "coll9" + database: "db9" + collectionName: default + - client: + id: "client10" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_regex": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "regex", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db10" + client: "client10" + databaseName: default + - collection: + id: "coll10" + database: "db10" + collectionName: default + - client: + id: "client11" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_date": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "date", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db11" + client: "client11" + databaseName: default + - collection: + id: "coll11" + database: "db11" + collectionName: default + - client: + id: "client12" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_minKey": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "minKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db12" + client: "client12" + databaseName: default + - collection: + id: "coll12" + database: "db12" + collectionName: default + - client: + id: "client13" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_maxKey": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "maxKey", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db13" + client: "client13" + databaseName: default + - collection: + id: "coll13" + database: "db13" + collectionName: default + - client: + id: "client14" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_undefined": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "undefined", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db14" + client: "client14" + databaseName: default + - collection: + id: "coll14" + database: "db14" + collectionName: default + - client: + id: "client15" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_array": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "array", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db15" + client: "client15" + databaseName: default + - collection: + id: "coll15" + database: "db15" + collectionName: default + - client: + id: "client16" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_bool": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "bool", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db16" + client: "client16" + databaseName: default + - collection: + id: "coll16" + database: "db16" + collectionName: default + - client: + id: "client17" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + schemaMap: + "default.default": + { + "properties": + { + "encrypted_null": + { + "encrypt": + { + "keyId": [{ "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } }], + "bsonType": "null", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", + }, + }, + }, + "bsonType": "object", + } + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db17" + client: "client17" + databaseName: default + - collection: + id: "coll17" + database: "db17" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "type=objectId" + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_objectId: { "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" } } + object: "coll0" + - name: find + object: coll0 + arguments: + filter: { _id: 1 } + expectResult: [*doc0] + outcome: + - documents: + - &doc0_encrypted { + _id: 1, + encrypted_objectId: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", + "subType": "06", + }, + }, + } + collectionName: &collection_name "default" + databaseName: &database_name "default" + expectEvents: + - client: "client0" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc0_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=symbol" + operations: + - name: insertOne + arguments: + document: &doc1 { _id: 1, encrypted_symbol: { "$symbol": "test" } } + object: "coll1" + - name: find + object: coll1 + arguments: + filter: { _id: 1 } + expectResult: [*doc1] + outcome: + - documents: + - &doc1_encrypted { + _id: 1, + encrypted_symbol: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client1" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc1_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=int" + operations: + - name: insertOne + arguments: + document: &doc2 { _id: 1, encrypted_int: { "$numberInt": "123" } } + object: "coll2" + - name: find + object: coll2 + arguments: + filter: { _id: 1 } + expectResult: [*doc2] + outcome: + - documents: + - &doc2_encrypted { + _id: 1, + encrypted_int: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client2" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc2_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=double" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_double: { "$numberDouble": "1.23" } } + object: "coll3" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: double" + # After it is: + # "Cannot encrypt element of type: double" + # Only check for the common suffix. + errorContains: "element of type: double" + - description: "type=decimal" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_decimal: { "$numberDecimal": "1.23" } } + object: "coll4" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: decimal" + # After it is: + # "Cannot encrypt element of type: decimal" + # Only check for the common suffix. + errorContains: "element of type: decimal" + - description: "type=binData" + operations: + - name: insertOne + arguments: + document: &doc6 { _id: 1, encrypted_binData: { "$binary": { base64: "AAAA", subType: "00" } } } + object: "coll5" + - name: find + object: coll5 + arguments: + filter: { _id: 1 } + expectResult: [*doc6] + outcome: + - documents: + - &doc6_encrypted { + _id: 1, + encrypted_binData: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client5" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc6_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=javascript" + operations: + - name: insertOne + arguments: + document: &doc7 { _id: 1, encrypted_javascript: { "$code": "var x = 1;" } } + object: "coll6" + - name: find + object: coll6 + arguments: + filter: { _id: 1 } + expectResult: [*doc7] + outcome: + - documents: + - &doc7_encrypted { + _id: 1, + encrypted_javascript: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client6" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc7_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=javascriptWithScope" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_javascriptWithScope: { "$code": "var x = 1;", "$scope": {} } } + object: "coll7" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: javascriptWithScope" + # After it is: + # "Cannot encrypt element of type: javascriptWithScope" + # Only check for the common suffix. + errorContains: "element of type: javascriptWithScope" + - description: "type=object" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_object: {} } + object: "coll8" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: object" + # After it is: + # "Cannot encrypt element of type: object" + # Only check for the common suffix. + errorContains: "element of type: object" + - description: "type=timestamp" + operations: + - name: insertOne + arguments: + document: &doc10 { _id: 1, encrypted_timestamp: { $timestamp: { t: 123, i: 456 } } } + object: "coll9" + - name: find + object: coll9 + arguments: + filter: { _id: 1 } + expectResult: [*doc10] + outcome: + - documents: + - &doc10_encrypted { + _id: 1, + encrypted_timestamp: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client9" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc10_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=regex" + operations: + - name: insertOne + arguments: + document: &doc11 { _id: 1, encrypted_regex: { $regularExpression: { pattern: "test", options: "" } } } + object: "coll10" + - name: find + object: coll10 + arguments: + filter: { _id: 1 } + expectResult: [*doc11] + outcome: + - documents: + - &doc11_encrypted { + _id: 1, + encrypted_regex: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client10" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc11_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=date" + operations: + - name: insertOne + arguments: + document: &doc13 { _id: 1, encrypted_date: { $date: { $numberLong: "123" } } } + object: "coll11" + - name: find + object: coll11 + arguments: + filter: { _id: 1 } + expectResult: [*doc13] + outcome: + - documents: + - &doc13_encrypted { + _id: 1, + encrypted_date: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client11" + events: + # Then key is fetched from the key vault. + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *doc13_encrypted + ordered: true + commandName: insert + - commandStartedEvent: + command: + find: *collection_name + filter: { _id: 1 } + commandName: find + - description: "type=minKey" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_minKey: { $minKey: 1 } } + object: "coll12" + expectError: + errorContains: "Cannot encrypt element of type: minKey" + - description: "type=maxKey" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_maxKey: { $maxKey: 1 } } + object: "coll13" + expectError: + errorContains: "Cannot encrypt element of type: maxKey" + - description: "type=undefined" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_undefined: { $undefined: true } } + object: "coll14" + expectError: + errorContains: "Cannot encrypt element of type: undefined" + - description: "type=array" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_array: [] } + object: "coll15" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: array" + # After it is: + # "Cannot encrypt element of type: array" + # Only check for the common suffix. + errorContains: "element of type: array" + - description: "type=bool" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_bool: true } + object: "coll16" + expectError: + # DRIVERS-2272: The expected error message changed in mongocryptd 6.0. Before it was: + # "Cannot use deterministic encryption for element of type: bool" + # After it is: + # "Cannot encrypt element of type: bool" + # Only check for the common suffix. + errorContains: "element of type: bool" + - description: "type=null" + operations: + - name: insertOne + arguments: + document: { _id: 1, encrypted_null: true } + object: "coll17" + expectError: + errorContains: "Cannot encrypt element of type: null" diff --git a/source/client-side-encryption/tests/unified/unsupportedCommand.json b/source/client-side-encryption/tests/unified/unsupportedCommand.json new file mode 100644 index 0000000000..a91390324a --- /dev/null +++ b/source/client-side-encryption/tests/unified/unsupportedCommand.json @@ -0,0 +1,200 @@ +{ + "description": "unsupportedCommand", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "x": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "x": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "mapReduce deterministic encryption (unsupported)", + "operations": [ + { + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "object": "coll", + "expectError": { + "errorContains": "command not supported for auto encryption: mapreduce" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/unsupportedCommand.yml b/source/client-side-encryption/tests/unified/unsupportedCommand.yml new file mode 100644 index 0000000000..053c44af4b --- /dev/null +++ b/source/client-side-encryption/tests/unified/unsupportedCommand.yml @@ -0,0 +1,133 @@ +description: unsupportedCommand +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + x: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { + _id: 2, + x: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "mapReduce deterministic encryption (unsupported)" + operations: + - name: mapReduce + arguments: + map: { $code: "function inc() { return emit(0, this.x + 1) }" } + reduce: { $code: "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" } + out: { inline: 1 } + object: "coll" + expectError: + errorContains: "command not supported for auto encryption: mapreduce" diff --git a/source/client-side-encryption/tests/unified/updateMany.json b/source/client-side-encryption/tests/unified/updateMany.json new file mode 100644 index 0000000000..cae4c0eaf4 --- /dev/null +++ b/source/client-side-encryption/tests/unified/updateMany.json @@ -0,0 +1,376 @@ +{ + "description": "updateMany", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "updateMany with deterministic encryption", + "operations": [ + { + "name": "updateMany", + "arguments": { + "filter": { + "encrypted_string": { + "$in": [ + "string0", + "string1" + ] + } + }, + "update": { + "$set": { + "encrypted_string": "string2", + "random": "abc" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + }, + { + "_id": 2, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$in": [ + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + }, + { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + } + ] + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + }, + "multi": true, + "upsert": false + } + ], + "ordered": true + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "updateMany fails when filtering on a random field", + "operations": [ + { + "name": "updateMany", + "arguments": { + "filter": { + "random": "abc" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/updateMany.yml b/source/client-side-encryption/tests/unified/updateMany.yml new file mode 100644 index 0000000000..fc5be3f96c --- /dev/null +++ b/source/client-side-encryption/tests/unified/updateMany.yml @@ -0,0 +1,246 @@ +description: updateMany +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + - { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "updateMany with deterministic encryption" + operations: + - name: updateMany + arguments: + filter: { encrypted_string: { $in: ["string0", "string1"] } } + update: { $set: { encrypted_string: "string2", random: "abc" } } + object: "coll" + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + - { + _id: 2, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + encrypted_string: + { + $in: + [ + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + ], + }, + } + u: + { + $set: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + }, + } + multi: true + upsert: false + ordered: true + commandName: update + - description: "updateMany fails when filtering on a random field" + operations: + - name: updateMany + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" diff --git a/source/client-side-encryption/tests/unified/updateOne.json b/source/client-side-encryption/tests/unified/updateOne.json new file mode 100644 index 0000000000..6c8fdcbb6e --- /dev/null +++ b/source/client-side-encryption/tests/unified/updateOne.json @@ -0,0 +1,538 @@ +{ + "description": "updateOne", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "4.1.10", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + }, + { + "client": { + "id": "client_unencrypted" + } + }, + { + "database": { + "id": "db_unencrypted", + "client": "client_unencrypted", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll_unencrypted", + "database": "db_unencrypted", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "createOptions": { + "validator": { + "$jsonSchema": { + "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" + } + } + } + }, + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "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": "updateOne with deterministic encryption", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "encrypted_string": "string0" + }, + "update": { + "$set": { + "encrypted_string": "string1", + "random": "abc" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "name": "find", + "object": "coll_unencrypted", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "AAAAAAAAAAAAAAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": { + "encrypted_string": { + "$eq": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + }, + "u": { + "$set": { + "encrypted_string": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06" + } + }, + "random": { + "$$type": "binData" + } + } + }, + "multi": false, + "upsert": false + } + ], + "ordered": true + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "updateOne fails when filtering on a random field", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": { + "random": "abc" + }, + "update": { + "$set": { + "encrypted_string": "string1" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "Cannot query on fields encrypted with the randomized encryption" + } + } + ] + }, + { + "description": "$unset works with an encrypted field", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$unset": { + "encrypted_string": "" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1 + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": {}, + "u": { + "$unset": { + "encrypted_string": "" + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "$rename works if target value has same encryption options", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$rename": { + "encrypted_string": "encrypted_string_equivalent" + } + } + }, + "object": "coll", + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "documents": [ + { + "_id": 1, + "encrypted_string_equivalent": { + "$binary": { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06" + } + } + } + ], + "collectionName": "default", + "databaseName": "default" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "commandName": "listCollections" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "default", + "updates": [ + { + "q": {}, + "u": { + "$rename": { + "encrypted_string": "encrypted_string_equivalent" + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true + }, + "commandName": "update" + } + } + ] + } + ] + }, + { + "description": "$rename fails if target value has different encryption options", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$rename": { + "encrypted_string": "random" + } + } + }, + "object": "coll", + "expectError": { + "errorContains": "$rename between two encrypted fields must have the same metadata or both be unencrypted" + } + } + ] + }, + { + "description": "an invalid update (no $ operators) is validated and errors", + "operations": [ + { + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "encrypted_string": "random" + } + }, + "object": "coll", + "expectError": { + "errorContains": "" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/updateOne.yml b/source/client-side-encryption/tests/unified/updateOne.yml new file mode 100644 index 0000000000..4c147b3f63 --- /dev/null +++ b/source/client-side-encryption/tests/unified/updateOne.yml @@ -0,0 +1,318 @@ +description: updateOne +schemaVersion: "1.25" +runOnRequirements: + - minServerVersion: "4.1.10" + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: &database_name default + - collection: + id: "coll" + database: "db" + collectionName: &collection_name default + - client: + id: "client_unencrypted" + - database: + id: "db_unencrypted" + client: "client_unencrypted" + databaseName: default + - collection: + id: "coll_unencrypted" + database: "db_unencrypted" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + createOptions: + validator: + $jsonSchema: + { + "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", + } + - databaseName: keyvault + collectionName: datakeys + documents: + [ + { + "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: "updateOne with deterministic encryption" + operations: + - name: updateOne + arguments: + filter: { encrypted_string: "string0" } + update: { $set: { encrypted_string: "string1", random: "abc" } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - name: find + object: coll_unencrypted + arguments: + filter: {} + expectResult: + - { + _id: 1, + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + } + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + find: datakeys + filter: + { + "$or": + [ + "_id": { "$in": ["$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" }] }, + "keyAltNames": { "$in": [] }, + ], + } + $db: keyvault + readConcern: { level: "majority" } + commandName: find + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: + { + encrypted_string: + { + $eq: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + }, + } + u: + { + $set: + { + encrypted_string: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", + "subType": "06", + }, + }, + random: { $$type: "binData" }, + }, + } + multi: false + upsert: false + ordered: true + commandName: update + - description: "updateOne fails when filtering on a random field" + operations: + - name: updateOne + arguments: + filter: { random: "abc" } + update: { $set: { encrypted_string: "string1" } } + object: "coll" + expectError: + errorContains: "Cannot query on fields encrypted with the randomized encryption" + - description: "$unset works with an encrypted field" + operations: + - name: updateOne + arguments: + filter: {} + update: { $unset: { encrypted_string: "" } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - documents: + - { _id: 1 } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: {} + u: { $unset: { encrypted_string: "" } } + multi: { "$$unsetOrMatches": false } + upsert: { "$$unsetOrMatches": false } + ordered: true + commandName: update + - description: "$rename works if target value has same encryption options" + operations: + - name: updateOne + arguments: + filter: {} + update: { $rename: { encrypted_string: "encrypted_string_equivalent" } } + object: "coll" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - documents: + - { + _id: 1, + encrypted_string_equivalent: + { + "$binary": + { + "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", + "subType": "06", + }, + }, + } + collectionName: *collection_name + databaseName: *database_name + expectEvents: + - client: "client0" + events: + # Auto encryption will request the collection info. + - commandStartedEvent: + command: + listCollections: 1 + filter: + name: *collection_name + commandName: listCollections + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: {} + u: { $rename: { encrypted_string: "encrypted_string_equivalent" } } + multi: { "$$unsetOrMatches": false } + upsert: { "$$unsetOrMatches": false } + ordered: true + commandName: update + - description: "$rename fails if target value has different encryption options" + operations: + - name: updateOne + arguments: + filter: {} + update: { $rename: { encrypted_string: "random" } } + object: "coll" + expectError: + errorContains: "$rename between two encrypted fields must have the same metadata or both be unencrypted" + - description: "an invalid update (no $ operators) is validated and errors" + operations: + - name: updateOne + arguments: + filter: {} + update: { encrypted_string: "random" } + object: "coll" + expectError: + errorContains: "" # Note, drivers differ in the error message. Just ensure an error is thrown. diff --git a/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.json b/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.json new file mode 100644 index 0000000000..c46a193273 --- /dev/null +++ b/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.json @@ -0,0 +1,323 @@ +{ + "description": "validatorAndPartialFieldExpression", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "minServerVersion": "6.0.0", + "csfle": { + "minLibmongocryptVersion": "1.15.1" + } + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + }, + "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" + } + } + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client0", + "databaseName": "default" + } + }, + { + "collection": { + "id": "coll", + "database": "db", + "collectionName": "default" + } + } + ], + "initialData": [ + { + "databaseName": "default", + "collectionName": "default", + "documents": [] + } + ], + "tests": [ + { + "description": "create with a validator on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection" + } + } + ] + }, + { + "description": "create with a validator on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection", + "validator": { + "encrypted_string": "foo" + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "collMod with a validator on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "encryptedCollection", + "validator": { + "unencrypted_string": "foo" + } + } + } + } + ] + }, + { + "description": "collMod with a validator on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "collMod", + "command": { + "collMod": "encryptedCollection", + "validator": { + "encrypted_string": "foo" + } + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "createIndexes", + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "unencrypted_string": "foo" + } + } + ] + } + } + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "default", + "collectionName": "encryptedCollection", + "indexName": "name" + } + } + ] + }, + { + "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "encryptedCollection" + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "createIndexes", + "command": { + "createIndexes": "encryptedCollection", + "indexes": [ + { + "name": "name", + "key": { + "name": 1 + }, + "partialFilterExpression": { + "encrypted_string": "foo" + } + } + ] + } + }, + "expectError": { + "errorContains": "Comparison to encrypted fields not supported" + } + } + ] + } + ] +} diff --git a/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.yml b/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.yml new file mode 100644 index 0000000000..420a1140ed --- /dev/null +++ b/source/client-side-encryption/tests/unified/validatorAndPartialFieldExpression.yml @@ -0,0 +1,200 @@ +description: validatorAndPartialFieldExpression +schemaVersion: "1.25" +runOnRequirements: + # Require server version 6.0.0 to get behavior added in SERVER-64911. + - minServerVersion: "6.0.0" + + csfle: + minLibmongocryptVersion: "1.15.1" +createEntities: + - client: + id: "client0" + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + 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", + } + observeEvents: + - commandStartedEvent + - database: + id: "db" + client: "client0" + databaseName: default + - collection: + id: "coll" + database: "db" + collectionName: default +initialData: + - databaseName: default + collectionName: default + documents: [] +tests: + - description: "create with a validator on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + validator: + unencrypted_string: "foo" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: &database_name "default" + collectionName: "encryptedCollection" + - description: "create with a validator on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + validator: + encrypted_string: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" + - description: "collMod with a validator on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: collMod + command: + collMod: "encryptedCollection" + validator: + unencrypted_string: "foo" + - description: "collMod with a validator on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: collMod + command: + collMod: "encryptedCollection" + validator: + encrypted_string: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" + - description: "createIndexes with a partialFilterExpression on an unencrypted field is OK" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: createIndexes + command: + createIndexes: "encryptedCollection" + indexes: + - name: "name" + key: { name: 1 } + partialFilterExpression: + unencrypted_string: "foo" + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database_name + collectionName: "encryptedCollection" + indexName: name + - description: "createIndexes with a partialFilterExpression on an encrypted field is an error" + operations: + # Drop to remove a collection that may exist from previous test runs. + - name: dropCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: createCollection + object: "db" + arguments: + collection: "encryptedCollection" + - name: runCommand + object: "db" + arguments: + commandName: createIndexes + command: + createIndexes: "encryptedCollection" + indexes: + - name: "name" + key: { name: 1 } + partialFilterExpression: + encrypted_string: "foo" + expectError: + errorContains: "Comparison to encrypted fields not supported" diff --git a/source/client-side-operations-timeout/client-side-operations-timeout.md b/source/client-side-operations-timeout/client-side-operations-timeout.md new file mode 100644 index 0000000000..0e774abba0 --- /dev/null +++ b/source/client-side-operations-timeout/client-side-operations-timeout.md @@ -0,0 +1,676 @@ +# Client Side Operations Timeout + +- Status: Accepted +- Minimum Server Version: 2.6 + +______________________________________________________________________ + +## Abstract + +This specification outlines a new `timeoutMS` option to govern the amount of time that a single operation can execute +before control is returned to the user. This timeout applies to all of the work done to execute the operation, including +but not limited to server selection, connection checkout, and server-side execution. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**min(a, b)** + +Shorthand for "the minimum of a and b" where `a` and `b` are numeric values. For any cases where 0 means "infinite" +(e.g. [timeoutMS](#timeoutms)), `min(0, other)` MUST evaluate to `other`. + +### MongoClient Configuration + +This specification introduces a new configuration option and deprecates some existing options. + +#### timeoutMS + +This 64-bit integer option specifies the per-operation timeout value in milliseconds. The default value is unset which +means this feature is not enabled, i.e. the existing timeout behavior is unchanged (including +`serverSelectionTimeoutMS`, `connectTimeoutMS`, `socketTimeoutMS` etc..). An explicit value of 0 means infinite, though +some client-side timeouts like `serverSelectionTimeoutMS` will still apply. Drivers MUST error if a negative value is +specified. This value MUST be configurable at the level of a MongoClient, MongoDatabase, MongoCollection, or of a single +operation. However, if the option is specified at any level, it cannot be later changed to unset. At each level, the +value MUST be inherited from the previous level if it is not explicitly specified. Additionally, some entities like +`ClientSession` and `GridFSBucket` either inherit `timeoutMS` from their parent entities or provide options to override +it. The behavior for these entities is described in individual sections of this specification. + +Drivers for languages that provide an idiomatic API for expressing durations of time (e.g. `TimeSpan` in .NET) MAY +choose to leverage these APIs for the `timeoutMS` option rather than using int64. Drivers that choose to do so MUST also +follow the semantics for special values defined by those types. Such drivers MUST also ensure that there is a way to +explicitly set `timeoutMS` to `infinite` in the API. + +See +[timeoutMS cannot be changed to unset once it's specified](#timeoutms-cannot-be-changed-to-unset-once-its-specified). + +#### Backwards Breaking Considerations + +This specification deprecates many existing timeout options and introduces a new exception type that is used to +communicate timeout expiration. If drivers need to make backwards-breaking changes to support `timeoutMS`, the backwards +breaking behavior MUST be gated behind the presence of the `timeoutMS` option. If the `timeoutMS` option is not set, +drivers MUST continue to honor existing timeouts such as `socketTimeoutMS`. Backwards breaking changes include any +changes to exception types thrown by stable API methods or changes to timeout behavior. Drivers MUST document these +changes. + +In a subsequent major release, drivers SHOULD drop support for legacy timeout behavior and only continue to support the +timeout options that are not deprecated by this specification. Once legacy options are removed, drivers MUST make the +backwards-breaking behavioral changes described in this specification regardless of whether or not `timeoutMS` is set by +the application. + +See the [Errors](#errors) section for explanations of the backwards-breaking changes to error reporting. + +#### Deprecations + +The following configuration timeout options MUST be deprecated in favor of `timeoutMS`: + +- `socketTimeoutMS` +- `waitQueueTimeoutMS` +- `wTimeoutMS` + +The following options for CRUD methods MUST be deprecated in favor of `timeoutMS`: + +- `maxTimeMS` +- `maxCommitTimeMS` + +### Timeout Behavior + +The `timeoutMS` option specifies the best-effort maximum amount of time a single operation can take before control is +returned to the application. Drivers MUST keep track of the remaining time before the timeout expires as an operation +progresses. + +#### Operations + +The `timeoutMS` option applies to all operations defined in the following specifications: + +- [CRUD](../crud/crud.md) +- [Change Streams](../change-streams/change-streams.md) +- [Client Side Encryption](../client-side-encryption/client-side-encryption.md) +- [Enumerating Collections](../enumerate-collections/enumerate-collections.md) +- [Enumerating Databases](../enumerate-databases/enumerate-databases.md) +- [GridFS](../gridfs/gridfs-spec.md) +- [Index Management](../index-management/index-management.md) +- [Transactions](../transactions/transactions.md) +- [Convenient API for Transactions](../transactions-convenient-api/transactions-convenient-api.md) + +In addition, it applies to all operations on cursor objects that may perform blocking work (e.g. methods to iterate or +close a cursor, any method that reads documents from a cursor into an array, etc). + +#### Validation and Overrides + +When executing an operation, drivers MUST ignore any deprecated timeout options if `timeoutMS` is set on the operation +or is inherited from the collection/database/client levels. In addition to being set at these levels, the timeout for an +operation can also be expressed via an explicit ClientSession (see +[Convenient Transactions API](#convenient-transactions-api)). In this case, the timeout on the session MUST be used as +the `timeoutMS` value for the operation. Drivers MUST raise a validation error if an explicit session with a timeout is +used and the `timeoutMS` option is set at the operation level for operations executed as part of a `withTransaction` +callback. + +See [timeoutMS overrides deprecated timeout options](#timeoutms-overrides-deprecated-timeout-options). + +#### Errors + +If the `timeoutMS` option is not set and support for deprecated timeout options has not been dropped but a timeout is +encountered (e.g. server selection times out), drivers MUST continue to return existing errors. This ensures that +error-handling code in existing applications does not break unless the user opts into using `timeoutMS`. + +If the `timeoutMS` option is set and the timeout expires, drivers MUST abort all blocking work and return control to the +user with an error. This error MUST be distinguished in some way (e.g. custom exception type) to make it easier for +users to detect when an operation fails due to a timeout. If the timeout expires during a blocking task, drivers MUST +expose the underlying error returned from the task from this new error type. The stringified version of the new error +type MUST include the stringified version of the underlying error as a substring. For example, if server selection +expires and returns a `ServerSelectionTimeoutException`, drivers must allow users to access that exception from this new +error type. If there is no underlying error, drivers MUST add information about when the timeout expiration was detected +to the stringified version of the timeout error. + +##### Error Transformations + +When using the new timeout error type, drivers MUST transform timeout errors from external sources into the new error. +One such error is the `MaxTimeMSExpired` server error. When checking for this error, drivers MUST only check that the +error code is 50 and MUST NOT check the code name or error message. This error can be present in a top-level response +document where the `ok` value is 0, as part of an error in the `writeErrors` array, or in a nested `writeConcernError` +document. For example, all three of the following server responses would match this criteria: + +```javascript +{ok: 0, code: 50, codeName: "MaxTimeMSExpired", errmsg: "operation time limit exceeded"} + +{ok: 1, writeErrors: [{code: 50, codeName: "MaxTimeMSExpired", errmsg: "operation time limit exceeded"}]} + +{ok: 1, writeConcernError: {code: 50, codeName: "MaxTimeMSExpired"}} +``` + +Timeouts from other sources besides MongoDB servers MUST also be transformed into this new exception type. These include +socket read/write timeouts and HTTP request timeouts. + +#### Blocking Sections for Operation Execution + +The following pieces of operation execution are considered blocking: + +1. Implicit session acquisition if an explicit session was not provided for the operation. This is only considered + blocking for drivers that perform server selection to determine session support when acquiring implicit sessions. +2. Server selection +3. Connection checkout - If `maxPoolSize` has already been reached for the selected server, this is the amount of time + spent waiting for a connection to be available. +4. Connection establishment - If the pool for the selected server is empty and a new connection is needed, the following + pieces of connection establishment are considered blocking: + 1. TCP socket establishment + 2. TLS handshake + 1. All messages sent over the socket as part of the TLS handshake + 2. OCSP verification - HTTP requests sent to OCSP responders. + 3. MongoDB handshake (i.e. initial connection `hello`) + 4. Authentication + 1. SCRAM-SHA-1, SCRAM-SHA-256, PLAIN: Execution of the command required for the SASL conversation. + 2. GSSAPI: Execution of the commands required for the SASL conversation and requests to the KDC and TGS. + 3. MONGODB-AWS: Execution of the commands required for the SASL conversation and all HTTP requests to ECS and EC2 + endpoints. + 4. MONGODB-X509: Execution of the commands required for the authentication conversation. +5. Client-side encryption + 1. Execution of `listCollections` commands to get collection schemas. + 2. Execution of `find` commands against the key vault collection to get encrypted data keys. + 3. Requests to non-local key management servers (e.g. AWS KMS) to decrypt data keys. + 4. Requests to mongocryptd servers. +6. Socket write to send a command to the server +7. Socket read to receive the server’s response + +The `timeoutMS` option MUST apply to all blocking sections. Drivers MUST document any exceptions. For example, drivers +that do not have full control over OCSP verification might not be able to set timeouts for HTTP requests to responders +and would document that OCSP verification could result in an execution time greater than `timeoutMS`. + +#### Server Selection + +If `timeoutMS` is set, drivers MUST use `min(serverSelectionTimeoutMS, remaining timeoutMS)`, referred to as +`computedServerSelectionTimeout` as the timeout for server selection and connection checkout. The server selection loop +MUST fail with a timeout error once the timeout expires. + +After a server has been selected, drivers MUST use the remaining `computedServerSelectionTimeout` value as the timeout +for connection checkout. If a new connection is required, +`min(connectTimeoutMS, remaining computedServerSelectionTimeout)` MUST be used as the timeout for TCP socket +establishment. Any network requests required to create or authenticate a connection (e.g. HTTP requests to OCSP +responders) MUST use `min(operationTimeout, remaining computedServerSelectionTimeout)` as a timeout, where +`operationTimeout` is the specified default timeout for the network request. If there is no specified default, these +operations MUST use the remaining `computedServerSelectionTimeout` value. All commands sent during the connection’s +handshake MUST use the remaining `computedServerSelectionTimeout` as their `timeoutMS` value. Handshake commands MUST +also set timeouts per the [Command Execution](#command-execution) section. + +If `timeoutMS` is not set and support for `waitQueueTimeoutMS` has not been removed, drivers MUST continue to exhibit +the existing timeout behavior by honoring `serverSelectionTimeoutMS` for server selection and `waitQueueTimeoutMS` for +connection checkout. If a new connection is required, drivers MUST use `connectTimeoutMS` as the timeout for socket +establishment and `socketTimeoutMS` as the socket timeout for all handshake commands. + +See [serverSelectionTimeoutMS is not deprecated](#serverselectiontimeoutms-is-not-deprecated) and +[connectTimeoutMS is not deprecated](#connecttimeoutms-is-not-deprecated). + +#### Command Execution + +If `timeoutMS` is set, drivers MUST append a `maxTimeMS` field to commands executed against a MongoDB server using the +`minRoundTripTime` field of the selected server. Note that this value MUST be retrieved during server selection using +the `servers` field of the same +[TopologyDescription](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#TopologyDescription) that +was used for selection before the selected server's description can be modified. Otherwise, drivers may be subject to a +race condition where a server is reset to the default description (e.g. due to an error in the monitoring thread) after +it has been selected but before the RTT is retrieved. + +If the `minRoundTripTime` is less than the remaining timeoutMS, the value of this field MUST be +`remaining timeoutMS - minRoundTripTime`. If not, drivers MUST return a timeout error without attempting to send the +message to the server. This is done to ensure that an operation is not routed to the server if it will likely fail with +a socket timeout as that could cause connection churn. The `maxTimeMS` field MUST be appended after all blocking work is +complete. + +After wire message construction, drivers MUST check for timeout before writing the message to the server. If the timeout +has expired or the amount of time remaining is less than the selected server's minimum RTT, drivers MUST return the +connection to the pool and raise a timeout exception. Otherwise, drivers MUST set the connection’s write timeout to the +remaining `timeoutMS` value before writing a message to the server. After the write is complete, drivers MUST check for +timeout expiration before reading the server’s response. If the timeout has expired, the connection MUST be closed and a +timeout exception MUST be propagated to the application. If it has not, drivers MUST set the connection’s read timeout +to the remaining `timeoutMS` value. The timeout MUST apply to the aggregate of all reads done to receive a server +response, not to individual reads. If any read or write calls on the socket fail with a timeout, drivers MUST transform +the error into the new timeout exception as described in the [Error Transformations](#error-transformations) section. + +If `timeoutMS` is not set and support for `socketTimeoutMS` has not been removed, drivers MUST honor `socketTimeoutMS` +as the timeout for socket reads and writes. + +See [maxTimeMS accounts for server RTT](#maxtimems-accounts-for-server-rtt). + +#### Batching + +If an operation must be sent to the server in multiple batches (e.g. `collection.bulkWrite()`), the `timeoutMS` option +MUST apply to the entire operation, not to each individual batch. + +#### Retryability + +If an operation requires a retry per the retryable reads or writes specifications and `timeoutMS` is set, drivers MUST +retry operations as many times as possible before the timeout expires or a retry attempt returns a non-retryable error. +Once the timeout expires, a timeout error MUST be raised. + +See +[Why don’t drivers use backoff/jitter between retry attempts?](#why-dont-drivers-use-backoffjitter-between-retry-attempts). + +#### Client Side Encryption + +If automatic client-side encryption or decryption is enabled, the remaining `timeoutMS` value MUST be used as the +`timeoutMS` when executing `listCollections` commands to retrieve collection schemas, `find` commands to get data from +the key vault, and any commands against mongocryptd. It MUST also be used as the request timeout for HTTP requests +against KMS servers to decrypt data keys. When sending a command to mongocryptd, drivers MUST NOT append a `maxTimeMS` +field. This is to ensure that a `maxTimeMS` field can be safely appended to the command after it has been marked by +mongocryptd and encrypted by libmongocrypt. To determine whether or not the server is a mongocryptd, drivers MUST check +that the `iscryptd` field in the server's description is `true`. + +For explicit encryption and decryption, the `ClientEncryptionOpts` options type used to construct +[ClientEncryption](../client-side-encryption/client-side-encryption.md#clientencryption) instances MUST support a new +`timeoutMS` option, which specifies the timeout for all operations executed on the `ClientEncryption` object. + +See [maxTimeMS is not added for mongocryptd](#maxtimems-is-not-added-for-mongocryptd). + +### Background Connection Pooling + +Connections created as part of a connection pool’s `minPoolSize` maintenance routine MUST use `connectTimeoutMS` as the +timeout for connection establishment. After the connection is established, if `timeoutMS` is set at the MongoClient +level, it MUST be used as the timeout for all commands sent as part of the MongoDB or authentication handshakes. The +timeout MUST be refreshed after each command. These commands MUST set timeouts per the +[Command Execution](#command-execution) section. If `timeoutMS` is not set, drivers MUST continue to honor +`socketTimeoutMS` as the socket timeout for handshake and authentication commands. + +### Server Monitoring + +Drivers MUST NOT use `timeoutMS` for commands executed by the server monitoring and RTT calculation threads. + +See [Monitoring threads do not use timeoutMS](#monitoring-threads-do-not-use-timeoutms). + +### Cursors + +For operations that create cursors, `timeoutMS` can either cap the lifetime of the cursor or be applied separately to +the original operation and all `next` calls. To support both of these use cases, these operations MUST support a +`timeoutMode` option. This option is an enum with possible values `CURSOR_LIFETIME` and `ITERATION`. The default value +depends on the type of cursor being created. Drivers MUST error if `timeoutMode` is set and `timeoutMS` is not. + +When applying the `timeoutMS` option to `next` calls on cursors, drivers MUST ensure it applies to the entire call, not +individual commands. For drivers that send `getMore` requests in a loop when iterating tailable cursors, the timeout +MUST apply to the totality of all `getMore`’s, not to each one individually. If a resume is required for a `next` call +on a change stream, the timeout MUST apply to the entirety of the initial `getMore` and all commands sent as part of the +resume attempt. + +For `close` methods, drivers MUST allow `timeoutMS` to be overridden if doing so is possible in the language. If +explicitly set for the operation, it MUST be honored. Otherwise, if `timeoutMS` was applied to the operation that +created the cursor, it MUST be refreshed for the `killCursors` command if one is required. Note that this means +`timeoutMS` will be refreshed for the `close` call even if the cursor was created with a `timeoutMode` of +`CURSOR_LIFETIME` and the timeout associated with the cursor has expired. The calculated timeout MUST apply to explicit +`close` methods that can be invoked by users as well as implicit destructors that are automatically invoked when exiting +resource blocks. + +See [Cursor close() methods refresh timeoutMS](#cursor-close-methods-refresh-timeoutms). + +#### Non-tailable Cursors + +For non-tailable cursors, the default value of `timeoutMode` is `CURSOR_LIFETIME`. If `timeoutMS` is set, drivers MUST +apply it to the original operation and the lifetime of the created cursor. For example, if a `find` is executed at time +`T`, the `find` and all `getMore`’s on the cursor must finish by time `T + timeoutMS`. When executing `next` calls on +the cursor, drivers MUST use the remaining timeout as the `timeoutMS` value for the operation but MUST NOT append a +`maxTimeMS` field to `getMore` commands. If there are documents remaining in a previously retrieved batch, the `next` +method MUST return them even if the timeout has expired and MUST only return a timeout error if a `getMore` is required. + +If `timeoutMode` is set to `ITERATION`, drivers MUST raise a client-side error if the operation is an `aggregate` with a +`$out` or `$merge` pipeline stage. If the operation is not an `aggregate` with `$out` or `$merge`, drivers MUST honor +the `timeoutMS` option for the initial command but MUST NOT append a `maxTimeMS` field to the command sent to the +server. After the operation has executed, the original `timeoutMS` value MUST also be applied to each `next` call on the +created cursor. Drivers MUST NOT append a `maxTimeMS` field to `getMore` commands. + +See [Non-tailable cursor behavior](#non-tailable-cursor-behavior). + +#### Tailable Cursors + +Tailable cursors only support the `ITERATION` value for the `timeoutMode` option. This is the default value and drivers +MUST error if the option is set to `CURSOR_LIFETIME`. + +##### Tailable non-awaitData Cursors + +If `timeoutMS` is set, drivers MUST apply it separately to the original operation and to all `next` calls on the +resulting cursor but MUST NOT append a `maxTimeMS` field to any commands. + +##### Tailable awaitData Cursors + +If `timeoutMS` is set, drivers MUST apply it to the original operation. Drivers MUST also apply the original `timeoutMS` +value to each `next` call on the resulting cursor. Helpers for operations that create tailable awaitData cursors MUST +also support the `maxAwaitTimeMS` option. Drivers MUST error if this option is set, `timeoutMS` is set to a non-zero +value, and `maxAwaitTimeMS` is greater than or equal to `timeoutMS`. If this option is set, drivers MUST use +`min(maxAwaitTimeMS, remaining timeoutMS - minRoundTripTime)` as the `maxTimeMS` field on `getMore` commands. + +See [Tailable cursor behavior](#tailable-cursor-behavior) for rationale regarding both non-awaitData and awaitData +cursors. + +#### Change Streams + +Driver `watch` helpers MUST support both `timeoutMS` and `maxAwaitTimeMS` options. Drivers MUST error if +`maxAwaitTimeMS` is set, `timeoutMS` is set to a non-zero value, and `maxAwaitTimeMS` is greater than or equal to +`timeoutMS`. These helpers MUST NOT support the `timeoutMode` option as change streams are an abstraction around +tailable-awaitData cursors, so they implicitly use `ITERATION` mode. If set, drivers MUST apply the `timeoutMS` option +to the initial `aggregate` operation. Drivers MUST also apply the original `timeoutMS` value to each `next` call on the +change stream. If this option is set, drivers MUST use `min(maxAwaitTimeMS, remaining timeoutMS - minRoundTripTime)` as +the `maxTimeMS` field on `getMore` commands. + +If a `next` call fails with a timeout error, drivers MUST NOT invalidate the change stream. The subsequent `next` call +MUST perform a resume attempt to establish a new change stream on the server. Any errors from the `aggregate` operation +done to create a new change stream MUST be propagated to the application. Drivers MUST document that users can either +call `next` again or close the existing change stream and create a new one if a previous `next` call times out. The +documentation MUST suggest closing and re-creating the stream with a higher timeout if the timeout occurs before any +events have been received because this is a signal that the server is timing out before it can finish processing the +existing oplog. + +See [Change stream behavior](#change-stream-behavior). + +### Sessions + +The [SessionOptions](../sessions/driver-sessions.md#mongoclient-changes) used to construct explicit +[ClientSession](../sessions/driver-sessions.md#clientsession) instances MUST accept a new `defaultTimeoutMS` option, +which specifies the `timeoutMS` value for the following operations executed on the session: + +1. commitTransaction +2. abortTransaction +3. withTransaction +4. endSession + +If this option is not specified for a `ClientSession`, it MUST inherit the `timeoutMS` of its parent MongoClient. + +#### Session checkout + +As noted in [Blocking Sections for Operation Execution](#blocking-sections-for-operation-execution), implicit session +checkout can be considered a blocking process for some drivers. Such drivers MUST apply the remaining `timeoutMS` value +to this process when executing an operation. For explicit session checkout, drivers MUST apply the `timeoutMS` value of +the MongoClient to the `startSession` call if set. Drivers MUST NOT allow users to override `timeoutMS` for +`startSession` operations. + +See [timeoutMS cannot be overridden for startSession calls](#timeoutms-cannot-be-overridden-for-startsession-calls). + +#### Convenient Transactions API + +If `timeoutMS` is set, drivers MUST apply it to the entire `withTransaction` call. To propagate the timeout to the +user-supplied callback, drivers MUST store the timeout as a field on the ClientSession object. This field SHOULD be +private to ensure that a user can not modify it while a `withTransaction` call is in progress. Drivers that cannot make +this field private MUST signal that the field should not be accessed or modified by users if there is an idiomatic way +to do so in the language (e.g. underscore-prefixed variable names in Python) and MUST document that modification of the +field can cause unintended correctness issues for applications. Drivers MUST document that the remaining timeout will +not be applied to callback operations that do not use the ClientSession. Drivers MUST also document that overriding +`timeoutMS` for operations executed using the explicit session inside the provided callback will result in a client-side +error, as defined in [Validation and Overrides](#validation-and-overrides). If the callback returns an error and the +transaction must be aborted, drivers MUST refresh the `timeoutMS` value for the `abortTransaction` operation. + +If `timeoutMS` is not set, drivers MUST continue to exhibit the existing 120 second timeout behavior. Drivers MUST NOT +change existing implementations to use `timeoutMS=120000` for this case. + +See +[withTransaction communicates timeoutMS via ClientSession](#withtransaction-communicates-timeoutms-via-clientsession) +and +[withTransaction refreshes the timeout for abortTransaction](#withtransaction-refreshes-the-timeout-for-aborttransaction). + +### GridFS API + +GridFS buckets MUST inherit `timeoutMS` from their parent MongoDatabase instance and all methods in the GridFS Bucket +API MUST support the `timeoutMS` option. For methods that create streams (e.g. `open_upload_stream`), the option MUST +cap the lifetime of the entire stream. This MUST include the time taken by any operations executed during stream +construction, reads/writes, and close/abort calls. For example, if a stream is created at time `T`, the final `close` +call on the stream MUST finish all blocking work before time `T + timeoutMS`. Methods that interact with a user-provided +stream (e.g. `upload_from_stream`) MUST use `timeoutMS` as the timeout for the entire upload/download operation. If the +user-provided streams do not support timeouts, drivers MUST document that the timeout for these methods may be breached +if calls to interact with the stream take longer than the remaining timeout. If `timeoutMS` is set, all cursors created +for GridFS API operations MUST internally set the `timeoutMode` option to `CURSOR_LIFETIME`. + +See [GridFS streams behavior](#gridfs-streams-behavior). + +### RunCommand + +The behavior of `runCommand` is undefined if the provided command document includes a `maxTimeMS` field and the +`timeoutMS` option is set. Drivers MUST document the behavior of `runCommand` for this case and MUST NOT attempt to +check the command document for the presence of a `maxTimeMS` field. + +See [runCommand behavior](#runcommand-behavior). + +### Explain + +> [!NOTE] +> This portion of the specification is only relevant for drivers that provide `explain` helpers. + +When `timeoutMS` is specified, drivers MUST provide a way to specify timeoutMS that results in maxTimeMS being set on +the `explain` command. For example, Node's implementation might look like: + +```typescript +collection.find({}).explain({ timeoutMS: 1000 }); +// sends: +{ explain: { find: ... }, maxTimeMS: } +``` + +## Test Plan + +See the [README.md](tests/README.md) in the tests directory. + +## Motivation for Change + +Users have many options to set timeouts for various parts of operation execution including, but not limited to, +`serverSelectionTimeoutMS`, `socketTimeoutMS`, `connectTimeoutMS`, `maxTimeMS`, and `wTimeoutMS`. As a result, users are +often unsure which timeout to use. Because some of these timeouts are additive, it is difficult to set a combination +which ensures control will be returned to the user after a specified amount of time. To make timeouts more intuitive, +changes are required to the drivers API to deprecate some of the existing timeouts and add a new one to specify the +maximum execution time for an entire operation from start to finish. + +In addition, automatically retrying reads and writes that failed due to transient network blips or planned maintenance +scenarios has improved application resiliency but the original behavior of only retrying once still allowed some errors +to be propagated to applications. Supporting a timeout for an entire operation allows drivers to retry operations +multiple times while still guaranteeing that an application can get back control once the specified amount of time has +elapsed. + +## Design Rationale + +### timeoutMS cannot be changed to unset once it's specified + +If `timeoutMS` is specified at any level, it cannot be later changed to unset at a lower level. For example, a user +cannot do: + +```python +client = MongoClient(uri, timeoutMS=1000) +db = client.database("foo", timeoutMS=None) +``` + +This is because drivers return existing exception types if `timeoutMS` is not specified, but will return new exception +types and use new timeout behaviors if it is. Once the user has opted into this behavior, we should not allow them to +opt out of it at a lower level. If a user wishes to set the timeout to infinite for a specific database, collection, or +operation, they can explicitly set `timeoutMS` to 0. + +### serverSelectionTimeoutMS is not deprecated + +The original goal of the project was to expose a single timeout and deprecate all others. This was not possible, +however, because executing an operation consists of two distinct parts. The first is selecting a server and checking out +a connection from its pool. This should have a default timeout because failure to do this indicates that the deployment +is not in a healthy state or that there was a configuration error which prevents the driver from successfully +connecting. The second is server-side operation execution, which cannot have a default timeout. Some operations finish +in a few milliseconds, while others can run for many hours. Adding a default would inevitably break applications. To +accomplish both of these goals, `serverSelectionTimeoutMS` was preserved and is used to timeout the client-side section +of operation execution. + +### connectTimeoutMS is not deprecated + +Similar to the reasoning for not deprecating `serverSelectionTimeoutMS`, socket establishment should have a default +timeout because failure to create a socket likely means that the target server is not healthy or there is a network +issue. To accomplish this, the `connectTimeoutMS` option is not deprecated by this specification. Drivers also use +`connectTimeoutMS` to derive a socket timeout for monitoring connections, which are not subject to timeoutMS. + +### timeoutMS overrides deprecated timeout options + +Applying both `timeoutMS` and a deprecated timeout option like `socketTimeoutMS` at the same time would lead to +confusing semantics that are difficult to document and understand. When first writing this specification, we considered +having drivers error in this situation to catch mismatched timeouts as early as possible. However, because `timeoutMS` +can be set at any level, this behavior could lead to unanticipated runtime errors if an application set `timeoutMS` for +a specific operation and the MongoClient used in production was configured with a deprecated timeout option. To have +clear semantics and avoid unexpected errors in applications, we decided that `timeoutMS` should override deprecated +timeout options. + +### maxTimeMS is not added for mongocryptd + +The mongocryptd server annotates the provided command to indicate encryption requirements and returns the marked up +result. If the command sent to mongocryptd contained `maxTimeMS`, the final command sent to MongoDB would contain two +`maxTimeMS` fields: one added by the regular MongoClient and another added by the mongocryptd client. To avoid this +complication, drivers do not add this field when sending commands to mongocryptd at all. Doing so does not sacrifice any +functionality because mongocryptd always runs on localhost and does not perform any blocking work, so execution or +network timeouts cannot occur. + +### maxTimeMS accounts for server RTT + +When constructing a command, drivers use the `timeoutMS` option to derive a value for the `maxTimeMS` command option and +the socket timeout. The full time to round trip a command is (network RTT + server-side execution time). If both +`maxTimeMS` and socket timeout were set to the same value, the server would never be able to respond with a +`MaxTimeMSExpired` error because drivers would hit the socket timeout first and close the connection. This would lead to +connection churn if the specified timeout is too low. To allow the server to gracefully error and avoid churn, drivers +must account for the network round trip in the `maxTimeMS` calculation. + +### Monitoring threads do not use timeoutMS + +Using `timeoutMS` in the monitoring and RTT calculation threads would require another special case in the code that +derives `maxTimeMS` from `timeoutMS` because the awaitable `hello` requests sent to 4.4+ servers already have a +`maxAwaitTimeMS` field. Adding `maxTimeMS` also does not help for non-awaitable `hello` commands because we expect them +to execute quickly on the server. The Server Monitoring spec already mandates that drivers set and dynamically update +the read/write timeout of the dedicated connections used in monitoring threads, so we rely on that to time out commands +rather than adding complexity to the behavior of `timeoutMS`. + +### runCommand behavior + +The behavior of runCommand varies across drivers. If the provided command document includes a `maxTimeMS` field and the +`timeoutMS` option is set, some drivers would overwrite the `maxTimeMS` field with the value derived from `timeoutMS`, +while others would append a second `maxTimeMS` field, which would cause a server error on versions 3.4+. To be +prescriptive, we could mandate that drivers raise a client-side error in this case, but this would require a potentially +expensive lookup in the command document. To avoid this additional cost, drivers are only required to document the +behavior and suggest that `timeoutMS` be used instead of including a manual `maxTimeMS` field. + +### Why don't drivers use backoff/jitter between retry attempts? + +Earlier versions of this specification proposed adding backoff and/or jitter between retry attempts to avoid connection +storming or overloading the server, but we later deemed this unnecessary. If multiple concurrent operations select the +same server for a retry and its connection pool is empty, we rely on the `maxConnecting` parameter introduced in +DRIVERS-781 to rate limit new connection attempts, which mitigates the risk of connection storms. Even if the new server +has enough connections in its pool to service the operations, recent server versions do very little resource-intensive +work until execution reaches the storage layer, which is already guarded by read/write tickets, so we don’t expect the +server to be overwhelmed. If we later decide that adding jitter would be useful, it may be easier to do so in the server +itself via a ticket-based admission system earlier in the execution stack. + +### Cursor close() methods refresh timeoutMS + +If a cursor times out client-side (e.g. a non-tailable cursor created with `timeoutMode=CURSOR_LIFETIME`), it’s +imperative that drivers make a good-faith effort to close the server-side cursor even though the timeout has expired +because failing to do so would leave resources open on the server for a potentially long time. It was decided that +`timeoutMS` will be refreshed for `close` operations to allow the cursor to be killed server-side. + +### Non-tailable cursor behavior + +There are two usage patterns for non-tailable cursors. The first is to read documents from a cursor into an iterable +object, either by explicitly iterating the cursor in a loop or using a language construct like Python list +comprehensions. To supply a timeout for the entire process, drivers use `timeoutMS` to cap the execution time for the +initial command and all required `getMore`’s. This use case also matches the server behavior; if `maxTimeMS` is set for +an operation that creates a non-tailable cursor, the server will use the time limit to cap the total server-side +execution time for future `getMore`’s. Because this type of usage matches the server behavior and is the more common +case, this is the default behavior. + +The second use case is batch processing, where the user takes advantage of the lazy nature of cursors to process +documents from a large collection. In this case, the user does not want all documents from the collection to be in an +array because that would require too much memory. To accommodate this use case, drivers support a new `timeoutMode` +option. Users can set the value for this option to `ITERATION` to have `timeoutMS` apply to the original command and +then separately to each `next` call. When this option is used, drivers do not set `maxTimeMS` on the initial command to +avoid capping the cursor lifetime in the server. + +### Tailable cursor behavior + +Once a tailable cursor is created, it conceptually lives forever. Therefore, it only makes sense to support +`timeoutMode=ITERATION` for these cursors and drivers error if `timeoutMode=CURSOR_LIFETIME` is specified. + +There are two types of tailable cursors. The first, tailable non-awaitData cursors, support `maxTimeMS` for the original +command but not for any `getMore` requests. However, setting `maxTimeMS` on the original command also incorrectly caps +the server-side execution time for future `getMore`’s ([SERVER-51153](http://jira.mongodb.org/browse/SERVER-51153)). +This is undesirable behavior because it does not match the guarantees made by `timeoutMode=ITERATION`. To work around +this, drivers honor `timeoutMS` for both the original operation and all `getMore`’s but only use it to derive +client-side timeouts and do not append a `maxTimeMS` field to any commands. The server-side execution time is enforced +via socket timeouts. + +The second type is tailable awaitData cursors. The server supports the `maxTimeMS` option for the original command. For +`getMore`’s, the option is supported, but instead of limiting the server-side execution time, it specifies how long the +server should wait for new data to arrive if it reaches the end of the capped collection and the batch is still empty. +If no new data arrives within that time limit, the server will respond with an empty batch. For these cursors, drivers +support both the `timeoutMS` and `maxAwaitTimeMS` options. The `timeoutMS` option is used to derive client-side +timeouts, while the `maxAwaitTimeMS` option is used as the `maxTimeMS` field for `getMore` commands. These values have +distinct meanings, so supporting both yields a more robust, albeit verbose, API. Drivers error if `maxAwaitTimeMS` is +greater than or equal to `timeoutMS` because in that case, `getMore` requests would not succeed if the batch was empty: +the server would wait for `maxAwaitTimeMS`, but the driver would close the socket after `timeoutMS`. + +For tailable awaitData cursors we use the `min(maxAwaitTimeMS, remaining timeoutMS - minRoundTripTime)` to allow the +server more opportunities to respond with an empty batch before a client-side timeout + +### Change stream behavior + +Change streams internally behave as tailable awaitData cursors, so the behavior of the `timeoutMS` option is the same +for both. The main difference is that change streams are resumable and drivers automatically perform resume attempts +when they encounter transient errors. This allows change streams to be resilient to timeouts. If `timeoutMS` expires +during a next call, drivers can’t auto-resume, but they can make sure the change stream is not invalidated so the user +can call next again. In this case, the subsequent call would perform the resume without doing a `getMore` first. + +### withTransaction communicates timeoutMS via ClientSession + +Because the `withTransaction` API doesn’t allow drivers to plumb down the remaining timeout into the user-provided +callback, this spec requires the remaining timeout to be stored on the ClientSession. Operations in the callback that +run under that ClientSession can then extract the timeout from the session and apply it. To avoid confusing validation +semantics, operations error if there is a timeout on the session but also an overridden timeout for the operation. It’s +possible that the ability to communicate timeouts for a block of operations via a ClientSession is useful as a general +purpose API, but we’ve decided to make it private until there are other known use cases. + +### withTransaction refreshes the timeout for abortTransaction + +If the user-provided callback to `withTransaction` times out, it could leave a transaction running on the server. It’s +imperative that drivers make an effort to abort the open transaction because failing to do so could result in the +collections and databases affected by the transaction being locked for a long period of time, which could cause +applications to stall. Because `timeoutMS` has expired before drivers attempt to abort the transaction, we require +drivers to refresh it and apply the original value to the execution of the `abortTransaction` operation. This can cause +the entire `withTransaction` call to take up to `2*timeoutMS`, but it was decided that this risk is worthwhile given the +importance of transaction cleanup. + +### GridFS streams behavior + +Streams created by GridFS API operations (e.g. by `open_upload_stream` and `open_download_stream`) present a challenge +for this specification. These types of streams execute multiple operations, but there can be artificial gaps between +operations if the application does not invoke the stream functions for long periods of time. Generally, we expect users +to upload or download an entire file as quickly as possible, so we decided to have `timeoutMS` cap the lifetime of the +created stream. The other option was to apply the entire `timeoutMS` value to each operation executed by the stream, but +streams perform many hidden operations, so this approach could cause an upload/download to take much longer than +expected. + +### timeoutMS cannot be overridden for startSession calls + +In general, users can override `timeoutMS` at the level of a single operation. The `startSession` operation, however, +only inherits `timeoutMS` from the MongoClient and does not allow the option to be overridden. This was a conscious API +design decision because drivers are moving towards only supporting MongoDB versions 3.6 and higher, so sessions will +always be supported. Adding an override for `startSession` would introduce a new knob and increase the API surface of +drivers without providing a significant benefit. + +### Drivers use minimum RTT to short circuit operations + +A previous version of this spec used the 90th percentile RTT to short circuit operations that might otherwise fail with +a socket timeout. We decided to change this logic to avoid canceling operations that may have a high chance of +succeeding and also remove a dependency on t-digest. Instead, drivers use the minimum RTT from the last 10 samples, or 0 +until at least 2 samples have been recorded. + +## Future work + +### Modify GridFS streams behavior via new options + +As explained in the design rationale, drivers use `timeoutMS` to cap the entire lifetime of streams created by GridFS +operations. If we find that users are often encountering timeout errors when using these APIs due to the time spent +during non-MongoDB operations (e.g. streaming data read from a GridFS stream into another data store), we could consider +toggling GridFS behavior via an option similar to `timeoutMode` for cursors. To avoid backwards-breaking behavioral +changes, the default would continue to cap the stream lifetime but there could be another mode that refreshes the +timeout for each database operation. This would mimic using `timeoutMode=ITERATION` for cursors. + +## Changelog + +- 2024-01-29: Adjust getMore maxTimeMS calculation for tailable awaitData cursors. +- 2024-09-12: Specify that explain helpers support support timeoutMS. +- 2023-12-07: Migrated from reStructuredText to Markdown. +- 2022-11-17: Use minimum RTT for maxTimeMS calculation instead of 90th percentile RTT. +- 2022-10-05: Remove spec front matter. +- 2022-01-19: Initial version. diff --git a/source/client-side-operations-timeout/client-side-operations-timeout.rst b/source/client-side-operations-timeout/client-side-operations-timeout.rst new file mode 100644 index 0000000000..fcff0f6c0f --- /dev/null +++ b/source/client-side-operations-timeout/client-side-operations-timeout.rst @@ -0,0 +1,3 @@ +.. note:: + This specification has been converted to Markdown and renamed to + `client-side-operations-timeout.md `_. diff --git a/source/client-side-operations-timeout/etc/generate-basic-tests.py b/source/client-side-operations-timeout/etc/generate-basic-tests.py new file mode 100644 index 0000000000..e70751c101 --- /dev/null +++ b/source/client-side-operations-timeout/etc/generate-basic-tests.py @@ -0,0 +1,134 @@ +from collections import namedtuple +from jinja2 import Template +import os +import sys + +Operation = namedtuple('Operation', ['operation_name', 'command_name', 'object', 'arguments']) + +CLIENT_OPERATIONS = [ + Operation('listDatabases', 'listDatabases', 'client', ['filter: {}']), + Operation('listDatabaseNames', 'listDatabases', 'client', []), + Operation('createChangeStream', 'aggregate', 'client', ['pipeline: []']) +] + +RUN_COMMAND_ARGUMENTS = '''command: { ping: 1 } + commandName: ping''' + +DB_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'database', ['pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ]']), + Operation('listCollections', 'listCollections', 'database', ['filter: {}']), + Operation('listCollectionNames', 'listCollections', 'database', ['filter: {}']), + Operation('runCommand', 'ping', 'database', [RUN_COMMAND_ARGUMENTS]), + Operation('createChangeStream', 'aggregate', 'database', ['pipeline: []']) +] + +INSERT_MANY_ARGUMENTS = '''documents: + - { x: 1 }''' + +BULK_WRITE_ARGUMENTS = '''requests: + - insertOne: + document: { _id: 1 }''' + +COLLECTION_READ_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'collection', ['pipeline: []']), + Operation('count', 'count', 'collection', ['filter: {}']), + Operation('countDocuments', 'aggregate', 'collection', ['filter: {}']), + Operation('estimatedDocumentCount', 'count', 'collection', []), + Operation('distinct', 'distinct', 'collection', ['fieldName: x', 'filter: {}']), + Operation('find', 'find', 'collection', ['filter: {}']), + Operation('findOne', 'find', 'collection', ['filter: {}']), + Operation('listIndexes', 'listIndexes', 'collection', []), + Operation('listIndexNames', 'listIndexes', 'collection', []), + Operation('createChangeStream', 'aggregate', 'collection', ['pipeline: []']), +] +COLLECTION_WRITE_OPERATIONS = [ + Operation('insertOne', 'insert', 'collection', ['document: { x: 1 }']), + Operation('insertMany', 'insert', 'collection', [INSERT_MANY_ARGUMENTS]), + Operation('deleteOne', 'delete', 'collection', ['filter: {}']), + Operation('deleteMany', 'delete', 'collection', ['filter: {}']), + Operation('replaceOne', 'update', 'collection', ['filter: {}', 'replacement: { x: 1 }']), + Operation('updateOne', 'update', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), + Operation('updateMany', 'update', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), + Operation('findOneAndDelete', 'findAndModify', 'collection', ['filter: {}']), + Operation('findOneAndReplace', 'findAndModify', 'collection', ['filter: {}', 'replacement: { x: 1 }']), + Operation('findOneAndUpdate', 'findAndModify', 'collection', ['filter: {}', 'update: { $set: { x: 1 } }']), + Operation('bulkWrite', 'insert', 'collection', [BULK_WRITE_ARGUMENTS]), + Operation('createIndex', 'createIndexes', 'collection', ['keys: { x: 1 }', 'name: "x_1"']), + Operation('dropIndex', 'dropIndexes', 'collection', ['name: "x_1"']), + Operation('dropIndexes', 'dropIndexes', 'collection', []), +] +COLLECTION_OPERATIONS = COLLECTION_READ_OPERATIONS + COLLECTION_WRITE_OPERATIONS + +# Session and GridFS operations are generally tested in other files, so they're not included in the list of all +# operations. Individual generation functions can choose to include them if needed. +OPERATIONS = CLIENT_OPERATIONS + DB_OPERATIONS + COLLECTION_OPERATIONS + +RETRYABLE_WRITE_OPERATIONS = [op for op in OPERATIONS if op.operation_name in + ['insertOne', 'updateOne', 'deleteOne', 'replaceOne', 'findOneAndDelete', 'findOneAndUpdate', 'findOneAndReplace', 'insertMany', 'bulkWrite'] +] + +RETRYABLE_READ_OPERATIONS = [op for op in OPERATIONS if op.operation_name in + ['find', 'findOne', 'aggregate', 'distinct', 'count', 'estimatedDocumentCount', 'countDocuments', 'createChangeStream', 'listDatabases', + 'listDatabaseNames', 'listCollections', 'listCollectionNames', 'listIndexes'] +] + +templates_dir = sys.argv[1] +tests_dir = sys.argv[2] + +def get_template(file): + path = f'{templates_dir}/{file}.yml.template' + return Template(open(path, 'r').read()) + +def write_yaml(file, template, injections): + rendered = template.render(**injections) + path = f'{tests_dir}/{file}.yml' + open(path, 'w').write(rendered) + +def get_command_object(object): + if object == 'client' or object == 'database': + return 1 + return '*collectionName' + +def max_time_supported(operation_name): + return operation_name in ['aggregate', 'count', 'estimatedDocumentCount', 'distinct', 'find', 'findOne', + 'findOneAndDelete', 'findOneAndReplace', 'findOneAndUpdate', 'createIndex', 'dropIndex', 'dropIndexes'] + +def generate(name, operations): + template = get_template(name) + injections = { + 'operations': operations, + 'get_command_object': get_command_object, + 'max_time_supported': max_time_supported, + } + write_yaml(name, template, injections) + +# TODO(DRIVERS-3266): Investigate dropping generator script for index-related +# timeoutMS tests +#def generate_global_timeout_tests(): +# generate('global-timeoutMS', OPERATIONS) +# +#def generate_override_db(): +# generate('override-database-timeoutMS', DB_OPERATIONS + COLLECTION_OPERATIONS) +# +#def generate_override_coll(): +# generate('override-collection-timeoutMS', COLLECTION_OPERATIONS) +# +#def generate_override_operation(): +# generate('override-operation-timeoutMS', OPERATIONS) +# +#def generate_deprecated(): +# generate('deprecated-options', OPERATIONS) + +def generate_retryable(): + generate('retryability-timeoutMS', RETRYABLE_WRITE_OPERATIONS + RETRYABLE_READ_OPERATIONS) + generate('retryability-legacy-timeouts', RETRYABLE_WRITE_OPERATIONS + RETRYABLE_READ_OPERATIONS) + +# TODO(DRIVERS-3266): Investigate dropping generator script for index-related +# timeoutMS tests +#generate_global_timeout_tests() +#generate_override_db() +#generate_override_coll() +#generate_override_operation() +#generate_deprecated() + +generate_retryable() diff --git a/source/client-side-operations-timeout/etc/templates/deprecated-options.yml.template b/source/client-side-operations-timeout/etc/templates/deprecated-options.yml.template new file mode 100644 index 0000000000..600750b10f --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/deprecated-options.yml.template @@ -0,0 +1,630 @@ +description: "operations ignore deprecated timeout options if timeoutMS is set" + +schemaVersion: "1.9" + +# Most tests in this file can be executed against any server version, but some tests execute operations that are only +# available on higher server versions (e.g. abortTransaction). To avoid too many special cases in templated tests, the +# min server version is set to 4.2 for all. +runOnRequirements: + - minServerVersion: "4.2" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, run these tests: + # + # 1. socketTimeoutMS is ignored if timeoutMS is set. The test creates a client with socketTimeoutMS=1, configures and + # a failpoint to block the operation for 5ms, runs the operation with timeoutMS=10000, and expects it to succeed. + # + # 2. wTimeoutMS is ignored if timeoutMS is set. The test creates a client with wTimeoutMS=1, runs the operation with + # timeoutMS=10000, expects the operation to succeed, and uses command monitoring expectations to assert that the + # command sent to the server does not contain a writeConcern field. + # + # 3. If the operation supports maxTimeMS, it ignores maxTimeMS if timeoutMS is set. The test executes the operation + # with timeoutMS=1000 and maxTimeMS=5000. It expects the operation to succeed and uses command monitoring expectations + # to assert that the actual maxTimeMS value sent was less than or equal to 100, thereby asserting that it was + # actually derived from timeoutMS. + + # Tests for commitTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. There is also one + # additional test to assert that maxCommitTimeMS is ignored if timeoutMS is set. + + - description: "commitTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for abortTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. + + - description: "abortTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "abortTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + # Tests for withTransaction. These are not included in the operations loop because the command monitoring + # expectations contain multiple commands. There is also one additional test to assert that maxCommitTimeMS is ignored + # if timeoutMS is set. + + - description: "withTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: withTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for operations that can be generated. + + {% for operation in operations %} + - description: "socketTimeoutMS is ignored if timeoutMS is set - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 5 + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 100000 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + + - description: "wTimeoutMS is ignored if timeoutMS is set - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 100000 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + {% if max_time_supported(operation.operation_name) -%} + - description: "maxTimeMS is ignored if timeoutMS is set - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$lte: *timeoutMS } + {% endif -%} + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/global-timeoutMS.yml.template b/source/client-side-operations-timeout/etc/templates/global-timeoutMS.yml.template new file mode 100644 index 0000000000..b1f24a5114 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/global-timeoutMS.yml.template @@ -0,0 +1,145 @@ +# Tests in this file are generated from global-timeoutMS.yml.template. + +description: "timeoutMS can be configured on a MongoClient" + +schemaVersion: "1.26" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, we execute two tests: + # + # 1. timeoutMS can be configured to a non-zero value on a MongoClient and is inherited by the operation. Each test + # constructs a client entity with timeoutMS=250 and configures a fail point to block the operation for 350ms so + # execution results in a timeout error. + # + # 2. timeoutMS can be set to 0 for a MongoClient. Each test constructs a client entity with timeoutMS=0 and + # configures a fail point to block the operation for 15ms. The tests expect the operation to succeed and the command + # sent to not contain a maxTimeMS field. + + {% for operation in operations -%} + - description: "timeoutMS can be configured on a MongoClient - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 350 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/override-collection-timeoutMS.yml.template b/source/client-side-operations-timeout/etc/templates/override-collection-timeoutMS.yml.template new file mode 100644 index 0000000000..d721812de1 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/override-collection-timeoutMS.yml.template @@ -0,0 +1,136 @@ +# Tests in this file are generated from override-collection-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for a MongoCollection" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + +initialData: + - collectionName: &collectionName coll + databaseName: *databaseName + documents: [] + +tests: + # For each collection-level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for a MongoCollection. Each test uses the client entity defined + # above to construct a collection entity with timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for a MongoCollection. Each test constructs a collection entity with + # timeoutMS=0 using the global client entity and configures a fail point to block the operation for 15ms. The + # operation should succeed and the command sent to the server should not contain a maxTimeMS field. + + {% for operation in operations -%} + - description: "timeoutMS can be configured on a MongoCollection - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/override-database-timeoutMS.yml.template b/source/client-side-operations-timeout/etc/templates/override-database-timeoutMS.yml.template new file mode 100644 index 0000000000..a8f543ef46 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/override-database-timeoutMS.yml.template @@ -0,0 +1,140 @@ +# Tests in this file are generated from override-database-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for a MongoDatabase" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each database-level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for a MongoDatabase. Each test constructs uses the client entity + # defined above to construct a database entity with timeoutMS=1000 and configures a fail point to block the operation + # for 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for a MongoDatabase. Each test constructs a database entity with timeoutMS=0 + # using the global client entity and configures a fail point to block the operation for 15ms. The operation should + # succeed and the command sent to the server should not contain a maxTimeMS field. + + {% for operation in operations -%} + - description: "timeoutMS can be configured on a MongoDatabase - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isClientError: false + isTimeoutError: false + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/override-operation-timeoutMS.yml.template b/source/client-side-operations-timeout/etc/templates/override-operation-timeoutMS.yml.template new file mode 100644 index 0000000000..68bee62ee3 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/override-operation-timeoutMS.yml.template @@ -0,0 +1,115 @@ +# Tests in this file are generated from override-operation-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for an operation" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for an operation. Each test executes an operation using one of + # the entities defined above with an overridden timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for an operation. Each test executes an operation using the entities defined + # above with an overridden timeoutMS=0 so the operation succeeds. + + {% for operation in operations -%} + - description: "timeoutMS can be configured for an operation - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 1000 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isTimeoutError: false # IndexNotFound + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 15 + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 0 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {% if operation.operation_name == "dropIndex" -%} + expectError: + isTimeoutError: false # IndexNotFound + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/retryability-legacy-timeouts.yml.template b/source/client-side-operations-timeout/etc/templates/retryability-legacy-timeouts.yml.template new file mode 100644 index 0000000000..ffb1bcde19 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/retryability-legacy-timeouts.yml.template @@ -0,0 +1,119 @@ +# Tests in this file are generated from retryability-legacy-timeouts.yml.template. + +description: "legacy timeouts behave correctly for retryable operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + socketTimeoutMS: 100 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run two tests: + # + # 1. Socket timeouts are retried once - Each test constructs a client entity with socketTimeoutMS=100, configures a + # fail point to block the operation once for 125ms, and expects the operation to succeed. + # + # 2. Operations fail after two consecutive socket timeouts - Same as (1) but the fail point is configured to block + # the operation twice and the test expects the operation to fail. + + {% for operation in operations -%} + - description: "operation succeeds after one socket timeout - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 125 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + - description: "operation fails after two consecutive socket timeouts - {{operation.operation_name}} on {{operation.object}}" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 125 + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + {% endfor %} diff --git a/source/client-side-operations-timeout/etc/templates/retryability-timeoutMS.yml.template b/source/client-side-operations-timeout/etc/templates/retryability-timeoutMS.yml.template new file mode 100644 index 0000000000..3e5c9cb987 --- /dev/null +++ b/source/client-side-operations-timeout/etc/templates/retryability-timeoutMS.yml.template @@ -0,0 +1,189 @@ +# Tests in this file are generated from retryability-timeoutMS.yml.template. + +description: "timeoutMS behaves correctly for retryable operations" + +schemaVersion: "1.26" + +# failCommand is available on 4.0+ replica sets and 4.2+ sharded clusters. +runOnRequirements: + - minServerVersion: "4.0" + topologies: ["replicaset"] + - minServerVersion: "4.2" + topologies: ["sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 100 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run three tests: + # + # 1. timeoutMS applies to the whole operation, not to individual attempts - Client timeoutMS=100 and the operation is + # fails with a retryable error after being blocked server-side for 60ms. The operation should fail with a timeout error + # because the second attempt should take it over the 100ms limit. This test only runs on 4.4+ because it uses the + # blockConnection option in failCommand. + # + # 2. operation is retried multiple times if timeoutMS is set to a non-zero value - Client timeoutMS=100 and the + # operation fails with a retryable error twice. Drivers should send the original operation and two retries, the + # second of which should succeed. + # + # 3. operation is retried multiple times if timeoutMS is set to a zero - Override timeoutMS to zero for the operation + # and set a fail point to force a retryable error twice. Drivers should send the original operation and two retries, + # the second of which should succeed. + # + # The fail points in these tests use error code 7 (HostNotFound) because it is a retryable error but does not trigger + # an SDAM state change so we don't lose any time to server rediscovery. The tests also explicitly specify an + # errorLabels array in the fail point to avoid behavioral differences among server types and ensure that the error + # will be considered retryable. + + {% for operation in operations -%} + - description: "timeoutMS applies to whole operation, not individual attempts - {{operation.operation_name}} on {{operation.object}}" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["{{operation.command_name}}"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: {{operation.operation_name}} + object: *{{operation.object}} + {% if operation.arguments|length > 0 -%} + arguments: + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + {%- endif %} + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - {{operation.operation_name}} on {{operation.object}}" + runOnRequirements: + {%- if operation.operation_name == 'createChangeStream' %} + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: ["replicaset"] + - minServerVersion: "4.3.1" # failCommand errorLabels option + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + {%- else %} + - minServerVersion: "4.3.1" # failCommand errorLabels option + {%- endif %} + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["{{operation.command_name}}"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 1000 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - {{operation.operation_name}} on {{operation.object}}" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["{{operation.command_name}}"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: {{operation.operation_name}} + object: *{{operation.object}} + arguments: + timeoutMS: 0 + {% for arg in operation.arguments -%} + {{arg}} + {% endfor %} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: {{operation.command_name}} + databaseName: {{ "admin" if operation.object == "client" else "*databaseName" }} + command: + {{operation.command_name}}: {{get_command_object(operation.object)}} + maxTimeMS: { $$exists: false } + {% endfor %} diff --git a/source/client-side-operations-timeout/tests/README.md b/source/client-side-operations-timeout/tests/README.md new file mode 100644 index 0000000000..e883aad593 --- /dev/null +++ b/source/client-side-operations-timeout/tests/README.md @@ -0,0 +1,667 @@ +# Client Side Operations Timeouts Tests + +______________________________________________________________________ + +## Introduction + +This document describes the tests that drivers MUST run to validate the behavior of the timeoutMS option. These tests +are broken up into automated YAML/JSON tests and additional prose tests. + +## Spec Tests + +This directory contains a set of YAML and JSON spec tests. Drivers MUST run these as described in the "Unified Test +Runner" specification. Because the tests introduced in this specification are timing-based, there is a risk that some of +them may intermittently fail without any bugs being present in the driver. As a mitigation, drivers MAY execute these +tests in two new Evergreen tasks that use single-node replica sets: one with only authentication enabled and another +with both authentication and TLS enabled. Drivers that choose to do so SHOULD use the `single-node-auth.json` and +`single-node-auth-ssl.json` files in the `drivers-evergreen-tools` repository to create these clusters. + +## Prose Tests + +There are some tests that cannot be expressed in the unified YAML/JSON format. For each of these tests, drivers MUST +create a MongoClient without the `timeoutMS` option set (referred to as `internalClient`). Any fail points set during a +test MUST be unset using `internalClient` after the test has been executed. All MongoClient instances created for tests +MUST be configured with read/write concern `majority`, read preference `primary`, and command monitoring enabled to +listen for `command_started` events. + +### 1. Multi-batch inserts + +This test MUST only run against standalones on server versions 4.4 and higher. The `insertMany` call takes an +exceedingly long time on replicasets and sharded clusters. Drivers MAY adjust the timeouts used in this test to allow +for differing bulk encoding performance. + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 2 + }, + data: { + failCommands: ["insert"], + blockConnection: true, + blockTimeMS: 1010 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) with `timeoutMS=2000`. + +4. Using `client`, insert 50 1-megabyte documents in a single `insertMany` call. + + - Expect this to fail with a timeout error. + +5. Verify that two `insert` commands were executed against `db.coll` as part of the `insertMany` call. + +### 2. maxTimeMS is not set for commands sent to mongocryptd + +This test MUST only be run against enterprise server versions 4.2 and higher. + +1. Launch a mongocryptd process on 23000. +2. Create a MongoClient (referred to as `client`) using the URI `mongodb://localhost:23000/?timeoutMS=1000`. +3. Using `client`, execute the `{ ping: 1 }` command against the `admin` database. +4. Verify via command monitoring that the `ping` command sent did not contain a `maxTimeMS` field. + +### 3. ClientEncryption + +Each test under this category MUST only be run against server versions 4.4 and higher. In these tests, `LOCAL_MASTERKEY` +refers to the following base64: + +```text +Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk +``` + +For each test, perform the following setup: + +1. Using `internalClient`, drop and create the `keyvault.datakeys` collection. + +2. Create a MongoClient (referred to as `keyVaultClient`) with `timeoutMS=10`. + +3. Create a `ClientEncryption` object that wraps `keyVaultClient` (referred to as `clientEncryption`). Configure this + object with `keyVaultNamespace` set to `keyvault.datakeys` and the following KMS providers map: + + ```javascript + { + "local": { "key": } + } + ``` + +#### createDataKey + +1. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: ["insert"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +2. Call `clientEncryption.createDataKey()` with the `local` KMS provider. + + - Expect this to fail with a timeout error. + +3. Verify that an `insert` command was executed against to `keyvault.datakeys` as part of the `createDataKey` call. + +#### encrypt + +1. Call `client_encryption.createDataKey()` with the `local` KMS provider. + + - Expect a BSON binary with subtype 4 to be returned, referred to as `datakeyId`. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: ["find"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +3. Call `clientEncryption.encrypt()` with the value `hello`, the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`, and the keyId `datakeyId`. + + - Expect this to fail with a timeout error. + +4. Verify that a `find` command was executed against the `keyvault.datakeys` collection as part of the `encrypt` call. + +#### decrypt + +1. Call `clientEncryption.createDataKey()` with the `local` KMS provider. + + - Expect this to return a BSON binary with subtype 4, referred to as `dataKeyId`. + +2. Call `clientEncryption.encrypt()` with the value `hello`, the algorithm + `AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic`, and the keyId `dataKeyId`. + + - Expect this to return a BSON binary with subtype 6, referred to as `encrypted`. + +3. Close and re-create the `keyVaultClient` and `clientEncryption` objects. + +4. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: ["find"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +5. Call `clientEncryption.decrypt()` with the value `encrypted`. + + - Expect this to fail with a timeout error. + +6. Verify that a `find` command was executed against the `keyvault.datakeys` collection as part of the `decrypt` call. + +### 4. Background Connection Pooling + +The tests in this section MUST only be run if the server version is 4.4 or higher and the URI has authentication fields +(i.e. a username and password). Each test in this section requires drivers to create a MongoClient and then wait for +some CMAP events to be published. Drivers MUST wait for up to 10 seconds and fail the test if the specified events are +not published within that time. + +#### timeoutMS used for handshake commands + +1. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 1 + }, + data: { + failCommands: ["saslContinue"], + blockConnection: true, + blockTimeMS: 15, + appName: "timeoutBackgroundPoolTest" + } + } + ``` + +2. Create a MongoClient (referred to as `client`) configured with the following: + + - `minPoolSize` of 1 + - `timeoutMS` of 10 + - `appName` of `timeoutBackgroundPoolTest` + - CMAP monitor configured to listen for `ConnectionCreatedEvent` and `ConnectionClosedEvent` events. + +3. Wait for a `ConnectionCreatedEvent` and a `ConnectionClosedEvent` to be published. + +#### timeoutMS is refreshed for each handshake command + +1. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["hello", "isMaster", "saslContinue"], + blockConnection: true, + blockTimeMS: 15, + appName: "refreshTimeoutBackgroundPoolTest" + } + } + ``` + +2. Create a MongoClient (referred to as `client`) configured with the following: + + - `minPoolSize` of 1 + - `timeoutMS` of 20 + - `appName` of `refreshTimeoutBackgroundPoolTest` + - CMAP monitor configured to listen for `ConnectionCreatedEvent` and `ConnectionReady` events. + +3. Wait for a `ConnectionCreatedEvent` and a `ConnectionReady` to be published. + +### 5. Blocking Iteration Methods + +Tests in this section MUST only be run against server versions 4.4 and higher and only apply to drivers that have a +blocking method for cursor iteration that executes `getMore` commands in a loop until a document is available or an +error occurs. + +#### Tailable cursors + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, insert the document `{ x: 1 }` into `db.coll`. + +3. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["getMore"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +4. Create a new MongoClient (referred to as `client`) with `timeoutMS=20`. + +5. Using `client`, create a tailable cursor on `db.coll` with `cursorType=tailable`. + + - Expect this to succeed and return a cursor with a non-zero ID. + +6. Call either a blocking or non-blocking iteration method on the cursor. + + - Expect this to succeed and return the document `{ x: 1 }` without sending a `getMore` command. + +7. Call the blocking iteration method on the resulting cursor. + + - Expect this to fail with a timeout error. + +8. Verify that a `find` command and two `getMore` commands were executed against the `db.coll` collection during the + test. + +#### Change Streams + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["getMore"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) with `timeoutMS=20`. + +4. Using `client`, use the `watch` helper to create a change stream against `db.coll`. + + - Expect this to succeed and return a change stream with a non-zero ID. + +5. Call the blocking iteration method on the resulting change stream. + + - Expect this to fail with a timeout error. + +6. Verify that an `aggregate` command and two `getMore` commands were executed against the `db.coll` collection during + the test. + +### 6. GridFS - Upload + +Tests in this section MUST only be run against server versions 4.4 and higher. Drivers SHOULD apply +[useMultipleMongoses=false](../../unified-test-format/unified-test-format.md#entity) as described in the unified test +format when testing on sharded clusters to ensure failpoint are hit by only using one mongos. + +#### uploads via openUploadStream can be timed out + +1. Using `internalClient`, drop and re-create the `db.fs.files` and `db.fs.chunks` collections. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + blockConnection: true, + blockTimeMS: 200 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) with `timeoutMS=150`. + +4. Using `client`, create a GridFS bucket (referred to as `bucket`) that wraps the `db` database. + +5. Call `bucket.open_upload_stream()` with the filename `filename` to create an upload stream (referred to as + `uploadStream`). + + - Expect this to succeed and return a non-null stream. + +6. Using `uploadStream`, upload a single `0x12` byte. + +7. Call `uploadStream.close()` to flush the stream and insert chunks. + + - Expect this to fail with a timeout error. + +#### Aborting an upload stream can be timed out + +This test only applies to drivers that provide an API to abort a GridFS upload stream. + +1. Using `internalClient`, drop and re-create the `db.fs.files` and `db.fs.chunks` collections. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["delete"], + blockConnection: true, + blockTimeMS: 200 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) with `timeoutMS=150`. + +4. Using `client`, create a GridFS bucket (referred to as `bucket`) that wraps the `db` database with + `chunkSizeBytes=2`. + +5. Call `bucket.open_upload_stream()` with the filename `filename` to create an upload stream (referred to as + `uploadStream`). + + - Expect this to succeed and return a non-null stream. + +6. Using `uploadStream`, upload the bytes `[0x01, 0x02, 0x03, 0x04]`. + +7. Call `uploadStream.abort()`. + + - Expect this to fail with a timeout error. + +### 7. GridFS - Download + +This test MUST only be run against server versions 4.4 and higher. Drivers SHOULD apply +[useMultipleMongoses=false](../../unified-test-format/unified-test-format.md#entity) as described in the unified test +format when testing on sharded clusters to ensure failpoint are hit by only using one mongos. + +1. Using `internalClient`, drop and re-create the `db.fs.files` and `db.fs.chunks` collections. + +2. Using `internalClient`, insert the following document into the `db.fs.files` collection: + + ```javascript + { + "_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": {} + } + ``` + +3. Create a new MongoClient (referred to as `client`) with `timeoutMS=150`. + +4. Using `client`, create a GridFS bucket (referred to as `bucket`) that wraps the `db` database. + +5. Call `bucket.open_download_stream` with the id `{ "$oid": "000000000000000000000005" }` to create a download stream + (referred to as `downloadStream`). + + - Expect this to succeed and return a non-null stream. + +6. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + blockConnection: true, + blockTimeMS: 200 + } + } + ``` + +7. Read from the `downloadStream`. + + - Expect this to fail with a timeout error. + +8. Verify that two `find` commands were executed during the read: one against `db.fs.files` and another against + `db.fs.chunks`. + +### 8. Server Selection + +#### serverSelectionTimeoutMS honored if timeoutMS is not set + +1. Create a MongoClient (referred to as `client`) with URI `mongodb://invalid/?serverSelectionTimeoutMS=10`. +2. Using `client`, execute the command `{ ping: 1 }` against the `admin` database. + - Expect this to fail with a server selection timeout error after no more than 15ms. + +#### timeoutMS honored for server selection if it's lower than serverSelectionTimeoutMS + +1. Create a MongoClient (referred to as `client`) with URI + `mongodb://invalid/?timeoutMS=10&serverSelectionTimeoutMS=20`. +2. Using `client`, run the command `{ ping: 1 }` against the `admin` database. + - Expect this to fail with a server selection timeout error after no more than 15ms. + +#### serverSelectionTimeoutMS honored for server selection if it's lower than timeoutMS + +1. Create a MongoClient (referred to as `client`) with URI + `mongodb://invalid/?timeoutMS=20&serverSelectionTimeoutMS=10`. +2. Using `client`, run the command `{ ping: 1 }` against the `admin` database. + - Expect this to fail with a server selection timeout error after no more than 15ms. + +#### serverSelectionTimeoutMS honored for server selection if timeoutMS=0 + +1. Create a MongoClient (referred to as `client`) with URI `mongodb://invalid/?timeoutMS=0&serverSelectionTimeoutMS=10`. +2. Using `client`, run the command `{ ping: 1 }` against the `admin` database. + - Expect this to fail with a server selection timeout error after no more than 15ms. + +#### timeoutMS honored for connection handshake commands if it's lower than serverSelectionTimeoutMS + +This test MUST only be run if the server version is 4.4 or higher and the URI has authentication fields (i.e. a username +and password). + +1. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: failCommand, + mode: { times: 1 }, + data: { + failCommands: ["saslContinue"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +2. Create a new MongoClient (referred to as `client`) with `timeoutMS=10` and `serverSelectionTimeoutMS=20`. + +3. Using `client`, insert the document `{ x: 1 }` into collection `db.coll`. + + - Expect this to fail with a timeout error after no more than 15ms. + +#### serverSelectionTimeoutMS honored for connection handshake commands if it's lower than timeoutMS + +This test MUST only be run if the server version is 4.4 or higher and the URI has authentication fields (i.e. a username +and password). + +1. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: failCommand, + mode: { times: 1 }, + data: { + failCommands: ["saslContinue"], + blockConnection: true, + blockTimeMS: 15 + } + } + ``` + +2. Create a new MongoClient (referred to as `client`) with `timeoutMS=20` and `serverSelectionTimeoutMS=10`. + +3. Using `client`, insert the document `{ x: 1 }` into collection `db.coll`. + + - Expect this to fail with a timeout error after no more than 15ms. + +### 9. endSession + +This test MUST only be run against replica sets and sharded clusters with server version 4.4 or higher. It MUST be run +three times: once with the timeout specified via the MongoClient `timeoutMS` option, once with the timeout specified via +the ClientSession `defaultTimeoutMS` option, and once more with the timeout specified via the `timeoutMS` option for the +`endSession` operation. In all cases, the timeout MUST be set to 150 milliseconds. + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: failCommand, + mode: { times: 1 }, + data: { + failCommands: ["abortTransaction"], + blockConnection: true, + blockTimeMS: 200 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) and an explicit ClientSession derived from that MongoClient + (referred to as `session`). + +4. Execute the following code: + + ```typescript + coll = client.database("db").collection("coll") + session.start_transaction() + coll.insert_one({x: 1}, session=session) + ``` + +5. Using `session`, execute `session.end_session` + + - Expect this to fail with a timeout error after no more than 150ms. + +### 10. Convenient Transactions + +Tests in this section MUST only run against replica sets and sharded clusters with server versions 4.4 or higher. + +#### timeoutMS is refreshed for abortTransaction if the callback fails + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: failCommand, + mode: { times: 2 }, + data: { + failCommands: ["insert", "abortTransaction"], + blockConnection: true, + blockTimeMS: 200 + } + } + ``` + +3. Create a new MongoClient (referred to as `client`) configured with `timeoutMS=150` and an explicit ClientSession + derived from that MongoClient (referred to as `session`). + +4. Using `session`, execute a `withTransaction` operation with the following callback: + + ```typescript + def callback() { + coll = client.database("db").collection("coll") + coll.insert_one({ _id: 1 }, session=session) + } + ``` + +5. Expect the previous `withTransaction` call to fail with a timeout error. + +6. Verify that the following events were published during the `withTransaction` call: + + 1. `command_started` and `command_failed` events for an `insert` command. + 2. `command_started` and `command_failed` events for an `abortTransaction` command. + +### 11. Multi-batch bulkWrites + +This test MUST only run against server versions 8.0+. This test must be skipped on Atlas Serverless. + +This test MUST only run against standalones. The bulkWrite call takes an exceedingly long time on replicasets and +sharded clusters. Drivers MAY adjust the timeouts used in this test to allow for differing bulk encoding performance. + +1. Using `internalClient`, drop the `db.coll` collection. + +2. Using `internalClient`, set the following fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { + times: 2 + }, + data: { + failCommands: ["bulkWrite"], + blockConnection: true, + blockTimeMS: 1010 + } + } + ``` + +3. Using `internalClient`, perform a `hello` command and record the `maxBsonObjectSize` and `maxMessageSizeBytes` values + in the response. + +4. Create a new MongoClient (referred to as `client`) with `timeoutMS=2000`. + +5. Create a list of write models (referred to as `models`) with the following write model repeated + (`maxMessageSizeBytes / maxBsonObjectSize + 1`) times: + + ```javascript + InsertOne { + "namespace": "db.coll", + "document": { "a": "b".repeat(maxBsonObjectSize - 500) } + } + ``` + +6. Call `bulkWrite` on `client` with `models`. + + - Expect this to fail with a timeout error. + +7. Verify that two `bulkWrite` commands were executed as part of the `MongoClient.bulkWrite` call. + +## Unit Tests + +The tests enumerated in this section could not be expressed in either spec or prose format. Drivers SHOULD implement +these if it is possible to do so using the driver's existing test infrastructure. + +- Operations should ignore `waitQueueTimeoutMS` if `timeoutMS` is also set. +- If `timeoutMS` is set for an operation, the remaining `timeoutMS` value should apply to connection checkout after a + server has been selected. +- If `timeoutMS` is not set for an operation, `waitQueueTimeoutMS` should apply to connection checkout after a server + has been selected. +- If a new connection is required to execute an operation, + `min(remaining computedServerSelectionTimeout, connectTimeoutMS)` should apply to socket establishment. +- For drivers that have control over OCSP behavior, `min(remaining computedServerSelectionTimeout, 5 seconds)` should + apply to HTTP requests against OCSP responders. +- The remaining `timeoutMS` value should apply to HTTP requests against KMS servers for CSFLE. +- The remaining `timeoutMS` value should apply to commands sent to mongocryptd as part of automatic encryption. +- When doing `minPoolSize` maintenance, `connectTimeoutMS` is used as the timeout for socket establishment. diff --git a/source/client-side-operations-timeout/tests/bulkWrite.json b/source/client-side-operations-timeout/tests/bulkWrite.json new file mode 100644 index 0000000000..9a05809f77 --- /dev/null +++ b/source/client-side-operations-timeout/tests/bulkWrite.json @@ -0,0 +1,160 @@ +{ + "description": "timeoutMS behaves correctly for bulkWrite operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "w": 1 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS applied to entire bulkWrite, not individual commands", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": {} + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert", + "update" + ], + "blockConnection": true, + "blockTimeMS": 120 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 1 + } + } + } + ], + "timeoutMS": 200 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/bulkWrite.yml b/source/client-side-operations-timeout/tests/bulkWrite.yml new file mode 100644 index 0000000000..0459dbbc16 --- /dev/null +++ b/source/client-side-operations-timeout/tests/bulkWrite.yml @@ -0,0 +1,87 @@ +description: "timeoutMS behaves correctly for bulkWrite operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + uriOptions: + # Used to speed up the test + w: 1 + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Test that drivers do not refresh timeoutMS between commands. This is done by running a bulkWrite that will require + # two commands with timeoutMS=200 and blocking each command for 120ms. The server should take over 200ms total, so the + # bulkWrite should fail with a timeout error. + - description: "timeoutMS applied to entire bulkWrite, not individual commands" + operations: + # Do an operation without a timeout to ensure the servers are discovered. + - name: insertOne + object: *collection + arguments: + document: {} + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert", "update"] + blockConnection: true + blockTimeMS: 120 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + - replaceOne: + filter: { _id: 1 } + replacement: { x: 1 } + timeoutMS: 200 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/source/client-side-operations-timeout/tests/change-streams.json b/source/client-side-operations-timeout/tests/change-streams.json new file mode 100644 index 0000000000..8cffb08e26 --- /dev/null +++ b/source/client-side-operations-timeout/tests/change-streams.json @@ -0,0 +1,598 @@ +{ + "description": "timeoutMS behaves correctly for change streams", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "error if maxAwaitTimeMS is greater than timeoutMS", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 10 + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "error if maxAwaitTimeMS is equal to timeoutMS", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 5 + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "timeoutMS applied to initial aggregate", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 200 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 30 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 1050 + }, + "saveResultAsEntity": "changeStream" + }, + { + "name": "iterateOnce", + "object": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 200, + "batchSize": 2, + "maxAwaitTimeMS": 1 + }, + "saveResultAsEntity": "changeStream" + }, + { + "name": "iterateOnce", + "object": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": 1 + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to full resume attempt in a next call", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 200 + }, + "saveResultAsEntity": "changeStream" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore", + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 120, + "errorCode": 7, + "errorLabels": [ + "ResumableChangeStreamError" + ] + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "change stream can be iterated again if previous iteration times out", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "maxAwaitTimeMS": 1, + "timeoutMS": 200 + }, + "saveResultAsEntity": "changeStream" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream", + "expectError": { + "isTimeoutError": true + } + }, + { + "name": "iterateOnce", + "object": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll" + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore - failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 200 + }, + "saveResultAsEntity": "changeStream" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/change-streams.yml b/source/client-side-operations-timeout/tests/change-streams.yml new file mode 100644 index 0000000000..c813be035a --- /dev/null +++ b/source/client-side-operations-timeout/tests/change-streams.yml @@ -0,0 +1,333 @@ +description: "timeoutMS behaves correctly for change streams" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + # Drivers are not required to execute killCursors during resume attempts, so it should be ignored for command + # monitoring assertions. + ignoreCommandMonitoringEvents: ["killCursors"] + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "error if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + + - description: "error if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + + - description: "timeoutMS applied to initial aggregate" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 250 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 200 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # If maxAwaitTimeMS is not set, timeoutMS should be refreshed for the getMore and the getMore should not have a + # maxTimeMS field. This test requires a high timeout because the server applies a default 1000ms maxAwaitTime. To + # ensure that the driver is refreshing the timeout between commands, the test blocks aggregate and getMore commands + # for 30ms each and creates/iterates a change stream with timeoutMS=1050. The initial aggregate will block for 30ms + # and the getMore will block for 1030ms. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate", "getMore"] + blockConnection: true + blockTimeMS: 30 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 1050 + saveResultAsEntity: &changeStream changeStream + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If maxAwaitTimeMS is set, timeoutMS should still be refreshed for the getMore and the getMore command should have a + # maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate", "getMore"] + blockConnection: true + blockTimeMS: 150 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 200 + batchSize: 2 + maxAwaitTimeMS: 1 + saveResultAsEntity: &changeStream changeStream + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: 1 + + # The timeout should be applied to the entire resume attempt, not individually to each command. The test creates a + # change stream with timeoutMS=200 which returns an empty initial batch and then sets a fail point to block both + # getMore and aggregate for 120ms each and fail with a resumable error. When the resume attempt happens, the getMore + # and aggregate block for longer than 200ms total, so it times out. + - description: "timeoutMS applies to full resume attempt in a next call" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 200 + saveResultAsEntity: &changeStream changeStream + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["getMore", "aggregate"] + blockConnection: true + blockTimeMS: 120 + errorCode: 7 # HostNotFound - resumable but does not require an SDAM state change. + # failCommand doesn't correctly add the ResumableChangeStreamError by default. It needs to be specified + # manually here so the error is considered resumable. The failGetMoreAfterCursorCheckout fail point + # would add the label without this, but it does not support blockConnection functionality. + errorLabels: ["ResumableChangeStreamError"] + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "change stream can be iterated again if previous iteration times out" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + # Specify a short maxAwaitTimeMS because otherwise the getMore on the new cursor will wait for 1000ms and + # time out. + maxAwaitTimeMS: 1 + timeoutMS: 200 + saveResultAsEntity: &changeStream changeStream + # Block getMore for 250ms to force the next() call to time out. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + # The original aggregate didn't return any events so this should do a getMore and return a timeout error. + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + # The previous iteration attempt timed out so this should re-create the change stream. We use iterateOnce rather + # than iterateUntilDocumentOrError because there haven't been any events and we only want to assert that the + # cursor was re-created. + - name: iterateOnce + object: *changeStream + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + # The iterateUntilDocumentOrError operation should send a getMore. + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + # The iterateOnce operation should re-create the cursor via an aggregate and then send a getMore to iterate + # the new cursor. + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + + # The timeoutMS value should be refreshed for getMore's. This is a failure test. The createChangeStream operation + # sets timeoutMS=200 and the getMore blocks for 250ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 200 + saveResultAsEntity: &changeStream changeStream + # The first iteration should do a getMore + - name: iterateUntilDocumentOrError + object: *changeStream + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + # The iterateUntilDocumentOrError operation should send a getMore. + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName diff --git a/source/client-side-operations-timeout/tests/close-cursors.json b/source/client-side-operations-timeout/tests/close-cursors.json new file mode 100644 index 0000000000..79b0de7b6a --- /dev/null +++ b/source/client-side-operations-timeout/tests/close-cursors.json @@ -0,0 +1,239 @@ +{ + "description": "timeoutMS behaves correctly when closing cursors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "timeoutMS is refreshed for close", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "batchSize": 2, + "timeoutMS": 200 + }, + "saveResultAsEntity": "cursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectError": { + "isTimeoutError": true + } + }, + { + "name": "close", + "object": "cursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore" + } + }, + { + "commandFailedEvent": { + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "command": { + "killCursors": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + }, + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be overridden for close", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "killCursors" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "batchSize": 2, + "timeoutMS": 200 + }, + "saveResultAsEntity": "cursor" + }, + { + "name": "close", + "object": "cursor", + "arguments": { + "timeoutMS": 400 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "killCursors": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + }, + "commandName": "killCursors" + } + }, + { + "commandSucceededEvent": { + "commandName": "killCursors" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/close-cursors.yml b/source/client-side-operations-timeout/tests/close-cursors.yml new file mode 100644 index 0000000000..c4c4ea0acd --- /dev/null +++ b/source/client-side-operations-timeout/tests/close-cursors.yml @@ -0,0 +1,127 @@ +description: "timeoutMS behaves correctly when closing cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 0 } + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "timeoutMS is refreshed for close" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + - name: createFindCursor + object: *collection + arguments: + filter: {} + batchSize: 2 + timeoutMS: 200 + saveResultAsEntity: &cursor cursor + # Iterate the cursor three times. The third should do a getMore, which should fail with a timeout error. + - name: iterateUntilDocumentOrError + object: *cursor + - name: iterateUntilDocumentOrError + object: *cursor + - name: iterateUntilDocumentOrError + object: *cursor + expectError: + isTimeoutError: true + # All errors from close() are ignored, so we close the cursor here but assert that killCursors was executed + # successfully via command monitoring expectations below. + - name: close + object: *cursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - commandStartedEvent: + commandName: getMore + - commandFailedEvent: + commandName: getMore + - commandStartedEvent: + command: + killCursors: *collectionName + # The close() operation should inherit timeoutMS from the initial find(). + maxTimeMS: { $$type: ["int", "long"] } + commandName: killCursors + - commandSucceededEvent: + commandName: killCursors + + - description: "timeoutMS can be overridden for close" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["killCursors"] + blockConnection: true + blockTimeMS: 250 + - name: createFindCursor + object: *collection + arguments: + filter: {} + batchSize: 2 + timeoutMS: 200 + saveResultAsEntity: &cursor cursor + - name: close + object: *cursor + arguments: + timeoutMS: 400 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - commandStartedEvent: + command: + killCursors: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + commandName: killCursors + - commandSucceededEvent: + commandName: killCursors diff --git a/source/client-side-operations-timeout/tests/command-execution.json b/source/client-side-operations-timeout/tests/command-execution.json new file mode 100644 index 0000000000..212cd41089 --- /dev/null +++ b/source/client-side-operations-timeout/tests/command-execution.json @@ -0,0 +1,399 @@ +{ + "description": "timeoutMS behaves correctly during command execution", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4.7", + "topologies": [ + "single", + "replicaset", + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + }, + { + "collectionName": "timeoutColl", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "maxTimeMS value in the command is less than timeoutMS", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "reduceMaxTimeMSTest", + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "uriOptions": { + "appName": "reduceMaxTimeMSTest", + "w": 1, + "timeoutMS": 500, + "heartbeatFrequencyMS": 500, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "timeoutCollection", + "database": "database", + "collectionName": "timeoutColl" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 1 + }, + "timeoutMS": 100000 + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 1000 + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 2 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "timeoutColl" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "timeoutColl", + "maxTimeMS": { + "$$lte": 450 + } + } + } + } + ] + } + ] + }, + { + "description": "command is not sent if RTT is greater than timeoutMS", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "rttTooHighTest", + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "uriOptions": { + "appName": "rttTooHighTest", + "w": 1, + "timeoutMS": 10, + "heartbeatFrequencyMS": 500, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "timeoutCollection", + "database": "database", + "collectionName": "timeoutColl" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 1 + }, + "timeoutMS": 100000 + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 1000 + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 2 + } + }, + "expectError": { + "isTimeoutError": true + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 3 + } + }, + "expectError": { + "isTimeoutError": true + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 4 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "timeoutColl" + } + } + } + ] + } + ] + }, + { + "description": "short-circuit is not enabled with only 1 RTT measurement", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "reduceMaxTimeMSTest", + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "uriOptions": { + "appName": "reduceMaxTimeMSTest", + "w": 1, + "timeoutMS": 90, + "heartbeatFrequencyMS": 100000, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "timeoutCollection", + "database": "database", + "collectionName": "timeoutColl" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 1 + }, + "timeoutMS": 100000 + } + }, + { + "name": "insertOne", + "object": "timeoutCollection", + "arguments": { + "document": { + "_id": 2 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "timeoutColl" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "timeoutColl", + "maxTimeMS": { + "$$lte": 450 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/command-execution.yml b/source/client-side-operations-timeout/tests/command-execution.yml new file mode 100644 index 0000000000..95a9abeefc --- /dev/null +++ b/source/client-side-operations-timeout/tests/command-execution.yml @@ -0,0 +1,255 @@ +description: "timeoutMS behaves correctly during command execution" + +schemaVersion: "1.26" + +runOnRequirements: + # Require SERVER-49336 for failCommand + appName on the initial handshake. + - minServerVersion: "4.4.7" + # Skip load-balanced and serverless which do not support RTT measurements. + topologies: [ single, replicaset, sharded ] + serverless: forbid + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + # The corresponding entities for the collections defined here are created in test-level createEntities operations. + # This is done so that tests can set fail points that will affect all of the handshakes and heartbeats done by a + # client. The collection and database names are listed here so that the collections will be dropped and re-created at + # the beginning of each test. + - collectionName: ®ularCollectionName coll + databaseName: &databaseName test + documents: [] + - collectionName: &timeoutCollectionName timeoutColl + databaseName: &databaseName test + documents: [] + +tests: + - description: "maxTimeMS value in the command is less than timeoutMS" + operations: + # Artificially increase the server RTT to ~50ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName reduceMaxTimeMSTest + blockConnection: true + blockTimeMS: 50 + # Create a client with the app name specified in the fail point and timeoutMS higher than blockTimeMS. + # Also create database and collection entities derived from the new client. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 500 + heartbeatFrequencyMS: 500 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Wait until short-circuiting has been enabled (at least 2 RTT measurements). + - name: wait + object: testRunner + arguments: + ms: 1000 + # Do an operation with timeoutCollection so the event will include a maxTimeMS field. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + maxTimeMS: { $$lte: 450 } + + - description: "command is not sent if RTT is greater than timeoutMS" + operations: + # Artificially increase the server RTT to ~50ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName rttTooHighTest + blockConnection: true + blockTimeMS: 50 + # Create a client with the app name specified in the fail point. Also create database and collection entities + # derived from the new client. There is one collection entity with no timeoutMS and another with a timeoutMS + # that's lower than the fail point's blockTimeMS value. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 10 + heartbeatFrequencyMS: 500 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Wait until short-circuiting has been enabled (at least 2 RTT measurements). + - name: wait + object: testRunner + arguments: + ms: 1000 + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectError: + isTimeoutError: true + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 3 } + expectError: + isTimeoutError: true + # Do an operation with timeoutCollection which will error. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 4 } + expectError: + isTimeoutError: true + expectEvents: + # There should only be one event, which corresponds to the first + # insertOne call. For the subsequent insertOne calls, drivers should + # fail client-side. + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + + - description: "short-circuit is not enabled with only 1 RTT measurement" + operations: + # Artificially increase the server RTT to ~300ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["hello", "isMaster"] + appName: &appName reduceMaxTimeMSTest + blockConnection: true + blockTimeMS: 100 + # Create a client with the app name specified in the fail point and timeoutMS lower than blockTimeMS. + # Also create database and collection entities derived from the new client. + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + appName: *appName + w: 1 # Override server's w:majority default to speed up the test. + timeoutMS: 90 + heartbeatFrequencyMS: 100000 # Override heartbeatFrequencyMS to ensure only 1 RTT is recorded. + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &timeoutCollection timeoutCollection + database: *database + collectionName: *timeoutCollectionName + # Do an operation with a large timeout to ensure the servers are discovered. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 1 } + timeoutMS: 100000 + # Do an operation with timeoutCollection which will succeed. If this + # fails it indicates the driver mistakenly used the min RTT even though + # there has only been one sample. + - name: insertOne + object: *timeoutCollection + arguments: + document: { _id: 2 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *timeoutCollectionName + maxTimeMS: { $$lte: 450 } diff --git a/source/client-side-operations-timeout/tests/convenient-transactions.json b/source/client-side-operations-timeout/tests/convenient-transactions.json new file mode 100644 index 0000000000..3400b82ba9 --- /dev/null +++ b/source/client-side-operations-timeout/tests/convenient-transactions.json @@ -0,0 +1,316 @@ +{ + "description": "timeoutMS behaves correctly for the withTransaction API", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 500, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction raises a client-side error if timeoutMS is overridden inside the callback", + "operations": [ + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session", + "timeoutMS": 100 + }, + "expectError": { + "isClientError": true + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [] + } + ] + }, + { + "description": "timeoutMS is not refreshed for each operation in the callback", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 300 + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session" + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + }, + "session": "session" + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "withTransaction surfaces a timeout after exhausting transient transaction retries, retaining the last transient error as the timeout cause.", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 25, + "errorCode": 24, + "errorLabels": [ + "TransientTransactionError" + ] + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session" + }, + "expectError": { + "isError": true + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/convenient-transactions.yml b/source/client-side-operations-timeout/tests/convenient-transactions.yml new file mode 100644 index 0000000000..8157c5e4d8 --- /dev/null +++ b/source/client-side-operations-timeout/tests/convenient-transactions.yml @@ -0,0 +1,177 @@ +description: "timeoutMS behaves correctly for the withTransaction API" + +schemaVersion: "1.26" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 500 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "withTransaction raises a client-side error if timeoutMS is overridden inside the callback" + operations: + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + timeoutMS: 100 + expectError: + isClientError: true + expectError: + isClientError: true + expectEvents: + # The only operation run fails with a client-side error, so there should be no events for the client. + - client: *client + events: [] + + - description: "timeoutMS is not refreshed for each operation in the callback" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 300 + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + - name: insertOne + object: *collection + arguments: + document: { _id: 2 } + session: *session + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: [ "int", "long" ] } + + # This test verifies that when withTransaction encounters transient transaction errors it does not + # throw the transient transaction error when the timeout is exceeded, but instead surfaces a timeout error after + # exhausting the retry attempts within the specified timeout. + # The timeout error thrown contains as a cause the last transient error encountered. + - description: "withTransaction surfaces a timeout after exhausting transient transaction retries, retaining the last transient error as the timeout cause." + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 25 + errorCode: 24 + errorLabels: ["TransientTransactionError"] + + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + expectError: + isError: true + expectError: + isTimeoutError: true + + # Verify that multiple insert (at least 2) attempts occurred due to TransientTransactionError retries + # The exact number of events depends on timing and retry backoff, but there should be at least: + # - 2 commandStartedEvent for insert (initial + at least one retry) + # - 2 commandFailedEvent for insert (corresponding failures) + expectEvents: + - client: *client + ignoreExtraEvents: true + events: + # First insert attempt + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + - commandFailedEvent: + commandName: abortTransaction + + # Second insert attempt (retry due to TransientTransactionError) + - commandStartedEvent: + commandName: insert + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + - commandFailedEvent: + commandName: abortTransaction diff --git a/source/client-side-operations-timeout/tests/cursors.json b/source/client-side-operations-timeout/tests/cursors.json new file mode 100644 index 0000000000..36949d7509 --- /dev/null +++ b/source/client-side-operations-timeout/tests/cursors.json @@ -0,0 +1,113 @@ +{ + "description": "tests for timeoutMS behavior that applies to all cursor types", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client" + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "find errors if timeoutMode is set and timeoutMS is not", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "collection aggregate errors if timeoutMode is set and timeoutMS is not", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "database aggregate errors if timeoutMode is set and timeoutMS is not", + "operations": [ + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [], + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "listCollections errors if timeoutMode is set and timeoutMS is not", + "operations": [ + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "listIndexes errors if timeoutMode is set and timeoutMS is not", + "operations": [ + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/cursors.yml b/source/client-side-operations-timeout/tests/cursors.yml new file mode 100644 index 0000000000..0202054732 --- /dev/null +++ b/source/client-side-operations-timeout/tests/cursors.yml @@ -0,0 +1,70 @@ +description: "tests for timeoutMS behavior that applies to all cursor types" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "find errors if timeoutMode is set and timeoutMS is not" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "collection aggregate errors if timeoutMode is set and timeoutMS is not" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: [] + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "database aggregate errors if timeoutMode is set and timeoutMS is not" + operations: + - name: aggregate + object: *database + arguments: + pipeline: [] + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "listCollections errors if timeoutMode is set and timeoutMS is not" + operations: + - name: listCollections + object: *database + arguments: + filter: {} + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: "listIndexes errors if timeoutMode is set and timeoutMS is not" + operations: + - name: listIndexes + object: *collection + arguments: + timeoutMode: cursorLifetime + expectError: + isClientError: true diff --git a/source/client-side-operations-timeout/tests/deprecated-options.json b/source/client-side-operations-timeout/tests/deprecated-options.json new file mode 100644 index 0000000000..647e1bf792 --- /dev/null +++ b/source/client-side-operations-timeout/tests/deprecated-options.json @@ -0,0 +1,7222 @@ +{ + "description": "operations ignore deprecated timeout options if timeoutMS is set", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction ignores socketTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 20 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "aggregate" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + }, + { + "name": "commitTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "commitTransaction ignores wTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "aggregate" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + }, + { + "name": "commitTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "commitTransaction ignores maxCommitTimeMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "aggregate" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client", + "sessionOptions": { + "defaultTransactionOptions": { + "maxCommitTimeMS": 5000 + } + } + } + } + ] + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + }, + { + "name": "commitTransaction", + "object": "session", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "abortTransaction ignores socketTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 20 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "aggregate" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + }, + { + "name": "abortTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "abortTransaction ignores wTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "aggregate" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + }, + { + "name": "abortTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "withTransaction ignores socketTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 20 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000, + "callback": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "withTransaction ignores wTimeoutMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "timeoutMS": 10000, + "callback": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "withTransaction ignores maxCommitTimeMS if timeoutMS is set", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client", + "sessionOptions": { + "defaultTransactionOptions": { + "maxCommitTimeMS": 5000 + } + } + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "timeoutMS": 1000, + "callback": [ + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {}, + "session": "session" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listDatabases on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listDatabases on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 100000 + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 100000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "fieldName": "x", + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "listIndexNames", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "document": { + "x": 1 + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "documents": [ + { + "x": 1 + } + ] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "timeoutMS": 100000, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "name": "x_1" + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "timeoutMS": 100000, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "timeoutMS": 100000, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + }, + { + "description": "socketTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 1 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 5 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1", + "timeoutMS": 100000 + } + }, + { + "name": "dropIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ] + }, + { + "description": "wTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "wTimeoutMS": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "dropIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 100000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS is ignored if timeoutMS is set - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ] + } + }, + { + "name": "dropIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "maxTimeMS": 5000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$lte": 1000 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/deprecated-options.yml b/source/client-side-operations-timeout/tests/deprecated-options.yml new file mode 100644 index 0000000000..e3378d5fa8 --- /dev/null +++ b/source/client-side-operations-timeout/tests/deprecated-options.yml @@ -0,0 +1,4008 @@ +description: "operations ignore deprecated timeout options if timeoutMS is set" + +schemaVersion: "1.9" + +# Most tests in this file can be executed against any server version, but some tests execute operations that are only +# available on higher server versions (e.g. abortTransaction). To avoid too many special cases in templated tests, the +# min server version is set to 4.2 for all. +runOnRequirements: + - minServerVersion: "4.2" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, run these tests: + # + # 1. socketTimeoutMS is ignored if timeoutMS is set. The test creates a client with socketTimeoutMS=1, configures and + # a failpoint to block the operation for 5ms, runs the operation with timeoutMS=10000, and expects it to succeed. + # + # 2. wTimeoutMS is ignored if timeoutMS is set. The test creates a client with wTimeoutMS=1, runs the operation with + # timeoutMS=10000, expects the operation to succeed, and uses command monitoring expectations to assert that the + # command sent to the server does not contain a writeConcern field. + # + # 3. If the operation supports maxTimeMS, it ignores maxTimeMS if timeoutMS is set. The test executes the operation + # with timeoutMS=1000 and maxTimeMS=5000. It expects the operation to succeed and uses command monitoring expectations + # to assert that the actual maxTimeMS value sent was less than or equal to 100, thereby asserting that it was + # actually derived from timeoutMS. + + # Tests for commitTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. There is also one + # additional test to assert that maxCommitTimeMS is ignored if timeoutMS is set. + + - description: "commitTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "commitTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: commitTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for abortTransaction. These are not included in the operations loop because the tests need to execute + # additional "startTransaction" and "insertOne" operations to establish a server-side transaction. + + - description: "abortTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "abortTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: ["aggregate"] + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: startTransaction + object: *session + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + - name: abortTransaction + object: *session + arguments: + timeoutMS: 10000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + # Tests for withTransaction. These are not included in the operations loop because the command monitoring + # expectations contain multiple commands. There is also one additional test to assert that maxCommitTimeMS is ignored + # if timeoutMS is set. + + - description: "withTransaction ignores socketTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + # This test uses 20 instead of 1 like other tests because socketTimeoutMS also applies to the + # operation done to start the server-side transaction and it needs time to succeed. + socketTimeoutMS: 20 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 5 + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores wTimeoutMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + - name: withTransaction + object: *session + arguments: + timeoutMS: 10000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "withTransaction ignores maxCommitTimeMS if timeoutMS is set" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: 5000 + - name: withTransaction + object: *session + arguments: + timeoutMS: &timeoutMS 1000 + callback: + - name: countDocuments + object: *collection + arguments: + filter: {} + session: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + # Assert that the final maxTimeMS field is derived from timeoutMS, not maxCommitTimeMS. + maxTimeMS: { $$lte: *timeoutMS } + + # Tests for operations that can be generated. + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 5 + - name: listDatabases + object: *client + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listDatabases + object: *client + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 5 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *client + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: aggregate + object: *database + arguments: + timeoutMS: 100000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *database + arguments: + timeoutMS: 100000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *database + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 5 + - name: listCollections + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listCollections + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 5 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 5 + - name: runCommand + object: *database + arguments: + timeoutMS: 100000 + command: { ping: 1 } + commandName: ping + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: runCommand + object: *database + arguments: + timeoutMS: 100000 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *database + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: aggregate + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: aggregate + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 5 + - name: count + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: count + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: count + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: countDocuments + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 5 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 5 + - name: distinct + object: *collection + arguments: + timeoutMS: 100000 + fieldName: x + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: distinct + object: *collection + arguments: + timeoutMS: 100000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: distinct + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 5 + - name: find + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: find + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: find + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 5 + - name: findOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOne + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 5 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 100000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: insertOne + object: *collection + arguments: + timeoutMS: 100000 + document: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: insertOne + object: *collection + arguments: + timeoutMS: 100000 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: insertMany + object: *collection + arguments: + timeoutMS: 100000 + documents: + - { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: insertMany + object: *collection + arguments: + timeoutMS: 100000 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 5 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: deleteOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 5 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: deleteMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: replaceOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: updateOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: updateOne + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 5 + - name: updateMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: updateMany + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 5 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 100000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 5 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 100000 + requests: + - insertOne: + document: { _id: 1 } + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 100000 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + + - description: "socketTimeoutMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 5 + - name: createIndex + object: *collection + arguments: + timeoutMS: 100000 + keys: { x: 1 } + name: "x_1" + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createIndex + object: *collection + arguments: + timeoutMS: 100000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: createIndex + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 5 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + timeoutMS: 100000 + name: "x_1" + - name: dropIndex + object: *collection + arguments: + timeoutMS: 100000 + name: "x_1" + - description: "wTimeoutMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + timeoutMS: 100000 + name: "x_1" + - name: dropIndex + object: *collection + arguments: + timeoutMS: 100000 + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + + - description: "socketTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + socketTimeoutMS: 1 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 5 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + timeoutMS: 100000 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + + - description: "wTimeoutMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + wTimeoutMS: 1 + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 100000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + writeConcern: { $$exists: false } + maxTimeMS: { $$type: ["int", "long"] } + + - description: "maxTimeMS is ignored if timeoutMS is set - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - bucket: + id: &bucket bucket + database: *database + - session: + id: &session session + client: *client + - name: dropIndexes + object: *collection + arguments: + timeoutMS: &timeoutMS 1000 + maxTimeMS: 5000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$lte: *timeoutMS } + diff --git a/source/client-side-operations-timeout/tests/error-transformations.json b/source/client-side-operations-timeout/tests/error-transformations.json new file mode 100644 index 0000000000..89be49f0a4 --- /dev/null +++ b/source/client-side-operations-timeout/tests/error-transformations.json @@ -0,0 +1,182 @@ +{ + "description": "MaxTimeMSExpired server errors are transformed into a custom timeout error", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.2", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "basic MaxTimeMSExpired error is transformed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 50 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "write concern error MaxTimeMSExpired is transformed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 50, + "errmsg": "maxTimeMS expired" + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/error-transformations.yml b/source/client-side-operations-timeout/tests/error-transformations.yml new file mode 100644 index 0000000000..0ea9373b7d --- /dev/null +++ b/source/client-side-operations-timeout/tests/error-transformations.yml @@ -0,0 +1,98 @@ +description: "MaxTimeMSExpired server errors are transformed into a custom timeout error" + +schemaVersion: "1.26" + +# failCommand is available on 4.0 for replica sets and 4.2 for sharded clusters. +runOnRequirements: + - minServerVersion: "4.0" + topologies: ["replicaset"] + - minServerVersion: "4.2" + topologies: ["sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # A server response like {ok: 0, code: 50, ...} is transformed. + - description: "basic MaxTimeMSExpired error is transformed" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + errorCode: 50 + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # A server response like {ok: 1, writeConcernError: {code: 50, ...}} is transformed. + - description: "write concern error MaxTimeMSExpired is transformed" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + writeConcernError: + code: 50 + errmsg: "maxTimeMS expired" + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/source/client-side-operations-timeout/tests/global-timeoutMS.json b/source/client-side-operations-timeout/tests/global-timeoutMS.json new file mode 100644 index 0000000000..9d8046d1bf --- /dev/null +++ b/source/client-side-operations-timeout/tests/global-timeoutMS.json @@ -0,0 +1,5906 @@ +{ + "description": "timeoutMS can be configured on a MongoClient", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be configured on a MongoClient - listDatabases on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listDatabases on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - listDatabaseNames on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listDatabaseNames on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - createChangeStream on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - createChangeStream on client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoClient - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 250, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 350 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoClient - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 0 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/global-timeoutMS.yml b/source/client-side-operations-timeout/tests/global-timeoutMS.yml new file mode 100644 index 0000000000..8cb1013166 --- /dev/null +++ b/source/client-side-operations-timeout/tests/global-timeoutMS.yml @@ -0,0 +1,3307 @@ +# Tests in this file are generated from global-timeoutMS.yml.template. + +description: "timeoutMS can be configured on a MongoClient" + +schemaVersion: "1.26" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each operation, we execute two tests: + # + # 1. timeoutMS can be configured to a non-zero value on a MongoClient and is inherited by the operation. Each test + # constructs a client entity with timeoutMS=250 and configures a fail point to block the operation for 350ms so + # execution results in a timeout error. + # + # 2. timeoutMS can be set to 0 for a MongoClient. Each test constructs a client entity with timeoutMS=0 and + # configures a fail point to block the operation for 15ms. The tests expect the operation to succeed and the command + # sent to not contain a maxTimeMS field. + + - description: "timeoutMS can be configured on a MongoClient - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 350 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listDatabases on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 350 + - name: listDatabaseNames + object: *client + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listDatabaseNames on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 350 + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 350 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 350 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 350 + - name: count + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 350 + - name: estimatedDocumentCount + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 350 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 350 + - name: find + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 350 + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: listIndexes + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: listIndexNames + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 350 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 350 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 350 + - name: deleteMany + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 350 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 350 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 350 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: dropIndex + object: *collection + arguments: + name: "x_1" + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoClient - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 250 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Use "times: 2" to workaround a quirk in Python on Windows where + # socket I/O can timeout ~20ms earlier than expected. With + # "times: 1" the retry would succeed within the remaining ~20ms. + mode: { times: 2 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 350 + - name: dropIndexes + object: *collection + + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoClient - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + timeoutMS: 0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + diff --git a/source/client-side-operations-timeout/tests/gridfs-advanced.json b/source/client-side-operations-timeout/tests/gridfs-advanced.json new file mode 100644 index 0000000000..c6c0944d2f --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-advanced.json @@ -0,0 +1,385 @@ +{ + "description": "timeoutMS behaves correctly for advanced GridFS API operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 75 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "collection": { + "id": "filesCollection", + "database": "database", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "chunksCollection", + "database": "database", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "test", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "test", + "documents": [ + { + "_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": "ESIzRA==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for a rename", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "rename", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + }, + "newFilename": "foo", + "timeoutMS": 2000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to update during a rename", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "rename", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + }, + "newFilename": "foo" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be overridden for drop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "drop" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "drop", + "object": "bucket", + "arguments": { + "timeoutMS": 2000 + } + } + ] + }, + { + "description": "timeoutMS applied to files collection drop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "drop" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "drop", + "object": "bucket", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "drop", + "databaseName": "test", + "command": { + "drop": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to chunks collection drop", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "drop" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "drop", + "object": "bucket", + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to drop as a whole, not individual parts", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "drop" + ], + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "drop", + "object": "bucket", + "expectError": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/gridfs-advanced.yml b/source/client-side-operations-timeout/tests/gridfs-advanced.yml new file mode 100644 index 0000000000..f6c37e165b --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-advanced.yml @@ -0,0 +1,206 @@ +description: "timeoutMS behaves correctly for advanced GridFS API operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + # Tests for the "rename" operation. + + - description: "timeoutMS can be overridden for a rename" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 100 + - name: rename + object: *bucket + arguments: + id: *fileDocumentId + newFilename: "foo" + timeoutMS: 2000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 2000ms should let it succeed. + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to update during a rename" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 100 + - name: rename + object: *bucket + arguments: + id: *fileDocumentId + newFilename: "foo" + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + # Tests for the "drop" operation. Any tests that might result in multiple commands being sent do not have expectEvents + # assertions as these assertions reduce test robustness and can cause flaky failures. + + - description: "timeoutMS can be overridden for drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + arguments: + timeoutMS: 2000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 2000ms should let it succeed. + + - description: "timeoutMS applied to files collection drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: drop + databaseName: *databaseName + command: + drop: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to chunks collection drop" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # Skip the drop for the files collection. + skip: 1 + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 100 + - name: drop + object: *bucket + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to drop as a whole, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["drop"] + blockConnection: true + blockTimeMS: 50 + - name: drop + object: *bucket + expectError: + isTimeoutError: true diff --git a/source/client-side-operations-timeout/tests/gridfs-delete.json b/source/client-side-operations-timeout/tests/gridfs-delete.json new file mode 100644 index 0000000000..9f4980114b --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-delete.json @@ -0,0 +1,285 @@ +{ + "description": "timeoutMS behaves correctly for GridFS delete operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 75 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "collection": { + "id": "filesCollection", + "database": "database", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "chunksCollection", + "database": "database", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "test", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "test", + "documents": [ + { + "_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": "ESIzRA==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for delete", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "delete", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + }, + "timeoutMS": 1000 + } + } + ] + }, + { + "description": "timeoutMS applied to delete against the files collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "delete", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to delete against the chunks collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "delete", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to entire delete, not individual parts", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "delete", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/gridfs-delete.yml b/source/client-side-operations-timeout/tests/gridfs-delete.yml new file mode 100644 index 0000000000..9c72537c38 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-delete.yml @@ -0,0 +1,152 @@ +description: "timeoutMS behaves correctly for GridFS delete operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + - description: "timeoutMS can be overridden for delete" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + + - description: "timeoutMS applied to delete against the files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to delete against the chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # The first "delete" will be against the files collection, so we skip it. + skip: 1 + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 100 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + + # Test that drivers are not refreshing the timeout between commands. We test this by blocking both "delete" commands + # for 50ms each. The delete should inherit timeoutMS=75 from the client/database and the server takes over 75ms + # total, so the operation should fail. + - description: "timeoutMS applied to entire delete, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 50 + - name: delete + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true diff --git a/source/client-side-operations-timeout/tests/gridfs-download.json b/source/client-side-operations-timeout/tests/gridfs-download.json new file mode 100644 index 0000000000..8542f69e89 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-download.json @@ -0,0 +1,359 @@ +{ + "description": "timeoutMS behaves correctly for GridFS download operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 75 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "collection": { + "id": "filesCollection", + "database": "database", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "chunksCollection", + "database": "database", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "test", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "test", + "documents": [ + { + "_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": "ESIzRA==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for download", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "download", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + }, + "timeoutMS": 1000 + } + } + ] + }, + { + "description": "timeoutMS applied to find to get files document", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "download", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to find to get chunks", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "download", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.chunks", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to entire download, not individual parts", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "download", + "object": "bucket", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.chunks", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/gridfs-download.yml b/source/client-side-operations-timeout/tests/gridfs-download.yml new file mode 100644 index 0000000000..772ffd6e08 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-download.yml @@ -0,0 +1,182 @@ +description: "timeoutMS behaves correctly for GridFS download operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: + - _id: &fileDocumentId { $oid: "000000000000000000000005" } + length: 8 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "length-8" + contentType: "application/octet-stream" + aliases: [] + metadata: {} + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: *fileDocumentId + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: *fileDocumentId + n: 1 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + +tests: + - description: "timeoutMS can be overridden for download" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + + - description: "timeoutMS applied to find to get files document" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to find to get chunks" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: + # The first "find" will be against the files collection, so we skip it. + skip: 1 + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *chunksCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + # Test that drivers are not refreshing the timeout between commands. We test this by blocking both "find" commands + # for 50ms each. The download should inherit timeoutMS=75 from the client/database and the server takes over 75ms + # total, so the operation should fail. + - description: "timeoutMS applied to entire download, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 50 + - name: download + object: *bucket + arguments: + id: *fileDocumentId + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *chunksCollectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/source/client-side-operations-timeout/tests/gridfs-find.json b/source/client-side-operations-timeout/tests/gridfs-find.json new file mode 100644 index 0000000000..7409036284 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-find.json @@ -0,0 +1,183 @@ +{ + "description": "timeoutMS behaves correctly for GridFS find operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 75 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "collection": { + "id": "filesCollection", + "database": "database", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "chunksCollection", + "database": "database", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "test", + "documents": [] + }, + { + "collectionName": "fs.chunks", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for a find", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "find", + "object": "bucket", + "arguments": { + "filter": {}, + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to find command", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "find", + "object": "bucket", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "fs.files", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/gridfs-find.yml b/source/client-side-operations-timeout/tests/gridfs-find.yml new file mode 100644 index 0000000000..000150ae67 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-find.yml @@ -0,0 +1,100 @@ +description: "timeoutMS behaves correctly for GridFS find operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: [] + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "timeoutMS can be overridden for a find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: find + object: *bucket + arguments: + filter: {} + timeoutMS: 1000 # The client timeoutMS is 75ms and the operation blocks for 100ms, so 1000ms should let it succeed. + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } + + - description: "timeoutMS applied to find command" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: find + object: *bucket + arguments: + filter: {} + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *filesCollectionName + maxTimeMS: { $$type: ["int", "long"] } diff --git a/source/client-side-operations-timeout/tests/gridfs-upload.json b/source/client-side-operations-timeout/tests/gridfs-upload.json new file mode 100644 index 0000000000..b3f174973d --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-upload.json @@ -0,0 +1,409 @@ +{ + "description": "timeoutMS behaves correctly for GridFS upload operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 75 + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "bucket": { + "id": "bucket", + "database": "database" + } + }, + { + "collection": { + "id": "filesCollection", + "database": "database", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "chunksCollection", + "database": "database", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "test", + "documents": [] + }, + { + "collectionName": "fs.chunks", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for upload", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + }, + "timeoutMS": 1000 + } + } + ] + }, + { + "description": "timeoutMS applied to initial find on files collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to listIndexes on files collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to index creation for files collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to listIndexes on chunks collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to index creation for chunks collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to chunk insertion", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to creation of files document", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 100 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "timeoutMS applied to upload as a whole, not individual parts", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 50 + } + } + } + }, + { + "name": "upload", + "object": "bucket", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/gridfs-upload.yml b/source/client-side-operations-timeout/tests/gridfs-upload.yml new file mode 100644 index 0000000000..51e1366878 --- /dev/null +++ b/source/client-side-operations-timeout/tests/gridfs-upload.yml @@ -0,0 +1,249 @@ +description: "timeoutMS behaves correctly for GridFS upload operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # GridFS ops can be slow on serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 75 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: &databaseName test + - bucket: + id: &bucket bucket + database: *database + - collection: + id: &filesCollection filesCollection + database: *database + collectionName: &filesCollectionName fs.files + - collection: + id: &chunksCollection chunksCollection + database: *database + collectionName: &chunksCollectionName fs.chunks + +initialData: + - collectionName: *filesCollectionName + databaseName: *databaseName + documents: [] + - collectionName: *chunksCollectionName + databaseName: *databaseName + documents: [] + +tests: + # Many tests in this file do not specify command monitoring expectations because GridFS uploads internally do a + # number of operations, so expecting an exact set of commands can cause flaky failures. + + - description: "timeoutMS can be overridden for upload" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + timeoutMS: 1000 + + # On the first write to the bucket, drivers check if the files collection is empty to see if indexes need to be + # created. + - description: "timeoutMS applied to initial find on files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # On the first write to the bucket, drivers check if the files collection has the correct indexes. + - description: "timeoutMS applied to listIndexes on files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # If the files collection is empty when the first write to the bucket occurs, drivers attempt to create an index + # on the bucket's files collection. + - description: "timeoutMS applied to index creation for files collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # On the first write to the bucket, drivers check if the chunks collection has the correct indexes. + - description: "timeoutMS applied to listIndexes on chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # The first listIndexes will be on the files collection, so we skip it. + mode: { skip: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # If the files collection is empty when the first write to the bucket occurs, drivers attempt to create an index + # on the bucket's chunks collection. + - description: "timeoutMS applied to index creation for chunks collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # This index is created after the one on the files collection, so we skip the first createIndexes command + # and target the second. + mode: { skip: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to chunk insertion" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + - description: "timeoutMS applied to creation of files document" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + # Skip the insert to upload the chunk. Because the whole file fits into one chunk, the second insert will + # be the files document upload. + mode: { skip: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 100 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true + + # Test that drivers apply timeoutMS to the entire upload rather than refreshing it between individual commands. We + # test this by blocking the "find" and "listIndexes" commands for 50ms each and performing an upload. The upload + # should inherit timeoutMS=75 from the client/database and the server takes over 75ms total, so the operation should + # fail. + - description: "timeoutMS applied to upload as a whole, not individual parts" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "listIndexes"] + blockConnection: true + blockTimeMS: 50 + - name: upload + object: *bucket + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + expectError: + isTimeoutError: true diff --git a/source/client-side-operations-timeout/tests/legacy-timeouts.json b/source/client-side-operations-timeout/tests/legacy-timeouts.json new file mode 100644 index 0000000000..535425c934 --- /dev/null +++ b/source/client-side-operations-timeout/tests/legacy-timeouts.json @@ -0,0 +1,379 @@ +{ + "description": "legacy timeouts continue to work if timeoutMS is not set", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "socketTimeoutMS is not used to derive a maxTimeMS command field", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "socketTimeoutMS": 50000 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "waitQueueTimeoutMS is not used to derive a maxTimeMS command field", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "waitQueueTimeoutMS": 50000 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "wTimeoutMS is not used to derive a maxTimeMS command field", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "wTimeoutMS": 50000 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + }, + "writeConcern": { + "wtimeout": 50000 + } + } + } + } + ] + } + ] + }, + { + "description": "maxTimeMS option is used directly as the maxTimeMS field on a command", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "maxTimeMS": 50000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": 50000 + } + } + } + ] + } + ] + }, + { + "description": "maxCommitTimeMS option is used directly as the maxTimeMS field on a commitTransaction command", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client", + "sessionOptions": { + "defaultTransactionOptions": { + "maxCommitTimeMS": 1000 + } + } + } + } + ] + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session" + } + }, + { + "name": "commitTransaction", + "object": "session" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": 1000 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/legacy-timeouts.yml b/source/client-side-operations-timeout/tests/legacy-timeouts.yml new file mode 100644 index 0000000000..81c48f7c4f --- /dev/null +++ b/source/client-side-operations-timeout/tests/legacy-timeouts.yml @@ -0,0 +1,204 @@ +description: "legacy timeouts continue to work if timeoutMS is not set" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.4" + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + - description: "socketTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + socketTimeoutMS: 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + + - description: "waitQueueTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + waitQueueTimeoutMS: 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + + - description: "wTimeoutMS is not used to derive a maxTimeMS command field" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + uriOptions: + wTimeoutMS: &wTimeoutMS 50000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + writeConcern: + wtimeout: *wTimeoutMS + + # If the maxTimeMS option is set for a specific command, it should be used as the maxTimeMS command field without any + # modifications. This is different from timeoutMS because in that case, drivers subtract the target server's min + # RTT from the remaining timeout to derive a maxTimeMS field. + - description: "maxTimeMS option is used directly as the maxTimeMS field on a command" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: estimatedDocumentCount + object: *collection + arguments: + maxTimeMS: &maxTimeMS 50000 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: *maxTimeMS + + # Same test as above but with the maxCommitTimeMS option. + - description: "maxCommitTimeMS option is used directly as the maxTimeMS field on a commitTransaction command" + runOnRequirements: + # Note: minServerVersion is specified in top-level runOnRequirements + - topologies: ["replicaset", "sharded"] + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - session: + id: &session session + client: *client + sessionOptions: + defaultTransactionOptions: + maxCommitTimeMS: &maxCommitTimeMS 1000 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + session: *session + - name: commitTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: *maxCommitTimeMS diff --git a/source/client-side-operations-timeout/tests/non-tailable-cursors.json b/source/client-side-operations-timeout/tests/non-tailable-cursors.json new file mode 100644 index 0000000000..58c59cb32d --- /dev/null +++ b/source/client-side-operations-timeout/tests/non-tailable-cursors.json @@ -0,0 +1,543 @@ +{ + "description": "timeoutMS behaves correctly for non-tailable cursors", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 200, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + { + "collectionName": "aggregateOutputColl", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS applied to find if timeoutMode is cursor_lifetime", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "remaining timeoutMS applied to getMore if timeoutMode is unset", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMS": 200, + "batchSize": 2 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "remaining timeoutMS applied to getMore if timeoutMode is cursor_lifetime", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime", + "timeoutMS": 200, + "batchSize": 2 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to find if timeoutMode is iteration", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "iteration" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if timeoutMode is iteration - success", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "iteration", + "timeoutMS": 200, + "batchSize": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if timeoutMode is iteration - failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "iteration", + "batchSize": 2 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate with $out errors if timeoutMode is iteration", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$out": "aggregateOutputColl" + } + ], + "timeoutMS": 100, + "timeoutMode": "iteration" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [] + } + ] + }, + { + "description": "aggregate with $merge errors if timeoutMode is iteration", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [ + { + "$merge": "aggregateOutputColl" + } + ], + "timeoutMS": 100, + "timeoutMode": "iteration" + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/non-tailable-cursors.yml b/source/client-side-operations-timeout/tests/non-tailable-cursors.yml new file mode 100644 index 0000000000..c0d34c96b9 --- /dev/null +++ b/source/client-side-operations-timeout/tests/non-tailable-cursors.yml @@ -0,0 +1,300 @@ +description: "timeoutMS behaves correctly for non-tailable cursors" + +schemaVersion: "1.26" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 200 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 0 } + - { _id: 1 } + - { _id: 2 } + - collectionName: &aggregateOutputCollectionName aggregateOutputColl + databaseName: *databaseName + documents: [] + +tests: + # If timeoutMode is explicitly set to CURSOR_LIFETIME, the timeout should apply to the initial command. + # This should also be the case if timeoutMode is unset, but this is already tested in global-timeoutMS.yml. + - description: "timeoutMS applied to find if timeoutMode is cursor_lifetime" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 250 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + + # If timeoutMode is unset, it should default to CURSOR_LIFETIME and the time remaining after the find succeeds should + # be applied to the getMore. + - description: "remaining timeoutMS applied to getMore if timeoutMode is unset" + operations: + # Block find/getMore for 15ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 125 + # Run a find with timeoutMS=200 and batchSize=1 to force two batches, which will cause a find and a getMore to be + # sent. Both will block for 125ms, so together they will go over the timeout. + - name: find + object: *collection + arguments: + filter: {} + timeoutMS: 200 + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # Same test as above, but with timeoutMode explicitly set to CURSOR_LIFETIME. + - description: "remaining timeoutMS applied to getMore if timeoutMode is cursor_lifetime" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 150 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + timeoutMS: 200 + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS should apply to the initial find command and the command shouldn't have a + # maxTimeMS field. + - description: "timeoutMS applied to find if timeoutMode is iteration" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 250 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS applies separately to the initial find and the getMore on the cursor. Neither + # command should have a maxTimeMS field. This is a success test. The "find" is executed with timeoutMS=200 and both + # "find" and "getMore" commands are blocked for 125ms each. Neither exceeds the timeout, so iteration succeeds. + - description: "timeoutMS is refreshed for getMore if timeoutMode is iteration - success" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 125 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + timeoutMS: 200 + batchSize: 2 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS applies separately to the initial find and the getMore on the cursor. Neither + # command should have a maxTimeMS field. This is a failure test. The "find" inherits timeoutMS=200 and "getMore" + # commands are blocked for 250ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore if timeoutMode is iteration - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: iteration + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + - description: "aggregate with $out errors if timeoutMode is iteration" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: + - $out: *aggregateOutputCollectionName + timeoutMS: 100 + timeoutMode: iteration + expectError: + isClientError: true + expectEvents: + - client: *client + events: [] + + - description: "aggregate with $merge errors if timeoutMode is iteration" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: + - $merge: *aggregateOutputCollectionName + timeoutMS: 100 + timeoutMode: iteration + expectError: + isClientError: true + expectEvents: + - client: *client + events: [] diff --git a/source/client-side-operations-timeout/tests/override-collection-timeoutMS.json b/source/client-side-operations-timeout/tests/override-collection-timeoutMS.json new file mode 100644 index 0000000000..c56d45bcbb --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-collection-timeoutMS.json @@ -0,0 +1,3522 @@ +{ + "description": "timeoutMS can be overridden for a MongoCollection", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 10 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be configured on a MongoCollection - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoCollection - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 1000 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoCollection - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll", + "collectionOptions": { + "timeoutMS": 0 + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/override-collection-timeoutMS.yml b/source/client-side-operations-timeout/tests/override-collection-timeoutMS.yml new file mode 100644 index 0000000000..07e2a14429 --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-collection-timeoutMS.yml @@ -0,0 +1,1891 @@ +# Tests in this file are generated from override-collection-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for a MongoCollection" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + +initialData: + - collectionName: &collectionName coll + databaseName: *databaseName + documents: [] + +tests: + # For each collection-level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for a MongoCollection. Each test uses the client entity defined + # above to construct a collection entity with timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for a MongoCollection. Each test constructs a collection entity with + # timeoutMS=0 using the global client entity and configures a fail point to block the operation for 15ms. The + # operation should succeed and the command sent to the server should not contain a maxTimeMS field. + + - description: "timeoutMS can be configured on a MongoCollection - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoCollection - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 1000 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoCollection - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + collectionOptions: + timeoutMS: 0 + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + diff --git a/source/client-side-operations-timeout/tests/override-database-timeoutMS.json b/source/client-side-operations-timeout/tests/override-database-timeoutMS.json new file mode 100644 index 0000000000..11ff7a59fd --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-database-timeoutMS.json @@ -0,0 +1,4646 @@ +{ + "description": "timeoutMS can be overridden for a MongoDatabase", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 10 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be configured on a MongoDatabase - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - aggregate on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - listCollections on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - listCollectionNames on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - runCommand on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - createChangeStream on database", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - aggregate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - count on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - countDocuments on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - estimatedDocumentCount on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - distinct on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - find on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - findOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - listIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - listIndexNames on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - createChangeStream on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - insertOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - insertMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - deleteOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - deleteMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - replaceOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - updateOne on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - updateMany on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - findOneAndDelete on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - findOneAndReplace on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - findOneAndUpdate on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - bulkWrite on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - createIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - dropIndex on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured on a MongoDatabase - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 1000 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 on a MongoDatabase - dropIndexes on collection", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test", + "databaseOptions": { + "timeoutMS": 0 + } + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/override-database-timeoutMS.yml b/source/client-side-operations-timeout/tests/override-database-timeoutMS.yml new file mode 100644 index 0000000000..4ded17164d --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-database-timeoutMS.yml @@ -0,0 +1,2501 @@ +# Tests in this file are generated from override-database-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for a MongoDatabase" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + +initialData: + - collectionName: &collectionName coll + databaseName: &databaseName test + documents: [] + +tests: + # For each database-level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for a MongoDatabase. Each test constructs uses the client entity + # defined above to construct a database entity with timeoutMS=1000 and configures a fail point to block the operation + # for 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for a MongoDatabase. Each test constructs a database entity with timeoutMS=0 + # using the global client entity and configures a fail point to block the operation for 15ms. The operation should + # succeed and the command sent to the server should not contain a maxTimeMS field. + + - description: "timeoutMS can be configured on a MongoDatabase - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - aggregate on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - listCollections on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - listCollectionNames on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - runCommand on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - createChangeStream on database" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - aggregate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - count on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - countDocuments on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - estimatedDocumentCount on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - distinct on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - find on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - findOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - listIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - listIndexNames on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - createChangeStream on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - insertOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - insertMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - deleteOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - deleteMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - replaceOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - updateOne on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - updateMany on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - findOneAndDelete on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - findOneAndReplace on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - findOneAndUpdate on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - bulkWrite on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - createIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - dropIndex on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + name: "x_1" + - name: dropIndex + object: *collection + arguments: + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured on a MongoDatabase - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 1000 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 on a MongoDatabase - dropIndexes on collection" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - database: + id: &database database + client: *client + databaseName: *databaseName + databaseOptions: + timeoutMS: 0 + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + diff --git a/source/client-side-operations-timeout/tests/override-operation-timeoutMS.json b/source/client-side-operations-timeout/tests/override-operation-timeoutMS.json new file mode 100644 index 0000000000..30b550089f --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-operation-timeoutMS.json @@ -0,0 +1,3650 @@ +{ + "description": "timeoutMS can be overridden for an operation", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 10 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be configured for an operation - listDatabases on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listDatabases on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - listDatabaseNames on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listDatabaseNames on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - createChangeStream on client", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.4", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - createChangeStream on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - aggregate on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - aggregate on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 0, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - listCollections on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listCollections on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - listCollectionNames on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listCollectionNames on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - runCommand on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - runCommand on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "timeoutMS": 0, + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - createChangeStream on database", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.4", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - createChangeStream on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - aggregate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - aggregate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - count on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - count on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - countDocuments on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - countDocuments on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - estimatedDocumentCount on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - estimatedDocumentCount on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - distinct on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - distinct on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - find on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - find on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - findOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - findOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - listIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - listIndexNames on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - listIndexNames on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "listIndexNames", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - createChangeStream on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.4", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - createChangeStream on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - insertOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - insertOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - insertMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - insertMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - deleteOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - deleteOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - deleteMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - deleteMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - replaceOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - replaceOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - updateOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - updateOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - updateMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - updateMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "updateMany", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - findOneAndDelete on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - findOneAndDelete on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - findOneAndReplace on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - findOneAndReplace on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - findOneAndUpdate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - findOneAndUpdate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - bulkWrite on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - bulkWrite on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - createIndex on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - createIndex on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "keys": { + "x": 1 + }, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test", + "command": { + "createIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - dropIndex on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "timeoutMS": 1000, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - dropIndex on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "createIndex", + "object": "collection", + "arguments": { + "keys": { + "x": 1 + }, + "timeoutMS": 0, + "name": "x_1" + } + }, + { + "name": "dropIndex", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "createIndexes", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be configured for an operation - dropIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS can be set to 0 for an operation - dropIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "dropIndexes" + ], + "blockConnection": true, + "blockTimeMS": 15 + } + } + } + }, + { + "name": "dropIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "dropIndexes", + "databaseName": "test", + "command": { + "dropIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/override-operation-timeoutMS.yml b/source/client-side-operations-timeout/tests/override-operation-timeoutMS.yml new file mode 100644 index 0000000000..9aa4f657a6 --- /dev/null +++ b/source/client-side-operations-timeout/tests/override-operation-timeoutMS.yml @@ -0,0 +1,1957 @@ +# Tests in this file are generated from override-operation-timeoutMS.yml.template. + +description: "timeoutMS can be overridden for an operation" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 10 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each level operation, we execute two tests: + # + # 1. timeoutMS can be overridden to a non-zero value for an operation. Each test executes an operation using one of + # the entities defined above with an overridden timeoutMS=1000 and configures a fail point to block the operation for + # 15ms so the operation succeeds. + # + # 2. timeoutMS can be overridden to 0 for an operation. Each test executes an operation using the entities defined + # above with an overridden timeoutMS=0 so the operation succeeds. + + - description: "timeoutMS can be configured for an operation - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabases + object: *client + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 15 + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset"] + - minServerVersion: "4.4" + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *client + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + timeoutMS: 1000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *database + arguments: + timeoutMS: 0 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollections + object: *database + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 15 + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - runCommand on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + timeoutMS: 1000 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - runCommand on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 15 + - name: runCommand + object: *database + arguments: + timeoutMS: 0 + command: { ping: 1 } + commandName: ping + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset"] + - minServerVersion: "4.4" + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *database + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: aggregate + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: count + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: countDocuments + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 15 + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + timeoutMS: 1000 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 15 + - name: distinct + object: *collection + arguments: + timeoutMS: 0 + fieldName: x + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: find + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 15 + - name: findOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexes + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - listIndexNames on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - listIndexNames on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: listIndexNames + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset"] + - minServerVersion: "4.4" + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 15 + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + timeoutMS: 1000 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertOne + object: *collection + arguments: + timeoutMS: 0 + document: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + timeoutMS: 1000 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: insertMany + object: *collection + arguments: + timeoutMS: 0 + documents: + - { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - deleteMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - deleteMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 15 + - name: deleteMany + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: replaceOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - updateMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - updateMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 15 + - name: updateMany + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 15 + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 1000 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 15 + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 0 + requests: + - insertOne: + document: { _id: 1 } + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - createIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + timeoutMS: 1000 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - createIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: createIndex + object: *collection + arguments: + timeoutMS: 0 + keys: { x: 1 } + name: "x_1" + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + command: + createIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - dropIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + timeoutMS: 1000 + name: "x_1" + - name: dropIndex + object: *collection + arguments: + timeoutMS: 1000 + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - dropIndex on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + # Create the index first so dropIndex doesn't return IndexNotFound on + # servers prior to 8.3. + - name: createIndex + object: *collection + arguments: + keys: { x: 1 } + timeoutMS: 0 + name: "x_1" + - name: dropIndex + object: *collection + arguments: + timeoutMS: 0 + name: "x_1" + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: createIndexes + databaseName: *databaseName + + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS can be configured for an operation - dropIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 1000 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "timeoutMS can be set to 0 for an operation - dropIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["dropIndexes"] + blockConnection: true + blockTimeMS: 15 + - name: dropIndexes + object: *collection + arguments: + timeoutMS: 0 + + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: dropIndexes + databaseName: *databaseName + command: + dropIndexes: *collectionName + maxTimeMS: { $$exists: false } + diff --git a/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.json b/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.json new file mode 100644 index 0000000000..aded781aee --- /dev/null +++ b/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.json @@ -0,0 +1,3042 @@ +{ + "description": "legacy timeouts behave correctly for retryable operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "socketTimeoutMS": 100 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "operation succeeds after one socket timeout - insertOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - insertOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - insertMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - insertMany on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - deleteOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - deleteOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - replaceOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - replaceOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - updateOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - updateOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - findOneAndDelete on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - findOneAndDelete on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - findOneAndReplace on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - findOneAndReplace on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - findOneAndUpdate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - findOneAndUpdate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - bulkWrite on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - bulkWrite on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - listDatabases on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - listDatabases on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - listDatabaseNames on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - listDatabaseNames on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - createChangeStream on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - createChangeStream on client", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - aggregate on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - aggregate on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - listCollections on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - listCollections on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - listCollectionNames on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - listCollectionNames on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - createChangeStream on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - createChangeStream on database", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1 + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - aggregate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - aggregate on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - count on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - count on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - countDocuments on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - countDocuments on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - estimatedDocumentCount on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - estimatedDocumentCount on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - distinct on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - distinct on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - find on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - find on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - findOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - findOne on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - listIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - listIndexes on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation succeeds after one socket timeout - createChangeStream on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + }, + { + "description": "operation fails after two consecutive socket timeouts - createChangeStream on collection", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 125 + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.yml b/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.yml new file mode 100644 index 0000000000..abcaec6127 --- /dev/null +++ b/source/client-side-operations-timeout/tests/retryability-legacy-timeouts.yml @@ -0,0 +1,1676 @@ +# Tests in this file are generated from retryability-legacy-timeouts.yml.template. + +description: "legacy timeouts behave correctly for retryable operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + socketTimeoutMS: 100 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run two tests: + # + # 1. Socket timeouts are retried once - Each test constructs a client entity with socketTimeoutMS=100, configures a + # fail point to block the operation once for 125ms, and expects the operation to succeed. + # + # 2. Operations fail after two consecutive socket timeouts - Same as (1) but the fail point is configured to block + # the operation twice and the test expects the operation to fail. + + - description: "operation succeeds after one socket timeout - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - insertOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - insertMany on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 125 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - description: "operation fails after two consecutive socket timeouts - deleteOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 125 + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + - description: "operation succeeds after one socket timeout - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation fails after two consecutive socket timeouts - replaceOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation succeeds after one socket timeout - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation fails after two consecutive socket timeouts - updateOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 125 + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndDelete on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndReplace on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOneAndUpdate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 125 + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + - description: "operation succeeds after one socket timeout - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation fails after two consecutive socket timeouts - bulkWrite on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 125 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - description: "operation succeeds after one socket timeout - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation fails after two consecutive socket timeouts - listDatabases on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation succeeds after one socket timeout - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabaseNames + object: *client + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation fails after two consecutive socket timeouts - listDatabaseNames on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 125 + - name: listDatabaseNames + object: *client + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + - description: "operation succeeds after one socket timeout - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - createChangeStream on client" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - aggregate on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollections + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation fails after two consecutive socket timeouts - listCollections on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation succeeds after one socket timeout - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation fails after two consecutive socket timeouts - listCollectionNames on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 125 + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + - description: "operation succeeds after one socket timeout - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation fails after two consecutive socket timeouts - createChangeStream on database" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + - description: "operation succeeds after one socket timeout - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - aggregate on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation succeeds after one socket timeout - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: count + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation fails after two consecutive socket timeouts - count on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: count + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation succeeds after one socket timeout - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - countDocuments on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation succeeds after one socket timeout - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: estimatedDocumentCount + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation fails after two consecutive socket timeouts - estimatedDocumentCount on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 125 + - name: estimatedDocumentCount + object: *collection + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + - description: "operation succeeds after one socket timeout - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 125 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - description: "operation fails after two consecutive socket timeouts - distinct on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 125 + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + - description: "operation succeeds after one socket timeout - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: find + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation fails after two consecutive socket timeouts - find on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: find + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation succeeds after one socket timeout - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: findOne + object: *collection + arguments: + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation fails after two consecutive socket timeouts - findOne on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 125 + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + - description: "operation succeeds after one socket timeout - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 125 + - name: listIndexes + object: *collection + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - description: "operation fails after two consecutive socket timeouts - listIndexes on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 125 + - name: listIndexes + object: *collection + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + - description: "operation succeeds after one socket timeout - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - description: "operation fails after two consecutive socket timeouts - createChangeStream on collection" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 125 + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + # Network errors are considered client errors by the unified test format spec. + isClientError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + \ No newline at end of file diff --git a/source/client-side-operations-timeout/tests/retryability-timeoutMS.json b/source/client-side-operations-timeout/tests/retryability-timeoutMS.json new file mode 100644 index 0000000000..8fb88cfd55 --- /dev/null +++ b/source/client-side-operations-timeout/tests/retryability-timeoutMS.json @@ -0,0 +1,5720 @@ +{ + "description": "timeoutMS behaves correctly for retryable operations", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.2", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 100, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS applies to whole operation, not individual attempts - insertOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - insertOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - insertOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - insertMany on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "x": 1 + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - insertMany on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - insertMany on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "documents": [ + { + "x": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - deleteOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "delete" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - deleteOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - deleteOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "deleteOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "databaseName": "test", + "command": { + "delete": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - replaceOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - replaceOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - replaceOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - updateOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "update" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - updateOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - updateOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "updateOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "databaseName": "test", + "command": { + "update": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - findOneAndDelete on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - findOneAndDelete on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - findOneAndDelete on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - findOneAndReplace on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - findOneAndReplace on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - findOneAndReplace on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - findOneAndUpdate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - findOneAndUpdate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - findOneAndUpdate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "databaseName": "test", + "command": { + "findAndModify": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - bulkWrite on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - bulkWrite on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - bulkWrite on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - listDatabases on client", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - listDatabases on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - listDatabases on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - listDatabaseNames on client", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - listDatabaseNames on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - listDatabaseNames on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listDatabaseNames", + "object": "client", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases", + "databaseName": "admin", + "command": { + "listDatabases": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - createChangeStream on client", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - createChangeStream on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.3.1", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - createChangeStream on client", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "client", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "admin", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - aggregate on database", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - aggregate on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - aggregate on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "database", + "arguments": { + "timeoutMS": 0, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - listCollections on database", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - listCollections on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - listCollections on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollections", + "object": "database", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - listCollectionNames on database", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - listCollectionNames on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - listCollectionNames on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listCollectionNames", + "object": "database", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections", + "databaseName": "test", + "command": { + "listCollections": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - createChangeStream on database", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - createChangeStream on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.3.1", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - createChangeStream on database", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "database", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": 1, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - aggregate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - aggregate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - aggregate on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "aggregate", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - count on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - count on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - count on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "count", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - countDocuments on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - countDocuments on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - countDocuments on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - estimatedDocumentCount on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "count" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - estimatedDocumentCount on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - estimatedDocumentCount on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "estimatedDocumentCount", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "count", + "databaseName": "test", + "command": { + "count": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - distinct on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "distinct" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - distinct on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - distinct on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "distinct", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "distinct", + "databaseName": "test", + "command": { + "distinct": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - find on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - find on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - find on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - findOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - findOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - findOne on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "findOne", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - listIndexes on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - listIndexes on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 1000 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - listIndexes on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "listIndexes", + "object": "collection", + "arguments": { + "timeoutMS": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes", + "databaseName": "test", + "command": { + "listIndexes": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS applies to whole operation, not individual attempts - createChangeStream on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "blockConnection": true, + "blockTimeMS": 60, + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + { + "description": "operation is retried multiple times for non-zero timeoutMS - createChangeStream on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.3.1", + "maxServerVersion": "8.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 1000, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + } + ] + } + ] + }, + { + "description": "operation is retried multiple times if timeoutMS is zero - createChangeStream on collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7, + "closeConnection": false, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "timeoutMS": 0, + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate", + "databaseName": "test", + "command": { + "aggregate": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/retryability-timeoutMS.yml b/source/client-side-operations-timeout/tests/retryability-timeoutMS.yml new file mode 100644 index 0000000000..ff5214f81b --- /dev/null +++ b/source/client-side-operations-timeout/tests/retryability-timeoutMS.yml @@ -0,0 +1,2841 @@ +# Tests in this file are generated from retryability-timeoutMS.yml.template. + +description: "timeoutMS behaves correctly for retryable operations" + +schemaVersion: "1.26" + +# failCommand is available on 4.0+ replica sets and 4.2+ sharded clusters. +runOnRequirements: + - minServerVersion: "4.0" + topologies: ["replicaset"] + - minServerVersion: "4.2" + topologies: ["sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 100 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # For each retryable operation, run three tests: + # + # 1. timeoutMS applies to the whole operation, not to individual attempts - Client timeoutMS=100 and the operation is + # fails with a retryable error after being blocked server-side for 60ms. The operation should fail with a timeout error + # because the second attempt should take it over the 100ms limit. This test only runs on 4.4+ because it uses the + # blockConnection option in failCommand. + # + # 2. operation is retried multiple times if timeoutMS is set to a non-zero value - Client timeoutMS=100 and the + # operation fails with a retryable error twice. Drivers should send the original operation and two retries, the + # second of which should succeed. + # + # 3. operation is retried multiple times if timeoutMS is set to a zero - Override timeoutMS to zero for the operation + # and set a fail point to force a retryable error twice. Drivers should send the original operation and two retries, + # the second of which should succeed. + # + # The fail points in these tests use error code 7 (HostNotFound) because it is a retryable error but does not trigger + # an SDAM state change so we don't lose any time to server rediscovery. The tests also explicitly specify an + # errorLabels array in the fail point to avoid behavioral differences among server types and ensure that the error + # will be considered retryable. + + - description: "timeoutMS applies to whole operation, not individual attempts - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + timeoutMS: 1000 + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - insertOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertOne + object: *collection + arguments: + timeoutMS: 0 + document: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + documents: + - { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + timeoutMS: 1000 + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - insertMany on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: insertMany + object: *collection + arguments: + timeoutMS: 0 + documents: + - { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["delete"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - deleteOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["delete"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: deleteOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: delete + databaseName: *databaseName + command: + delete: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - replaceOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: replaceOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["update"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - updateOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["update"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: updateOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: update + databaseName: *databaseName + command: + update: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndDelete on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndDelete + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + filter: {} + replacement: { x: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndReplace on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndReplace + object: *collection + arguments: + timeoutMS: 0 + filter: {} + replacement: { x: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["findAndModify"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + filter: {} + update: { $set: { x: 1 } } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOneAndUpdate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["findAndModify"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOneAndUpdate + object: *collection + arguments: + timeoutMS: 0 + filter: {} + update: { $set: { x: 1 } } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: findAndModify + databaseName: *databaseName + command: + findAndModify: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["insert"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { _id: 1 } + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 1000 + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - bulkWrite on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: bulkWrite + object: *collection + arguments: + timeoutMS: 0 + requests: + - insertOne: + document: { _id: 1 } + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listDatabases on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabases + object: *client + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listDatabases"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listDatabaseNames on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listDatabases"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listDatabaseNames + object: *client + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listDatabases + databaseName: admin + command: + listDatabases: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: ["replicaset"] + - minServerVersion: "4.3.1" # failCommand errorLabels option + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on client" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *client + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: admin + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - aggregate on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - aggregate on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + timeoutMS: 1000 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - aggregate on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *database + arguments: + timeoutMS: 0 + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listCollections on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listCollections on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listCollections on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollections + object: *database + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listCollections"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listCollectionNames on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listCollections"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listCollectionNames + object: *database + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listCollections + databaseName: *databaseName + command: + listCollections: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: ["replicaset"] + - minServerVersion: "4.3.1" # failCommand errorLabels option + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on database" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *database + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: 1 + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - aggregate on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: aggregate + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - count on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - count on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - count on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: count + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - countDocuments on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: countDocuments + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["count"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - estimatedDocumentCount on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["count"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: estimatedDocumentCount + object: *collection + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: count + databaseName: *databaseName + command: + count: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - distinct on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["distinct"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - distinct on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + timeoutMS: 1000 + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - distinct on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["distinct"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: distinct + object: *collection + arguments: + timeoutMS: 0 + fieldName: x + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: distinct + databaseName: *databaseName + command: + distinct: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - find on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - find on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - find on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: find + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - findOne on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + filter: {} + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - findOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + timeoutMS: 1000 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - findOne on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: findOne + object: *collection + arguments: + timeoutMS: 0 + filter: {} + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["listIndexes"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + arguments: + timeoutMS: 1000 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - listIndexes on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["listIndexes"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: listIndexes + object: *collection + arguments: + timeoutMS: 0 + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: listIndexes + databaseName: *databaseName + command: + listIndexes: *collectionName + maxTimeMS: { $$exists: false } + - description: "timeoutMS applies to whole operation, not individual attempts - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: ["aggregate"] + blockConnection: true + blockTimeMS: 60 + errorCode: 7 + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + + expectError: + isTimeoutError: true + - description: "operation is retried multiple times for non-zero timeoutMS - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: ["replicaset"] + - minServerVersion: "4.3.1" # failCommand errorLabels option + maxServerVersion: "8.99" # Skip sharded 9.0+ due to reasons noted in DRIVERS-3556. + # Removing the skip may be desired in the future if this test migrates from timeoutMS to maxTimeMS. See DRIVERS-3556 for a suggested approach: add a unified test runner option to advance the config server's cluster time immediately before opening a change stream. Otherwise, existing tests using maxTimeMS < periodicNoopIntervalSecs on 9.0+ sharded clusters may unexpectedly fail with server timeouts. + topologies: ["sharded"] + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 1000 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$type: ["int", "long"] } + - description: "operation is retried multiple times if timeoutMS is zero - createChangeStream on collection" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["aggregate"] + errorCode: 7 + closeConnection: false + errorLabels: ["RetryableWriteError"] + - name: createChangeStream + object: *collection + arguments: + timeoutMS: 0 + pipeline: [] + + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: aggregate + databaseName: *databaseName + command: + aggregate: *collectionName + maxTimeMS: { $$exists: false } + \ No newline at end of file diff --git a/source/client-side-operations-timeout/tests/runCursorCommand.json b/source/client-side-operations-timeout/tests/runCursorCommand.json new file mode 100644 index 0000000000..e5182e338e --- /dev/null +++ b/source/client-side-operations-timeout/tests/runCursorCommand.json @@ -0,0 +1,587 @@ +{ + "description": "runCursorCommand", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "commandClient", + "uriOptions": { + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "commandDb", + "client": "commandClient", + "databaseName": "commandDb" + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "collection", + "database": "db", + "collectionName": "collection" + } + } + ], + "initialData": [ + { + "collectionName": "collection", + "databaseName": "db", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "errors if timeoutMode is set without timeoutMS", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "collection" + }, + "timeoutMode": "cursorLifetime" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "error if timeoutMode is cursorLifetime and cursorType is tailableAwait", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "collection" + }, + "timeoutMode": "cursorLifetime", + "cursorType": "tailableAwait" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "Non-tailable cursor lifetime remaining timeoutMS applied to getMore if timeoutMode is unset", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 60 + } + } + } + }, + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "timeoutMS": 100, + "command": { + "find": "collection", + "batchSize": 2 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "command": { + "find": "collection", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "Non-tailable cursor iteration timeoutMS is refreshed for getMore if timeoutMode is iteration - failure", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 60 + } + } + } + }, + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "collection", + "batchSize": 2 + }, + "timeoutMode": "iteration", + "timeoutMS": 100, + "batchSize": 2 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "db", + "command": { + "find": "collection", + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "db", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "Tailable cursor iteration timeoutMS is refreshed for getMore - failure", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 60 + } + } + } + }, + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection", + "capped": true, + "size": 4096, + "max": 3 + }, + "saveResultAsEntity": "cappedCollection" + }, + { + "name": "insertMany", + "object": "cappedCollection", + "arguments": { + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + }, + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "cappedCollection", + "batchSize": 1, + "tailable": true + }, + "timeoutMode": "iteration", + "timeoutMS": 100, + "batchSize": 1, + "cursorType": "tailable" + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "db", + "command": { + "find": "cappedCollection", + "tailable": true, + "awaitData": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "db", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "cappedCollection", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "Tailable cursor awaitData iteration timeoutMS is refreshed for getMore - failure", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 60 + } + } + } + }, + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection", + "capped": true, + "size": 4096, + "max": 3 + }, + "saveResultAsEntity": "cappedCollection" + }, + { + "name": "insertMany", + "object": "cappedCollection", + "arguments": { + "documents": [ + { + "foo": "bar" + }, + { + "fizz": "buzz" + } + ] + } + }, + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "command": { + "find": "cappedCollection", + "tailable": true, + "awaitData": true + }, + "cursorType": "tailableAwait", + "batchSize": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "db", + "command": { + "find": "cappedCollection", + "tailable": true, + "awaitData": true, + "maxTimeMS": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "db", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "cappedCollection" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/runCursorCommand.yml b/source/client-side-operations-timeout/tests/runCursorCommand.yml new file mode 100644 index 0000000000..e0b7cfe6d2 --- /dev/null +++ b/source/client-side-operations-timeout/tests/runCursorCommand.yml @@ -0,0 +1,308 @@ +description: runCursorCommand + +schemaVersion: '1.26' + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &commandClient commandClient + uriOptions: + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: [commandStartedEvent, commandSucceededEvent] + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + ignoreCommandMonitoringEvents: [killCursors] + - database: # For tests that need success event assertions + id: &commandDb commandDb + client: *commandClient + databaseName: *commandDb + - database: + id: &db db + client: *client + databaseName: *db + - collection: + id: &collection collection + database: *db + collectionName: *collection + +initialData: + - collectionName: *collection + databaseName: *db + documents: &documents + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + +tests: + - description: errors if timeoutMode is set without timeoutMS + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + command: { find: *collection } + timeoutMode: cursorLifetime + expectError: + isClientError: true + + - description: error if timeoutMode is cursorLifetime and cursorType is tailableAwait + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + command: { find: *collection } + timeoutMode: cursorLifetime + cursorType: tailableAwait + expectError: + isClientError: true + + # If timeoutMode is unset, it should default to CURSOR_LIFETIME and the time remaining after the find succeeds should be applied to the getMore + - description: Non-tailable cursor lifetime remaining timeoutMS applied to getMore if timeoutMode is unset + runOnRequirements: + - serverless: forbid + operations: + # Block find/getMore for 60ms. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [find, getMore] + blockConnection: true + blockTimeMS: 60 + # Run a find with timeoutMS less than double our failPoint blockTimeMS and + # batchSize less than the total document count will cause a find and a + # getMore to be sent. Both will block for 60ms so together they will go + # over the timeout. + - name: runCursorCommand + object: *db + arguments: + commandName: find + timeoutMS: 100 + command: { find: *collection, batchSize: 2 } + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + command: + find: *collection + maxTimeMS: { $$type: [int, long] } + - commandStartedEvent: + commandName: getMore + command: + getMore: { $$type: [int, long] } + collection: *collection + maxTimeMS: { $$exists: false } + + # If timeoutMode=ITERATION, timeoutMS applies separately to the initial find and the getMore on the cursor. Neither + # command should have a maxTimeMS field. This is a failure test. The "find" inherits timeoutMS=100 and "getMore" + # commands are blocked for 60ms, causing iteration to fail with a timeout error. + - description: Non-tailable cursor iteration timeoutMS is refreshed for getMore if timeoutMode is iteration - failure + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 60 + - name: runCursorCommand + object: *db + arguments: + commandName: find + command: { find: *collection, batchSize: 2 } + timeoutMode: iteration + timeoutMS: 100 + batchSize: 2 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *db + command: + find: *collection + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *db + command: + getMore: { $$type: ["int", "long"] } + collection: *collection + maxTimeMS: { $$exists: false } + + # The timeoutMS option should apply separately to the initial "find" and each getMore. This is a failure test. The + # find inherits timeoutMS=100 from the collection and the getMore command blocks for 60ms, causing iteration to fail + # with a timeout error. + - description: Tailable cursor iteration timeoutMS is refreshed for getMore - failure + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 60 + - name: dropCollection + object: *db + arguments: + collection: &cappedCollection cappedCollection + - name: createCollection + object: *db + arguments: + collection: *cappedCollection + capped: true + size: 4096 + max: 3 + saveResultAsEntity: *cappedCollection + - name: insertMany + object: *cappedCollection + arguments: + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - name: createCommandCursor + object: *db + arguments: + commandName: find + command: { find: *cappedCollection, batchSize: 1, tailable: true } + timeoutMode: iteration + timeoutMS: 100 + batchSize: 1 + cursorType: tailable + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate the cursor twice: the first iteration will return the document from the batch in the find and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: drop + - commandStartedEvent: + commandName: create + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: find + databaseName: *db + command: + find: *cappedCollection + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *db + command: + getMore: { $$type: ["int", "long"] } + collection: *cappedCollection + maxTimeMS: { $$exists: false } + + # The timeoutMS value should be refreshed for getMore's. This is a failure test. The find inherits timeoutMS=10 from + # the collection and the getMore blocks for 15ms, causing iteration to fail with a timeout error. + - description: Tailable cursor awaitData iteration timeoutMS is refreshed for getMore - failure + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 60 + - name: dropCollection + object: *db + arguments: + collection: &cappedCollection cappedCollection + - name: createCollection + object: *db + arguments: + collection: *cappedCollection + capped: true + size: 4096 + max: 3 + saveResultAsEntity: *cappedCollection + - name: insertMany + object: *cappedCollection + arguments: + documents: [ { foo: bar }, { fizz: buzz } ] + - name: createCommandCursor + object: *db + arguments: + command: { find: *cappedCollection, tailable: true, awaitData: true } + cursorType: tailableAwait + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: drop + - commandStartedEvent: + commandName: create + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: find + databaseName: *db + command: + find: *cappedCollection + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *db + command: + getMore: { $$type: ["int", "long"] } + collection: *cappedCollection diff --git a/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json b/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json new file mode 100644 index 0000000000..50feabf609 --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json @@ -0,0 +1,334 @@ +{ + "description": "sessions inherit timeoutMS from their parent MongoClient", + "schemaVersion": "1.26", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 500, + "minPoolSize": 1 + }, + "awaitMinPoolSizeMS": 10000, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS applied to commitTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "commitTransaction", + "object": "session", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "commitTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to abortTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "abortTransaction", + "object": "session" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to withTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert", + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.yml b/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.yml new file mode 100644 index 0000000000..4cb9f1c646 --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.yml @@ -0,0 +1,175 @@ +description: "sessions inherit timeoutMS from their parent MongoClient" + +schemaVersion: "1.26" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 500 + minPoolSize: 1 + awaitMinPoolSizeMS: 10000 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS applied to commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert", "abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: [ "int", "long" ] } + - commandFailedEvent: + commandName: abortTransaction diff --git a/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.json b/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.json new file mode 100644 index 0000000000..78e873f948 --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.json @@ -0,0 +1,336 @@ +{ + "description": "timeoutMS can be overridden for individual session operations", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS can be overridden for commitTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "commitTransaction", + "object": "session", + "arguments": { + "timeoutMS": 500 + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "commitTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to abortTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "abortTransaction", + "object": "session", + "arguments": { + "timeoutMS": 500 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to withTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert", + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "timeoutMS": 500, + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.yml b/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.yml new file mode 100644 index 0000000000..c91f7366dc --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.yml @@ -0,0 +1,176 @@ +description: "timeoutMS can be overridden for individual session operations" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS can be overridden for commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + arguments: + timeoutMS: 500 + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + arguments: + timeoutMS: 500 + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert", "abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: withTransaction + object: *session + arguments: + timeoutMS: 500 + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction diff --git a/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.json b/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.json new file mode 100644 index 0000000000..c4a446c531 --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.json @@ -0,0 +1,332 @@ +{ + "description": "timeoutMS can be overridden at the level of a ClientSession", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + }, + { + "session": { + "id": "session", + "client": "client", + "sessionOptions": { + "defaultTimeoutMS": 500 + } + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "timeoutMS applied to commitTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "commitTransaction", + "object": "session", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "commitTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to abortTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + } + }, + { + "name": "abortTransaction", + "object": "session" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll" + } + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + }, + { + "description": "timeoutMS applied to withTransaction", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert", + "abortTransaction" + ], + "blockConnection": true, + "blockTimeMS": 600 + } + } + } + }, + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 1 + } + }, + "expectError": { + "isTimeoutError": true + } + } + ] + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "databaseName": "test", + "command": { + "insert": "coll", + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction", + "databaseName": "admin", + "command": { + "abortTransaction": 1, + "maxTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + } + }, + { + "commandFailedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.yml b/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.yml new file mode 100644 index 0000000000..815d749052 --- /dev/null +++ b/source/client-side-operations-timeout/tests/sessions-override-timeoutMS.yml @@ -0,0 +1,173 @@ +description: "timeoutMS can be overridden at the level of a ClientSession" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + - session: + id: &session session + client: *client + sessionOptions: + defaultTimeoutMS: 500 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + # Drivers ignore errors from abortTransaction, so the tests in this file use commandSucceededEvent and + # commandFailedEvent events to assert success/failure. + + - description: "timeoutMS applied to commitTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: commitTransaction + object: *session + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: commitTransaction + + - description: "timeoutMS applied to abortTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + - name: abortTransaction + object: *session + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: abortTransaction + + - description: "timeoutMS applied to withTransaction" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["insert", "abortTransaction"] + blockConnection: true + blockTimeMS: 600 + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 1 } + expectError: + isTimeoutError: true + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: insert + databaseName: *databaseName + command: + insert: *collectionName + # withTransaction specifies timeoutMS for each operation in the callback that uses the session, so the + # insert command should have a maxTimeMS field. + maxTimeMS: { $$type: ["int", "long"] } + - commandFailedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + databaseName: admin + command: + abortTransaction: 1 + maxTimeMS: { $$type: [ "int", "long" ] } + - commandFailedEvent: + commandName: abortTransaction diff --git a/source/client-side-operations-timeout/tests/tailable-awaitData.json b/source/client-side-operations-timeout/tests/tailable-awaitData.json new file mode 100644 index 0000000000..80e95ca906 --- /dev/null +++ b/source/client-side-operations-timeout/tests/tailable-awaitData.json @@ -0,0 +1,632 @@ +{ + "description": "timeoutMS behaves correctly for tailable awaitData cursors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 200 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "createOptions": { + "capped": true, + "size": 500 + }, + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "error if timeoutMode is cursor_lifetime", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime", + "cursorType": "tailableAwait" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "error on find if maxAwaitTimeMS is greater than timeoutMS", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "timeoutMS": 5, + "maxAwaitTimeMS": 10 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "error on aggregate if maxAwaitTimeMS is greater than timeoutMS", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 10 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "error on watch if maxAwaitTimeMS is greater than timeoutMS", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 10 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "error on find if maxAwaitTimeMS is equal to timeoutMS", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "timeoutMS": 5, + "maxAwaitTimeMS": 5 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "error on aggregate if maxAwaitTimeMS is equal to timeoutMS", + "operations": [ + { + "name": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 5 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "error on watch if maxAwaitTimeMS is equal to timeoutMS", + "operations": [ + { + "name": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [], + "timeoutMS": 5, + "maxAwaitTimeMS": 5 + }, + "expectError": { + "isClientError": true, + "isTimeoutError": false + } + } + ] + }, + { + "description": "timeoutMS applied to find", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 300 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": true, + "maxTimeMS": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "timeoutMS": 250, + "batchSize": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": true, + "maxTimeMS": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "timeoutMS": 250, + "batchSize": 1, + "maxAwaitTimeMS": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": true, + "maxTimeMS": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": 1 + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore - failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "batchSize": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": true, + "maxTimeMS": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll" + } + } + } + ] + } + ] + }, + { + "description": "apply remaining timeoutMS if less than maxAwaitTimeMS", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 30 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + }, + "cursorType": "tailableAwait", + "batchSize": 1, + "maxAwaitTimeMS": 100, + "timeoutMS": 200 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateOnce", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "maxTimeMS": { + "$$lte": 100 + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "maxTimeMS": { + "$$lte": 70 + } + } + } + } + ] + } + ] + }, + { + "description": "apply maxAwaitTimeMS if less than remaining timeout", + "operations": [ + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailableAwait", + "batchSize": 1, + "maxAwaitTimeMS": 100, + "timeoutMS": 200 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateOnce", + "object": "tailableCursor" + }, + { + "name": "iterateOnce", + "object": "tailableCursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test" + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "maxTimeMS": { + "$$lte": 100 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/tailable-awaitData.yml b/source/client-side-operations-timeout/tests/tailable-awaitData.yml new file mode 100644 index 0000000000..c4d5a82f52 --- /dev/null +++ b/source/client-side-operations-timeout/tests/tailable-awaitData.yml @@ -0,0 +1,373 @@ +description: "timeoutMS behaves correctly for tailable awaitData cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid # Capped collections are not allowed for serverless. + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 200 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + createOptions: + capped: true + size: 500 + documents: + - { _id: 0 } + - { _id: 1 } + +tests: + - description: "error if timeoutMode is cursor_lifetime" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + cursorType: tailableAwait + expectError: + isClientError: true + + - description: "error on find if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + isTimeoutError: false + + - description: "error on aggregate if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + isTimeoutError: false + + - description: "error on watch if maxAwaitTimeMS is greater than timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 10 + expectError: + isClientError: true + isTimeoutError: false + + - description: "error on find if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + isTimeoutError: false + + - description: "error on aggregate if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: aggregate + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + isTimeoutError: false + + - description: "error on watch if maxAwaitTimeMS is equal to timeoutMS" + operations: + - name: createChangeStream + object: *collection + arguments: + pipeline: [] + timeoutMS: 5 + maxAwaitTimeMS: 5 + expectError: + isClientError: true + isTimeoutError: false + + - description: "timeoutMS applied to find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 300 + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + + # If maxAwaitTimeMS is not set, timeoutMS should be refreshed for the getMore and the getMore should not have a + # maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is not set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 150 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 250 + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. The first iteration will return the document from the first batch and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # If maxAwaitTimeMS is set for the initial command, timeoutMS should still be refreshed for the getMore and the + # getMore command should have a maxTimeMS field. + - description: "timeoutMS is refreshed for getMore if maxAwaitTimeMS is set" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 150 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + timeoutMS: 250 + batchSize: 1 + maxAwaitTimeMS: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: 1 + + # The timeoutMS value should be refreshed for getMore's. This is a failure test. The find inherits timeoutMS=200 from + # the collection and the getMore blocks for 250ms, causing iteration to fail with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: true + maxTimeMS: { $$exists: true } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + + - description: "apply remaining timeoutMS if less than maxAwaitTimeMS" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 30 + - name: createFindCursor + object: *collection + arguments: + filter: { _id: 1 } + cursorType: tailableAwait + batchSize: 1 + maxAwaitTimeMS: 100 + timeoutMS: 200 + saveResultAsEntity: &tailableCursor tailableCursor + - name: iterateOnce + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + ignoreExtraEvents: true + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + maxTimeMS: { $$lte: 100 } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + maxTimeMS: { $$lte: 70 } + + - description: "apply maxAwaitTimeMS if less than remaining timeout" + operations: + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailableAwait + batchSize: 1 + maxAwaitTimeMS: 100 + timeoutMS: 200 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate twice to force a getMore. + - name: iterateOnce + object: *tailableCursor + - name: iterateOnce + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + maxTimeMS: { $$lte: 100 } + diff --git a/source/client-side-operations-timeout/tests/tailable-non-awaitData.json b/source/client-side-operations-timeout/tests/tailable-non-awaitData.json new file mode 100644 index 0000000000..e88230e4f7 --- /dev/null +++ b/source/client-side-operations-timeout/tests/tailable-non-awaitData.json @@ -0,0 +1,312 @@ +{ + "description": "timeoutMS behaves correctly for tailable non-awaitData cursors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "timeoutMS": 200 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "test", + "createOptions": { + "capped": true, + "size": 500 + }, + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "error if timeoutMode is cursor_lifetime", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "timeoutMode": "cursorLifetime", + "cursorType": "tailable" + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "timeoutMS applied to find", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailable" + }, + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore - success", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailable", + "timeoutMS": 200, + "batchSize": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "timeoutMS is refreshed for getMore - failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getMore" + ], + "blockConnection": true, + "blockTimeMS": 250 + } + } + } + }, + { + "name": "createFindCursor", + "object": "collection", + "arguments": { + "filter": {}, + "cursorType": "tailable", + "batchSize": 1 + }, + "saveResultAsEntity": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "tailableCursor", + "expectError": { + "isTimeoutError": true + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "find", + "databaseName": "test", + "command": { + "find": "coll", + "tailable": true, + "awaitData": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "getMore", + "databaseName": "test", + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll", + "maxTimeMS": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/tailable-non-awaitData.yml b/source/client-side-operations-timeout/tests/tailable-non-awaitData.yml new file mode 100644 index 0000000000..eb75deaa65 --- /dev/null +++ b/source/client-side-operations-timeout/tests/tailable-non-awaitData.yml @@ -0,0 +1,181 @@ +description: "timeoutMS behaves correctly for tailable non-awaitData cursors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + timeoutMS: 200 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + createOptions: + capped: true + size: 500 + documents: + - { _id: 0 } + - { _id: 1 } + +tests: + - description: "error if timeoutMode is cursor_lifetime" + operations: + - name: find + object: *collection + arguments: + filter: {} + timeoutMode: cursorLifetime + cursorType: tailable + expectError: + isClientError: true + + - description: "timeoutMS applied to find" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["find"] + blockConnection: true + blockTimeMS: 250 + - name: find + object: *collection + arguments: + filter: {} + cursorType: tailable + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + # Due to SERVER-51153, the find command should not contain a maxTimeMS field for tailable non-awaitData + # cursors because that would cap the lifetime of the created cursor. + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + + # The timeoutMS option should apply separately to the initial "find" and each getMore. This is a success test. The + # find is executed with timeoutMS=20 and both find and getMore commands are configured to block for 15ms each. Neither + # exceeds the timeout so the operation succeeds. + - description: "timeoutMS is refreshed for getMore - success" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: ["find", "getMore"] + blockConnection: true + blockTimeMS: 150 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailable + timeoutMS: 200 + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate the cursor twice: the first iteration will return the document from the batch in the find and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } + + # The timeoutMS option should apply separately to the initial "find" and each getMore. This is a failure test. The + # find inherits timeoutMS=200 from the collection and the getMore command blocks for 250ms, causing iteration to fail + # with a timeout error. + - description: "timeoutMS is refreshed for getMore - failure" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getMore"] + blockConnection: true + blockTimeMS: 250 + - name: createFindCursor + object: *collection + arguments: + filter: {} + cursorType: tailable + batchSize: 1 + saveResultAsEntity: &tailableCursor tailableCursor + # Iterate the cursor twice: the first iteration will return the document from the batch in the find and the + # second will do a getMore. + - name: iterateUntilDocumentOrError + object: *tailableCursor + - name: iterateUntilDocumentOrError + object: *tailableCursor + expectError: + isTimeoutError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: find + databaseName: *databaseName + command: + find: *collectionName + tailable: true + awaitData: { $$exists: false } + maxTimeMS: { $$exists: false } + - commandStartedEvent: + commandName: getMore + databaseName: *databaseName + command: + getMore: { $$type: ["int", "long"] } + collection: *collectionName + maxTimeMS: { $$exists: false } diff --git a/source/client-side-operations-timeout/tests/waitQueueTimeout.json b/source/client-side-operations-timeout/tests/waitQueueTimeout.json new file mode 100644 index 0000000000..138d5cc161 --- /dev/null +++ b/source/client-side-operations-timeout/tests/waitQueueTimeout.json @@ -0,0 +1,176 @@ +{ + "description": "WaitQueueTimeoutError does not clear the pool", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "client", + "uriOptions": { + "maxPoolSize": 1, + "appname": "waitQueueTimeoutErrorTest" + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + } + ], + "tests": [ + { + "description": "WaitQueueTimeoutError does not clear the pool", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "blockConnection": true, + "blockTimeMS": 500, + "appName": "waitQueueTimeoutErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "thread": { + "id": "thread0" + } + } + ] + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread0", + "operation": { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "commandStartedEvent": { + "commandName": "ping" + } + }, + "count": 1 + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "timeoutMS": 100, + "command": { + "hello": 1 + }, + "commandName": "hello" + }, + "expectError": { + "isTimeoutError": true + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread0" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "hello": 1 + }, + "commandName": "hello" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping", + "databaseName": "test", + "command": { + "ping": 1 + } + } + }, + { + "commandStartedEvent": { + "commandName": "hello", + "databaseName": "test", + "command": { + "hello": 1 + } + } + } + ] + }, + { + "client": "client", + "eventType": "cmap", + "events": [] + } + ] + } + ] +} diff --git a/source/client-side-operations-timeout/tests/waitQueueTimeout.yml b/source/client-side-operations-timeout/tests/waitQueueTimeout.yml new file mode 100644 index 0000000000..dca58df7a3 --- /dev/null +++ b/source/client-side-operations-timeout/tests/waitQueueTimeout.yml @@ -0,0 +1,105 @@ +description: "WaitQueueTimeoutError does not clear the pool" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["single", "replicaset", "sharded"] + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &client client + uriOptions: + maxPoolSize: 1 + appname: &appname waitQueueTimeoutErrorTest + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - poolClearedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName test + +tests: + - description: "WaitQueueTimeoutError does not clear the pool" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["ping"] + blockConnection: true + blockTimeMS: 500 + appName: *appname + # Start thread. + - name: createEntities + object: testRunner + arguments: + entities: + - thread: + id: &thread0 thread0 + - name: runOnThread + object: testRunner + arguments: + thread: *thread0 + operation: + name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: ping + # Wait for the thread to checkout the only connection (maxPoolSize=1). + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + commandStartedEvent: + commandName: ping + count: 1 + # Run another command with a short timeout to make it likely to get a WaitQueueTimeoutError. + - name: runCommand + object: *database + arguments: + timeoutMS: 100 + command: { hello: 1 } + commandName: hello + expectError: + isTimeoutError: true + - name: waitForThread + object: testRunner + arguments: + thread: *thread0 + # Run another command with no timeout to ensure the pool is not cleared. + - name: runCommand + object: *database + arguments: + command: { hello: 1 } + commandName: hello + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + commandName: ping + databaseName: *databaseName + command: + ping: 1 + - commandStartedEvent: + commandName: hello + databaseName: *databaseName + command: + hello: 1 + # No poolClearedEvent. + - client: *client + eventType: cmap + events: [] diff --git a/source/collation/collation.md b/source/collation/collation.md new file mode 100644 index 0000000000..b3fade46ac --- /dev/null +++ b/source/collation/collation.md @@ -0,0 +1,232 @@ +# Collation + +- Status: Accepted +- Minimum Server Version: 3.4 + +______________________________________________________________________ + +## Abstract + +As of MongoDB server version 3.4 (maxWireVersion 5), a collation option is supported by the query system for matching +and sorting on language strings in a locale-aware fashion. + +A driver MUST support a Collation option for each of the relevant operations in server versions >= 3.4 (maxWireVersion +5\) and MUST throw an error if a user supplies a Collation option for the operation and the selected server has +maxWireVersion < 5 or if the user is using opcode-based unacknowledged writes. + +The CRUD and Index Management specs include the collation option in descriptions of API elements where it is supported. +This document provides more details on the specific driver behavior required to handle the collation option. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Design Rationale + +### Collation is not a setting on a driver Client, Collection, or Database object + +MongoDB supports a server-side collation default on a collection. It is problematic to add a default Collation option on +a driver’s Collection or Database object, because automatic collection and index creation would bypass any driver +object's Collation setting. Additionally, this would have a number of performance implications in the context of bulk +writes: any bulk write on a Collection object having a default Collation would require iterating through each operation +and adding a Collation document. + +After the initial release of server version 3.4, many users will want to apply Collations to all operations on an +existing collection. Such users will have to supply the Collation option to each operation explicitly; however, +eventually the majority of users wishing to use Collations on all operations on a collection will create a collection +with a server-side default. We chose to favor user verbosity right now over abstracting the feature for short-term +gains. + +### Drivers throw an error if a user supplies Collation and the selected server does not support the feature + +Server versions earlier than 3.4 don't always throw an error if an unknown option is supplied to certain operations. +Because a Collation defines how documents are matched and sorted for both read and write operations, behavior +differences between server versions are significant. Drivers therefore MUST throw an error if a user specifies a +Collation and the selected server has a maxWireVersion < 5 or if using opcode-based unacknowledged writes. + +## Specification + +### Collation Document Model + +For naming and deviation guidance, see the [CRUD specification](../crud/crud.md#naming). Defined below are the +constructs for drivers. Note that the only required field is `locale`, but the driver should let the server validate the +presence of this field. + +```typescript +class Collation { + /** + * The locale. + */ + locale: String + + /** + * The case level. + */ + caseLevel: Optional + + /** + * The case ordering. + */ + caseFirst: Optional + + /** + * The number of comparison levels to use. + */ + strength: Optional + + /** + * Whether to order numbers based on numerical order and not collation order. + */ + numericOrdering: Optional + + /** + * Whether spaces and punctuation are considered base characters. + */ + alternate: Optional + + /** + * Which characters are affected by alternate: "shifted". + */ + maxVariable: Optional + + /** + * If true, normalizes text into Unicode NFD. + */ + normalization: Optional + + /** + * Causes secondary differences to be considered in reverse order, as it is done in the French language. + */ + backwards: Optional +} +``` + +#### Unknown Options + +For forward compatibility, a driver MUST NOT raise an error when a user provides unknown options or values. The driver +MUST NOT validate collation document types; the driver relies on the server to validate values and other contents of the +collation document. + +#### Generic Command Method + +If a driver offers a generic `RunCommand` method on the `database` object, the driver MUST NOT validate whether the +provided command document contains a "collation" subdocument, and MUST NOT check the server's wire version before +sending the command including the "collation" subdocument. + +### API + +The driver helpers that must support a collation option include the create collection helper, any CRUD API components +relying on the MongoDB query system (including updates and deletes) and some index management helpers. The CRUD-related +commands that take a collation option are: + +- aggregate +- count +- distinct +- find (command only) +- findAndModify +- geoNear +- group +- mapReduce +- delete (command only) +- update (command only) + +The collation option is sent to the server in the form of a BSON Document. See the +[CRUD specification](../crud/crud.md#naming) for details on supporting the option in the CRUD API. + +Driver helpers manipulating or using indexes MUST support a collation option. These include creating, deleting, and +hinting an index. See the [Index Management specification](../index-management/index-management.md) for details. + +### Require maxWireVersion 5 + +Drivers MUST require the server's maxWireVersion >= 5 to support Collations. When a collation is explicitly specified +for a server with maxWireVersion < 5, the driver MUST raise an error. + +### Opcode-based Unacknowledged Writes + +The driver MUST NOT allow collation with opcodes, because the server doesn't support it. If a driver uses opcode-based +writes when the write concern is unacknowledged, the driver MUST raise an error if a collation is explicitly set. + +### Setting a default collation on a collection + +Drivers MUST allow the create command to accept a parameter called "collation". For example, + +```typescript +db.command({ + create: "myCollection", + collation: {locale: "en_US"} +}); +``` + +### BulkWrite API + +If maxWireVersion < 5, the driver MUST inspect each BulkWrite operation model for a collation and MUST raise an error +and MUST NOT send any operations to the server if a collation is explicitly specified on an operation. For example, the +user will provide BulkWrite operation models as in the following example: + +```typescript +db.collection.bulkWrite([ + {insertOne: { ... }}, + + {updateOne: { filter: { name: "PING" }, + update: { $set: { name: "pong" }}, + collation: { locale: "en_US", strength: 2 }}}, + {updateMany: {..., collation: {...}}}, + {replaceOne: {..., collation: {...}}}, + {deleteOne: {..., collation: {...}}}, + {deleteMany: {..., collation: {...}}} +]); +``` + +The driver MUST inspect all operations before sending any to the server. If any operation specifies a Collation, the +driver MUST raise an error and MUST NOT send any operations to the server. In the example above, even the insertOne +(which has no Collation) MUST NOT be sent. + +## Test Plan + +There is no specific test plan for driver Collation support; however drivers should test each affected CRUD, Index +Management API, and collection creation/modification component to ensure that Collation is a supported option. + +In addition, drivers should test that two indexes can be created with identical key patterns and different collations. A +custom name must be provided for one of them. Then, the test should ensure that the correct index is dropped when +delete_one is called with an index name. + +Drivers should also test that errors are raised in each place Collation can be provided to a API method and the selected +server has maxWireVersion < 5. + +## Backwards Compatibility + +There should be no backwards compatibility concerns. + +## Reference Implementation + +Reference Implementation: + +- [RUBY-1126](https://jira.mongodb.org/browse/RUBY-1126) +- [JAVA-2241](https://jira.mongodb.org/browse/JAVA-2241) + +## Q & A + +Q: Insert doesn’t take a collation? + +A: No, only queries take collation. A collation is a per operation value, it does not affect how the data is stored. + +Q: Delete and Update take a collation? + +A: Yes, delete and update operations use the query system to match against a provided delete/update filter. Providing a +collation when deleting a document matching `ObjectID()` doesn’t change anything, but matching a string value would. + +Q: How do I create a collection with default collation? Does it affect my existing collection creation helper? + +A: A collection with a default collation can be created using the create helper and by providing a collation option. + +## Changelog + +- 2026-05-20: Clarify BulkWrite pre-flight check: use RFC 2119 MUST and make explicit that all operations are inspected + before any are sent. +- 2026-05-13: Fix "Minimum Server Version" header from 1.8 to 3.4, matching the maxWireVersion 5 requirement stated in + the spec body. +- 2024-02-15: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter and reformat changelog. +- 2017-05-15: Minor markup fixes in API section. +- 2016-08-31: Initial version. diff --git a/source/collation/collation.rst b/source/collation/collation.rst index 677bac67b2..f37d5ef3dd 100644 --- a/source/collation/collation.rst +++ b/source/collation/collation.rst @@ -1,234 +1,6 @@ .. role:: javascript(code) :language: javascript -========= -Collation -========= - -:Spec: 79 -:Title: Collation -:Authors: Emily Stolfo -:Advisory Group: Derick Rethans, Craig Wilson -:Status: Approved -:Type: Standards -:Minimum Server Version: 1.8 -:Last Modified: May 15, 2017 -:Version: 1.0 - -.. contents:: - --------- - -Abstract -======== - -As of MongoDB server version 3.4 (maxWireVersion 5), a collation option is supported by the query system for matching and sorting on language strings in a locale-aware fashion. - -A driver MUST support a Collation option for each of the relevant operations in server versions >= 3.4 (maxWireVersion 5) and MUST throw an error if a user supplies a Collation option for the operation and the selected server has maxWireVersion < 5 or if the user is using opcode-based unacknowledged writes. - -The CRUD and Index Management specs include the collation option in descriptions of API elements where it is supported. This document provides more details on the specific driver behavior required to handle the collation option. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Design Rationale -================ - ------------------------------------------------------------------------------ -Collation is not a setting on a driver Client, Collection, or Database object ------------------------------------------------------------------------------ - -MongoDB supports a server-side collation default on a collection. It is problematic to add a default Collation option on a driver’s Collection or Database object, because automatic collection and index creation would bypass any driver object's Collation setting. Additionally, this would have a number of performance implications in the context of bulk writes: any bulk write on a Collection object having a default Collation would require iterating through each operation and adding a Collation document. - -After the initial release of server version 3.4, many users will want to apply Collations to all operations on an existing collection. Such users will have to supply the Collation option to each operation explicitly; however, eventually the majority of users wishing to use Collations on all operations on a collection will create a collection with a server-side default. We chose to favor user verbosity right now over abstracting the feature for short-term gains. - --------------------------------------------------------------------------------------------------------- -Drivers throw an error if a user supplies Collation and the selected server does not support the feature --------------------------------------------------------------------------------------------------------- - -Server versions earlier than 3.4 don't always throw an error if an unknown option is supplied to certain operations. Because a Collation defines how documents are matched and sorted for both read and write operations, behavior differences between server versions are significant. Drivers therefore MUST throw an error if a user specifies a Collation and the selected server has a maxWireVersion < 5 or if using opcode-based unacknowledged writes. - -Specification -============= - ------------------------- -Collation Document Model ------------------------- - -For naming and deviation guidance, see the `CRUD specification `_. Defined below are the constructs for drivers. Note that the only required field is ``locale``, but the driver should let the server validate the presence of this field. - -.. code:: typescript - - class Collation { - /** - * The locale. - */ - locale: String - - /** - * The case level. - */ - caseLevel: Optional - - /** - * The case ordering. - */ - caseFirst: Optional - - /** - * The number of comparison levels to use. - */ - strength: Optional - - /** - * Whether to order numbers based on numerical order and not collation order. - */ - numericOrdering: Optional - - /** - * Whether spaces and punctuation are considered base characters. - */ - alternate: Optional - - /** - * Which characters are affected by alternate: “shifted”. - */ - maxVariable: Optional - - /** - * If true, normalizes text into Unicode NFD. - */ - normalization: Optional - - /** - * Causes secondary differences to be considered in reverse order, as it is done in the French language. - */ - backwards: Optional - } - -Unknown Options -------------------------------------------------------------------- - -For forward compatibility, a driver MUST NOT raise an error when a user provides unknown options or values. The driver MUST NOT validate collation document types; the driver relies on the server to validate values and other contents of the collation document. - -Generic Command Method ----------------------- - -If a driver offers a generic ``RunCommand`` method on the ``database`` object, the driver MUST NOT validate whether the provided command document contains a "collation" subdocument, and MUST NOT check the server's wire version before sending the command including the "collation" subdocument. - ---- -API ---- -The driver helpers that must support a collation option include the create collection helper, any CRUD API components relying on the MongoDB query system (including updates and deletes) and some index management helpers. The CRUD-related commands that take a collation option are: - -- aggregate -- count -- distinct -- find (command only) -- findAndModify -- geoNear -- group -- mapReduce -- delete (command only) -- update (command only) - -The collation option is sent to the server in the form of a BSON Document. See the `CRUD specification `_ for details on supporting the option in the CRUD API. - -Driver helpers manipulating or using indexes MUST support a collation option. These include creating, deleting, and hinting an index. See the `Index Management specification `_ for details. - ------------------------- -Require maxWireVersion 5 ------------------------- - -Drivers MUST require the server's maxWireVersion >= 5 to support Collations. When a collation is explicitly specified for a server with maxWireVersion < 5, the driver MUST raise an error. - ----------------------------------- -Opcode-based Unacknowledged Writes ----------------------------------- - -The driver MUST NOT allow collation with opcodes, because the server doesn't support it. If a driver uses opcode-based writes when the write concern is unacknowledged, the driver MUST raise an error if a collation is explicitly set. - -------------------------------------------- -Setting a default collation on a collection -------------------------------------------- - -Drivers MUST allow the create command to accept a parameter called “collation”. For example, - -.. code:: typescript - - db.command({ - create: “myCollection”, - collation: {locale: “en_US”} - }); - -------------- -BulkWrite API -------------- - -If maxWireVersion < 5, the driver MUST inspect each BulkWrite operation model for a collation and MUST raise an error and MUST NOT send any operations to the server if a collation is explicitly specified on an operation. For example, the user will provide BulkWrite operation models as in the following example: - -.. code:: typescript - - db.collection.bulkWrite([ - {insertOne: { … }}, - - {updateOne: { filter: { name: "PING" }, - update: { $set: { name: "pong" }}, - collation: { locale: "en_US", strength: 2 }}}, - {updateMany: {..., collation: {...}}}, - {replaceOne: {..., collation: {...}}}, - {deleteOne: {..., collation: {...}}}, - {deleteMany: {..., collation: {...}}} - ]); - -The driver must inspect each operation for a Collation if maxWireVersion is < 5 and fail the entire bulkWrite if a collation was explicitly specified. In the example above, that means even the insertOne (without Collation) MUST NOT be sent. - - -Test Plan -========= - -There is no specific test plan for driver Collation support; however drivers should test each affected CRUD, Index Management API, and collection creation/modification component to ensure that Collation is a supported option. - -https://github.com/mongodb/specifications/blob/master/source/index-management.rst -https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst - -In addition, drivers should test that two indexes can be created with identical key patterns and different collations. A custom name must be provided for one of them. Then, the test should ensure that the correct index is dropped when delete_one is called with an index name. - -Drivers should also test that errors are raised in each place Collation can be provided to a API method and the selected server has maxWireVersion < 5. - - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. - - -Reference Implementation -======================== - -Reference Implementation: - `RUBY-1126 `_ - `JAVA-2241 `_ - -Q & A -===== - -Q: Insert doesn’t take a collation? - No, only queries take collation. A collation is a per operation value, it does not affect how the data is stored. - -Q: Delete and Update take a collation? - Yes, delete and update operations use the query system to match against a provided delete/update filter. Providing a collation when deleting a document matching ObjectID() doesn’t change anything, but matching a string value would. - -Q: How do I create a collection with default collation? Does it affect my existing collection creation helper? - A collection with a default collation can be created using the create helper and by providing a collation option. - - -Version History -=============== - -- 2017-05-15: minor markup fixes in API section. -- 2016-08-31: version 1.0. +.. note:: + This specification has been converted to Markdown and renamed to + `collation.md `_. diff --git a/source/collection-management/tests/README.md b/source/collection-management/tests/README.md new file mode 100644 index 0000000000..40eb499eb7 --- /dev/null +++ b/source/collection-management/tests/README.md @@ -0,0 +1,4 @@ +# Collection Management Tests + +This directory contains tests for collection management. They are implemented in the +[Unified Test Format](../../unified-test-format/unified-test-format.md). diff --git a/source/collection-management/tests/clustered-indexes.json b/source/collection-management/tests/clustered-indexes.json new file mode 100644 index 0000000000..9db5ff06d7 --- /dev/null +++ b/source/collection-management/tests/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/source/collection-management/tests/clustered-indexes.yml b/source/collection-management/tests/clustered-indexes.yml new file mode 100644 index 0000000000..e485735f8c --- /dev/null +++ b/source/collection-management/tests/clustered-indexes.yml @@ -0,0 +1,135 @@ +description: "clustered-indexes" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "5.3" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name ci-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: "createCollection with clusteredIndex" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: &clusteredIndex + key: { _id: 1 } + unique: true + name: &index0Name "test index" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + + - description: "listCollections includes clusteredIndex" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: *clusteredIndex + - name: listCollections + object: *database0 + arguments: + filter: &filter { name: { $eq: *collection0Name } } + expectResult: + - name: *collection0Name + options: + clusteredIndex: + key: { _id: 1 } + unique: true + name: *index0Name + v: { $$type: [ int, long ] } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + - commandStartedEvent: + command: + listCollections: 1 + filter: *filter + databaseName: *database0Name + + - description: "listIndexes returns the index" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + clusteredIndex: *clusteredIndex + - name: listIndexes + object: *collection0 + expectResult: + - key: { _id: 1 } + name: *index0Name + clustered: true + unique: true + v: { $$type: [ int, long ] } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + clusteredIndex: *clusteredIndex + databaseName: *database0Name + - commandStartedEvent: + command: + listIndexes: *collection0Name + databaseName: *database0Name diff --git a/source/collection-management/tests/createCollection-pre_and_post_images.json b/source/collection-management/tests/createCollection-pre_and_post_images.json new file mode 100644 index 0000000000..f488deacd8 --- /dev/null +++ b/source/collection-management/tests/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/source/collection-management/tests/createCollection-pre_and_post_images.yml b/source/collection-management/tests/createCollection-pre_and_post_images.yml new file mode 100644 index 0000000000..100e9a1d7c --- /dev/null +++ b/source/collection-management/tests/createCollection-pre_and_post_images.yml @@ -0,0 +1,50 @@ +description: "createCollection-pre_and_post_images" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "6.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name papi-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + - description: "createCollection with changeStreamPreAndPostImages enabled" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + changeStreamPreAndPostImages: { enabled: true } + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + changeStreamPreAndPostImages: { enabled: true } + databaseName: *database0Name diff --git a/source/collection-management/tests/listCollections-rawdata.json b/source/collection-management/tests/listCollections-rawdata.json new file mode 100644 index 0000000000..ec09ec31db --- /dev/null +++ b/source/collection-management/tests/listCollections-rawdata.json @@ -0,0 +1,97 @@ +{ + "description": "listCollections-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + } + ], + "tests": [ + { + "description": "listCollections with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "auth": false + } + ], + "operations": [ + { + "name": "listCollections", + "object": "database0", + "arguments": { + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "$$unsetOrMatches": {} + }, + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "listCollections with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99", + "auth": false + } + ], + "operations": [ + { + "name": "listCollections", + "object": "database0", + "arguments": { + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1, + "filter": { + "$$unsetOrMatches": {} + }, + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/collection-management/tests/listCollections-rawdata.yml b/source/collection-management/tests/listCollections-rawdata.yml new file mode 100644 index 0000000000..0a9f24cfac --- /dev/null +++ b/source/collection-management/tests/listCollections-rawdata.yml @@ -0,0 +1,49 @@ +description: "listCollections-rawData" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + +tests: + - description: "listCollections with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + auth: false + operations: + - name: listCollections + object: *database0 + arguments: + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: { $$unsetOrMatches: {} } + rawData: *rawdata + - description: "listCollections with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + auth: false + operations: + - name: listCollections + object: *database0 + arguments: + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + listCollections: 1 + filter: { $$unsetOrMatches: {} } + rawData: + $$exists: false diff --git a/source/collection-management/tests/modifyCollection-errorResponse.json b/source/collection-management/tests/modifyCollection-errorResponse.json new file mode 100644 index 0000000000..aea71eb08f --- /dev/null +++ b/source/collection-management/tests/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/source/collection-management/tests/modifyCollection-errorResponse.yml b/source/collection-management/tests/modifyCollection-errorResponse.yml new file mode 100644 index 0000000000..e61a01211c --- /dev/null +++ b/source/collection-management/tests/modifyCollection-errorResponse.yml @@ -0,0 +1,59 @@ +description: "modifyCollection-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name collMod-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 1 } + - { _id: 2, x: 1 } + +tests: + - description: "modifyCollection prepareUnique violations are accessible" + runOnRequirements: + - minServerVersion: "5.2" # SERVER-61158 + operations: + - name: createIndex + object: *collection0 + arguments: + keys: { x: 1 } + - name: modifyCollection + object: *database0 + arguments: + collection: *collection0Name + index: + keyPattern: { x: 1 } + prepareUnique: true + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3, x: 1 } + expectError: + errorCode: 11000 # DuplicateKey + - name: modifyCollection + object: *database0 + arguments: + collection: *collection0Name + index: + keyPattern: { x: 1 } + unique: true + expectError: + isClientError: false + errorCode: 359 # CannotConvertIndexToUnique + errorResponse: + violations: + - { ids: [ 1, 2 ] } diff --git a/source/collection-management/tests/modifyCollection-pre_and_post_images.json b/source/collection-management/tests/modifyCollection-pre_and_post_images.json new file mode 100644 index 0000000000..8026faeb17 --- /dev/null +++ b/source/collection-management/tests/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/source/collection-management/tests/modifyCollection-pre_and_post_images.yml b/source/collection-management/tests/modifyCollection-pre_and_post_images.yml new file mode 100644 index 0000000000..75675bf2c7 --- /dev/null +++ b/source/collection-management/tests/modifyCollection-pre_and_post_images.yml @@ -0,0 +1,58 @@ +description: "modifyCollection-pre_and_post_images" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "6.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name papi-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + - description: "modifyCollection to changeStreamPreAndPostImages enabled" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + changeStreamPreAndPostImages: { enabled: false } + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + - name: modifyCollection + object: *database0 + arguments: + collection: *collection0Name + changeStreamPreAndPostImages: { enabled: true } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + changeStreamPreAndPostImages: { enabled: false } + - commandStartedEvent: + command: + collMod: *collection0Name + changeStreamPreAndPostImages: { enabled: true } diff --git a/source/collection-management/tests/timeseries-collection.json b/source/collection-management/tests/timeseries-collection.json new file mode 100644 index 0000000000..2ee52eac41 --- /dev/null +++ b/source/collection-management/tests/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": "6.3" + } + ], + "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/source/collection-management/tests/timeseries-collection.yml b/source/collection-management/tests/timeseries-collection.yml new file mode 100644 index 0000000000..f0609ca203 --- /dev/null +++ b/source/collection-management/tests/timeseries-collection.yml @@ -0,0 +1,164 @@ +description: "timeseries-collection" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name ts-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: "createCollection with all options" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + # expireAfterSeconds should be an int64 (as it is stored on the server). + expireAfterSeconds: 604800 + timeseries: ×eries0 + timeField: "time" + metaField: "meta" + granularity: "minutes" + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + expireAfterSeconds: 604800 + timeseries: *timeseries0 + databaseName: *database0Name + + # Unlike regular collections, time-series collections allow duplicate ids. + - description: "insertMany with duplicate ids" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + # expireAfterSeconds should be an int64 (as it is stored on the server). + expireAfterSeconds: 604800 + timeseries: *timeseries0 + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + - name: insertMany + object: *collection0 + arguments: + documents: &docs + - { + _id: 1, + time: { + $date: { + $numberLong: "1552949630482" + } + } + } + - { + _id: 1, + time: { + $date: { + $numberLong: "1552949630483" + } + } + } + - name: find + object: *collection0 + arguments: + filter: {} + sort: { time: 1 } + expectResult: *docs + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + expireAfterSeconds: 604800 + timeseries: *timeseries0 + databaseName: *database0Name + - commandStartedEvent: + command: + insert: *collection0Name + documents: *docs + - commandStartedEvent: + command: + find: *collection0Name + filter: {} + sort: { time: 1 } + databaseName: *database0Name + + - description: "createCollection with bucketing options" + runOnRequirements: + - minServerVersion: "6.3" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: createCollection + object: *database0 + arguments: + collection: *collection0Name + timeseries: ×eries1 + timeField: "time" + bucketMaxSpanSeconds: 3600 + bucketRoundingSeconds: 3600 + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + timeseries: *timeseries1 + databaseName: *database0Name + diff --git a/source/command-logging-and-monitoring/command-logging-and-monitoring.md b/source/command-logging-and-monitoring/command-logging-and-monitoring.md new file mode 100644 index 0000000000..98c97be5ce --- /dev/null +++ b/source/command-logging-and-monitoring/command-logging-and-monitoring.md @@ -0,0 +1,500 @@ +# Command Logging and Monitoring + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Specification + +The command logging and monitoring specification defines a set of behaviour in the drivers for providing runtime +information about commands in log messages as well as in events which users can consume programmatically, either +directly or by integrating with third-party APM libraries. + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +#### Terms + +**Document** + +The term `Document` refers to the implementation in the driver's language of a BSON document. + +### Guidance + +#### Documentation + +The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation +for the driver. + +#### Messages and Events + +All drivers MUST implement the specified event types as well as log messages. + +Implementation details are noted in the comments when a specific implementation is required. Within each event and log +message, all properties are REQUIRED unless noted otherwise. + +#### Naming + +All drivers MUST name types, properties, and log message values as defined in the following sections. Exceptions to this +rule are noted in the appropriate section. Class and interface names may vary according to the driver and language best +practices. + +#### Publishing & Subscribing + +The driver SHOULD publish events in a manner that is standard to the driver's language publish/subscribe patterns and is +not strictly mandated in this specification. + +Similarly, as described in the [logging specification](../logging/logging.md#implementation-requirements) the driver +SHOULD emit log messages in a manner that is standard for the language. + +#### Guarantees + +The driver MUST guarantee that every `CommandStartedEvent` has either a correlating `CommandSucceededEvent` or +`CommandFailedEvent`, and that every "command started" log message has either a correlating "command succeeded" log +message or "command failed" log message. + +The driver MUST guarantee that the `requestId` of the `CommandStartedEvent` and the corresponding +`CommandSucceededEvent` or `CommandFailedEvent` is the same, and that the `requestId` of the "command started" log +message and the corresponding "command succeeded" or "command failed" log message is the same. + +#### Unacknowledged/Acknowledged Writes + +Unacknowledged writes must provide a `CommandSucceededEvent` and a "command succeeded" log message with a `{ ok: 1 }` +reply. + +A non-default write concern MUST be included in the published command. The default write concern is not required to be +included. + +#### Succeeded or Failed + +Commands that executed on the server and return a status of `{ ok: 1.0 }` are considered successful commands and MUST +generate a `CommandSucceededEvent` and "command succeeded" log message. Commands that have write errors are included +since the actual command did succeed, only writes failed. + +#### Error Handling + +If an exception occurs while sending the operation to the server, the driver MUST generate a `CommandFailedEvent` and +"command failed" log message with the exception or message, and re-raise the exception. + +#### Bulk Writes + +This specification defines the monitoring and logging of individual commands and in that respect MUST generate events +and log messages for each command a bulk write executes. Each of these commands, however, must be linked together via +the same `operationId`. + +#### Implementation Notes + +When a driver sends an OP_MSG with a document sequence, it MUST include the document sequence as a BSON array in +`CommandStartedEvent.command`. The array's field name MUST be the OP_MSG sequence identifier. For example, if the driver +sends an `update` command using OP_MSG, and sends a document sequence as a separate section of payload type 1 with +identifier `updates`, the driver MUST include the documents as a BSON array in `CommandStartedEvent.command` with field +name `updates`. + +See "Why are document sequences included as BSON arrays?" in the [rationale](#rationale). + +### Rationale + +*1. Why are commands with* `{ ok: 1 }` *treated as successful and* `{ ok: 0 }` *as failed?* + +The specification is consistent with what the server deems as a successful or failed command and reports this as so. +This also allows for server changes around this behaviour in the future to require no change in the drivers to continue +to be compliant. + +The command listener API is responsible only for receiving and handling events sent from the lowest level of the driver, +and is only about informing listeners about what commands are sent and what replies are received. As such, it would be +innappropiate at this level for a driver to execute custom logic around particular commands to determine what failure or +success means for a particular command. Implementers of the API are free to handle these events as they see fit, which +may include code that further interprets replies to specific commands based on the presence or absence of other fields +in the reply beyond the `ok` field. + +*2. Why are document sequences included as BSON arrays?* + +The OP_MSG wire protocol was introduced in MongoDB 3.6, with document sequences as an optimization for bulk writes. We +have chosen to represent these OP_MSGs as single command documents for now, until a need for a more accurate (and +perhaps better-performing) command monitoring API for document sequences has been demonstrated. + +*3. Why is BSON serialization and deserialization optional to include in durations?* + +Different drivers will serialize and deserialize BSON at different levels of the driver architecture. For example, some +parts of a command (e.g. inserted document structs) could be pre-encoded early into a "raw" BSON form and the final +command with late additions like a session ID could encoded just before putting it on the wire. + +Rather than specify a duration rule that would be hard to satisfy consistently, we allow duration to include BSON +serialization/deserialization or not based on the architecture needs of each driver. + +### Security + +Some commands and replies will contain sensitive data relating to authentication. + +In order to not risk leaking this data to external sources or logs, for these commands: + +- The "command" field in `CommandStartedEvent` and "command started" log messages MUST be replaced with an empty BSON + document. + +- The "reply" field in `CommandSucceededEvent` and "command succeeded" log messages MUST be replaced with an empty BSON + document. + +- If the error is a server-side error, the "failure" field in `CommandFailedEvent` and "command failed" log messages + MUST have all fields besides the following redacted: + + - `code` + - `codeName` + - `errorLabels` + + The exact implementation of redaction is flexible depending on the type the driver uses to represent a failure in + these events and log messages. For example, a driver could choose to set all properties besides these on an error + object to null. Alternatively, a driver that uses strings to represent failures could replace relevant portions of + the string with "REDACTED". + +The list of sensitive commands is as follows: + +| Command | +| ------------------------------------------------------------------- | +| `authenticate` | +| `saslStart` | +| `saslContinue` | +| `getnonce` | +| `createUser` | +| `updateUser` | +| `copydbgetnonce` | +| `copydbsaslstart` | +| `copydb` | +| `hello` (or legacy hello) when `speculativeAuthenticate` is present | + +See the [MongoDB Handshake spec](../mongodb-handshake/handshake.md) for more information on `hello` and legacy hello. +Note that legacy hello has two different letter casings that must be taken into account. See the previously mentioned +MongoDB Handshake spec for details. + +### Events API + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#events) for details on the `serviceId` field. + +```typescript +interface CommandStartedEvent { + + /** + * Returns the command. + */ + command: Document; + + /** + * Returns the database name. + */ + databaseName: String; + + /** + * Returns the command name. + */ + commandName: String; + + /** + * Returns the driver generated request id. + */ + requestId: Int64; + + /** + * Returns the driver generated operation id. This is used to link events together such + * as bulk write operations. OPTIONAL. + */ + operationId: Int64; + + /** + * Returns the connection id for the command. For languages that do not have this, + * this MUST return the driver equivalent which MUST include the server address and port. + * The name of this field is flexible to match the object that is returned from the driver. + */ + connectionId: ConnectionId; + + /** + * Returns the server connection id for the command. The server connection id is distinct from + * the connection id and is returned by the hello or legacy hello response as "connectionId" + * from the server on 4.2+. Drivers MUST use an Int64 to represent the server connection ID value. + */ + serverConnectionId: Optional; + + /** + * Returns the service id for the command when the driver is in load balancer mode. + * For drivers that wish to include this in their ConnectionId object, this field is + * optional. + */ + serviceId: Optional; +} + +interface CommandSucceededEvent { + + /** + * Returns the execution time of the event in the highest possible resolution for the platform. + * The calculated value MUST be the time to send the message and receive the reply from the server + * and MAY include BSON serialization and/or deserialization. The name can imply the units in which the + * value is returned, i.e. durationMS, durationNanos. + */ + duration: Int64; + + /** + * Returns the command reply. + */ + reply: Document; + + /** + * Returns the command name. + */ + commandName: String; + + /** + * Returns the database name. + */ + databaseName: String; + + /** + * Returns the driver generated request id. + */ + requestId: Int64; + + /** + * Returns the driver generated operation id. This is used to link events together such + * as bulk write operations. OPTIONAL. + */ + operationId: Int64; + + /** + * Returns the connection id for the command. For languages that do not have this, + * this MUST return the driver equivalent which MUST include the server address and port. + * The name of this field is flexible to match the object that is returned from the driver. + */ + connectionId: ConnectionId; + + /** + * Returns the server connection id for the command. The server connection id is distinct from + * the connection id and is returned by the hello or legacy hello response as "connectionId" + * from the server on 4.2+. Drivers MUST use an Int64 to represent the server connection ID value. + */ + serverConnectionId: Optional; + + /** + * Returns the service id for the command when the driver is in load balancer mode. + * For drivers that wish to include this in their ConnectionId object, this field is + * optional. + */ + serviceId: Optional; +} + +interface CommandFailedEvent { + + /** + * Returns the execution time of the event in the highest possible resolution for the platform. + * The calculated value MUST be the time to send the message and receive the reply from the server + * and MAY include BSON serialization and/or deserialization. The name can imply the units in which the + * value is returned, i.e. durationMS, durationNanos. + */ + duration: Int64; + + /** + * Returns the command name. + */ + commandName: String; + + /** + * Returns the database name. + */ + databaseName: String; + + /** + * Returns the failure. Based on the language, this SHOULD be a message string, exception + * object, or error document. + */ + failure: String,Exception,Document; + + /** + * Returns the client generated request id. + */ + requestId: Int64; + + /** + * Returns the driver generated operation id. This is used to link events together such + * as bulk write operations. OPTIONAL. + */ + operationId: Int64; + + /** + * Returns the connection id for the command. For languages that do not have this, + * this MUST return the driver equivalent which MUST include the server address and port. + * The name of this field is flexible to match the object that is returned from the driver. + */ + connectionId: ConnectionId; + + /** + * Returns the server connection id for the command. The server connection id is distinct from + * the connection id and is returned by the hello or legacy hello response as "connectionId" + * from the server on 4.2+. Drivers MUST use an Int64 to represent the server connection ID value. + */ + serverConnectionId: Optional; + + /** + * Returns the service id for the command when the driver is in load balancer mode. + * For drivers that wish to include this in their ConnectionId object, this field is + * optional. + */ + serviceId: Optional; +} +``` + +### Log Messages + +Please refer to the [logging specification](../logging/logging.md) for details on logging implementations in general, +including log levels, log components, and structured versus unstructured logging. + +Drivers MUST support logging of command information via the following types of log messages. These messages MUST be +logged at `Debug` level and use the `command` log component. + +The log messages are intended to match the information contained in the events above. Drivers MAY implement command +logging support via an event subscriber if it is convenient to do so. + +The types used in the structured message definitions below are demonstrative, and drivers MAY use similar types instead +so long as the information is present (e.g. a double instead of an integer, or a string instead of an integer if the +structured logging framework does not support numeric types.) + +Drivers MUST not emit command log messages for commands issued as part of the handshake with the server, or heartbeat +commands issued by server monitors. + +#### Common Fields + +The following key-value pairs MUST be included in all command messages: + +| Key | Suggested Type | Value | +| ------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| commandName | String | The command name. | +| databaseName | String | The database name. | +| requestId | Int | The driver-generated request ID. | +| operationId | Int | The driver-generated operation ID. Optional; only present if the driver generated operation IDs and this command has one. | +| driverConnectionId | Int64 | The driver's ID for the connection used for the command. Note this is NOT the same as `CommandStartedEvent.connectionId` defined above, but refers to the `connectionId` defined in the [connection monitoring and pooling specification](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md). Unlike `CommandStartedEvent.connectionId` this field MUST NOT contain the host/port; that information MUST be in the following fields, `serverHost` and `serverPort`. This field is optional for drivers that do not implement CMAP if they do have an equivalent concept of a connection ID. | +| serverHost | String | The hostname or IP address for the server the command is being run on. | +| serverPort | Int | The port for the server the command is being run on. Optional; not present for Unix domain sockets. When the user does not specify a port and the default (27017) is used, the driver SHOULD include it here. | +| serverConnectionId | Int64 | The server's ID for the connection used for the command. Optional; only present for server versions 4.2+. NOTE: Existing drivers may represent this as an Int32 already. For strongly-typed languages, you may have to introduce a new Int64 field and deprecate the old Int32 field. The next major version should remove the old Int32 field. | +| serviceId | String | The hex string representation of the service ID for the command. Optional; only present when the driver is in load balancer mode. | + +#### Command Started Message + +In addition to the common fields, command started messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Command started" | +| command | String | Relaxed extJSON representation of the command. This document MUST be truncated appropriately according to rules defined in the [logging specification](../logging/logging.md#configurable-max-document-length), and MUST be replaced with an empty document "{ }" if the command is considered sensitive. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Command "{{commandName}}" started on database "{{databaseName}}" using a connection with driver-generated ID +> {{driverConnectionId}} and server-generated ID {{serverConnectionId}} to {{serverHost}}:{{serverPort}} with service ID +> {{serviceId}}. The requestID is {{requestId}} and the operation ID is {{operationId}}. Command: {{command}} + +#### Command Succeeded Message + +In addition to the common fields, command succeeded messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Command succeeded" | +| durationMS | Int32/Int64/Double | The execution time for the command in milliseconds. The calculated value MUST be the time to send the message and receive the reply from the server and MAY include BSON serialization and/or deserialization. | +| reply | String | Relaxed extJSON representation of the reply. This document MUST be truncated appropriately according to rules defined in the [logging specification](../logging/logging.md#configurable-max-document-length), and MUST be replaced with an empty document "{ }" if the command is considered sensitive. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Command "{{commandName}}" succeeded on database "{{databaseName}}" in {{durationMS}} ms using a connection with +> driver-generated ID {{driverConnectionId}} and server-generated ID {{serverConnectionId}} to +> {{serverHost}}:{{serverPort}} with service ID {{serviceId}}. The requestID is {{requestId}} and the operation ID is +> {{operationId}}. Command reply: {{command}} + +#### Command Failed Message + +In addition to the common fields, command failed messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Command failed" | +| durationMS | Int32/Int64/Double | The execution time for the command in milliseconds. The calculated value MUST be the time to send the message and receive the reply from the server and MAY include BSON serialization and/or deserialization. | +| failure | Flexible | The error. The type and format of this value is flexible; see the [logging specification](../logging/logging.md#representing-errors-in-log-messages) for details on representing errors in log messages. If the command is considered sensitive, the error MUST be redacted and replaced with a language-appropriate alternative for a redacted error, e.g. an empty string, empty document, or null. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Command "{{commandName}}" failed on database "{{databaseName}}" in {{durationMS}} ms using a connection with +> driver-generated ID {{driverConnectionId}} and server-generated ID {{serverConnectionId}} to +> {{serverHost}}:{{serverPort}} with service ID {{serviceId}}. The requestID is {{requestId}} and the operation ID is +> {{operationId}}. Error: {{error}} + +### Testing + +See the README in the test directory for requirements and guidance. + +## Q&A + +### Why is the command document only available in the `CommandStartEvent`? + +Some drivers may realize the command document as raw BSON, treating it as a component of the message transmitted to the +server and stored in an internal buffer. By the time the server's response is received, this buffer may have been +released. Requiring the retention of this buffer until command completion could result in unacceptable performance +penalties, particularly when event listeners are introduced. + +## Changelog + +- 2025-01-22: Clarify durationMS in logs may be Int32/Int64/Double. + +- 2024-09-11: Migrated from reStructuredText to Markdown. + +- 2015-09-16: Removed `limit` from find test with options to support 3.2.\ + Changed find test read preference to `primaryPreferred`. + +- 2015-10-01: Changed find test with a kill cursors to not run on server versions\ + greater than 3.0. Added a find test with no kill cursors command which only runs on 3.1 and higher. Added notes on + which tests should run based on server versions. + +- 2015-10-19: Changed batchSize in the 3.2 find tests to expect the remaining value. + +- 2015-10-31: Changed find test on 3.1 and higher to ignore being run on sharded clusters. + +- 2015-11-22: Specify how to merge OP_MSG document sequences into command-started events. + +- 2016-03-29: Added note on guarantee of the request ids. + +- 2016-11-02: Added clause for not upconverting commands larger than maxBsonSize. + +- 2018-04-16: Made inclusion of BSON serialization/deserialization in command\ + durations to be optional. + +- 2020-02-12: Added legacy hello `speculativeAuthenticate` to the list of\ + values that should be redacted. + +- 2021-04-15: Added `serviceId` field to events. + +- 2021-05-05: Updated to use hello and legacy hello. + +- 2021-08-30: Added `serverConnectionId` field to `CommandStartedEvent`,\ + `CommandSucceededEvent` and `CommandFailedEvent`. + +- 2022-05-18: Converted legacy tests to the unified test format. + +- 2022-09-02: Remove material that only applies to MongoDB versions < 3.6. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-10-11: Add command logging information and tests. + +- 2022-11-16: Update sensitive command tests to only run on server versions where the commands are supported. + +- 2022-12-13: Updated log message `serverPort` field description to clarify drivers should populate it with the\ + default port 27017 when relevant. Updated suggested unstructured forms of log messages to more clearly label + connection IDs and use more readable server address representations. + +- 2023-03-23: Updated `serverConnectionId` field to be Int64 as long-running servers can return Int64. + +- 2023-06-13: Added `databaseName` field to `CommandFailedEvent` and `CommandSucceededEvent`.\ + Updated suggested unstructured forms of log messages reflecting the changes. + +- 2023-10-19: Add Q&A section diff --git a/source/command-logging-and-monitoring/command-logging-and-monitoring.rst b/source/command-logging-and-monitoring/command-logging-and-monitoring.rst new file mode 100644 index 0000000000..85a7d7966b --- /dev/null +++ b/source/command-logging-and-monitoring/command-logging-and-monitoring.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `command-logging-and-monitoring.md `_. diff --git a/source/command-logging-and-monitoring/tests/README.md b/source/command-logging-and-monitoring/tests/README.md new file mode 100644 index 0000000000..c6159699da --- /dev/null +++ b/source/command-logging-and-monitoring/tests/README.md @@ -0,0 +1,59 @@ +# Command Logging and Monitoring + +______________________________________________________________________ + +## Testing + +### Automated Tests + +There are tests in the [Unified Test Format](../../unified-test-format/unified-test-format.md) for both logging and +monitoring in `/logging` and `monitoring`, respectively. Drivers MUST run the logging tests with their max document +length setting (as described in the [logging specification](../../logging/logging.md#configurable-max-document-length)) +set to a large value e.g. 10,000; this is necessary in order for the driver to emit the full server reply (and to allow +matching against that reply) on certain MongoDB versions and topologies. + +### Prose Tests + +Drivers MUST implement the following logging prose tests. These tests require the ability to capture log message data in +a structured form as described in the +[Unified Test Format specification](../../unified-test-format/unified-test-format.md#expectedLogMessage). + +Note: the following tests mention string "length"; this refers to length in terms of whatever unit the driver has chosen +to support for specifying max document length as discussed in the +[logging specification](../../logging/logging.md#configurable-max-document-length). + +*Test 1: Default truncation limit* + +1. Configure logging with a minimum severity level of "debug" for the "command" component. Do not explicitly configure + the max document length. +2. Construct an array `docs` containing the document `{"x" : "y"}` repeated 100 times. +3. Insert `docs` to a collection via `insertMany`. +4. Inspect the resulting "command started" log message and assert that the "command" value is a string of length 1000 + + (length of trailing ellipsis). +5. Inspect the resulting "command succeeded" log message and assert that the "reply" value is a string of length \<= + 1000 + (length of trailing ellipsis). +6. Run `find()` on the collection where the document was inserted. +7. Inspect the resulting "command succeeded" log message and assert that the reply is a string of length 1000 + (length + of trailing ellipsis). + +*Test 2: Explicitly configured truncation limit* + +1. Configure logging with a minimum severity level of "debug" for the "command" component. Set the max document length + to 5. +2. Run the command `{"hello": true}`. +3. Inspect the resulting "command started" log message and assert that the "command" value is a string of length 5 + + (length of trailing ellipsis). +4. Inspect the resulting "command succeeded" log message and assert that the "reply" value is a string of length 5 + + (length of trailing ellipsis). +5. If the driver attaches raw server responses to failures and can access these via log messages to assert on, run the + command `{"notARealCommand": true}`. Inspect the resulting "command failed" log message and confirm that the server + error is a string of length 5 + (length of trailing ellipsis). + +*Test 3: Truncation with multi-byte codepoints* + +A specific test case is not provided here due to the allowed variations in truncation logic as well as varying extended +JSON whitespace usage. Drivers MUST write language-specific tests that confirm truncation of commands, replies, and (if +applicable) server responses included in error messages work as expected when the data being truncated includes +multi-byte Unicode codepoints. If the driver uses anything other than Unicode codepoints as the unit for max document +length, there also MUST be tests confirming that cases where the max length falls in the middle of a multi-byte +codepoint are handled gracefully. diff --git a/source/command-logging-and-monitoring/tests/logging/command.json b/source/command-logging-and-monitoring/tests/logging/command.json new file mode 100644 index 0000000000..57e18b365c --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/command.json @@ -0,0 +1,543 @@ +{ + "description": "command-logging", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful command", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ping": 1, + "$db": "logging-tests" + } + } + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "databaseName": "logging-tests", + "commandName": "ping", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A failed command", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "find", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "databaseName": "logging-tests", + "commandName": "find", + "failure": { + "$$exists": true + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A successful commitTransaction command", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "name": "commitTransaction", + "object": "session" + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "insert", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "databaseName": "logging-tests", + "commandName": "insert", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "admin", + "commandName": "commitTransaction", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "databaseName": "admin", + "commandName": "commitTransaction", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A successful abortTransaction command", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "startTransaction", + "object": "session" + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "session": "session", + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "name": "abortTransaction", + "object": "session" + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "insert", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "databaseName": "logging-tests", + "commandName": "insert", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "admin", + "commandName": "abortTransaction", + "command": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "databaseName": "admin", + "commandName": "abortTransaction", + "reply": { + "$$type": "string" + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/command.yml b/source/command-logging-and-monitoring/tests/logging/command.yml new file mode 100644 index 0000000000..ef1a64b642 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/command.yml @@ -0,0 +1,226 @@ +description: "command-logging" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "A successful command" + operations: + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + command: + $$matchAsDocument: + $$matchAsRoot: + ping: 1 + $db: *databaseName + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + databaseName: *databaseName + commandName: *commandName + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - description: "A failed command" + operations: + - name: &commandName find + object: *collection + arguments: + filter: { $or: true } + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command failed" + databaseName: *databaseName + commandName: *commandName + failure: { $$exists: true } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - description: "A successful commitTransaction command" + runOnRequirements: + - topologies: + - replicaset + - sharded + operations: + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 2, x: 22 } + - name: commitTransaction + object: *session + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: insert + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + databaseName: *databaseName + commandName: insert + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - level: debug + component: command + data: + message: "Command started" + databaseName: admin + commandName: commitTransaction + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + databaseName: admin + commandName: commitTransaction + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - description: "A successful abortTransaction command" + runOnRequirements: + - topologies: + - replicaset + - sharded + operations: + - name: startTransaction + object: *session + - name: insertOne + object: *collection + arguments: + session: *session + document: { _id: 3, x: 33 } + - name: abortTransaction + object: *session + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: insert + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + databaseName: *databaseName + commandName: insert + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - level: debug + component: command + data: + message: "Command started" + databaseName: admin + commandName: abortTransaction + command: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + databaseName: admin + commandName: abortTransaction + reply: { $$type: string } + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } diff --git a/source/command-logging-and-monitoring/tests/logging/driver-connection-id.json b/source/command-logging-and-monitoring/tests/logging/driver-connection-id.json new file mode 100644 index 0000000000..40db98d6fa --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/driver-connection-id.json @@ -0,0 +1,146 @@ +{ + "description": "driver-connection-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "A successful command", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "A failed command", + "operations": [ + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "find", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/driver-connection-id.yml b/source/command-logging-and-monitoring/tests/logging/driver-connection-id.yml new file mode 100644 index 0000000000..f299edabaf --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/driver-connection-id.yml @@ -0,0 +1,76 @@ +# This is a separate test so that drivers that do not implement CMAP can easily skip it. +description: "driver-connection-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "A successful command" + operations: + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - description: "A failed command" + operations: + - name: &commandName find + object: *collection + arguments: + filter: { $or: true } + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + driverConnectionId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command failed" + commandName: *commandName + driverConnectionId: { $$type: [int, long] } diff --git a/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.json b/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.json new file mode 100644 index 0000000000..a61e208798 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.json @@ -0,0 +1,94 @@ +{ + "description": "no-handshake-command-logs", + "schemaVersion": "1.13", + "tests": [ + { + "description": "Handshake commands should not generate log messages", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionCreatedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping" + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml b/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml new file mode 100644 index 0000000000..bb7dd18e09 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/no-handshake-messages.yml @@ -0,0 +1,58 @@ +description: "no-handshake-command-logs" + +schemaVersion: "1.13" + +tests: + - description: "Handshake commands should not generate log messages" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + command: debug + observeEvents: + - connectionCreatedEvent + - connectionReadyEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionCreatedEvent: {} + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionReadyEvent: {} + count: 1 + expectLogMessages: + # since the ping happens after the handshake, seeing events for only the ping + # implies the driver did not emit any log messages for the handshake. + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName diff --git a/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json b/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json new file mode 100644 index 0000000000..525be9171d --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.json @@ -0,0 +1,91 @@ +{ + "description": "no-heartbeat-command-logs", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "tests": [ + { + "description": "Heartbeat commands should not generate log messages", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": {} + }, + "count": 1 + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "ping" + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml b/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml new file mode 100644 index 0000000000..7d35fbe003 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/no-heartbeat-messages.yml @@ -0,0 +1,58 @@ +description: "no-heartbeat-command-logs" + +schemaVersion: "1.13" + +# no heartbeats in load balanced mode. +runOnRequirements: + - topologies: + - single + - replicaset + - sharded + +tests: + - description: "Heartbeat commands should not generate log messages" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + command: debug + observeEvents: + - serverDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + # a server description change implies that a heartbeat has happened. + serverDescriptionChangedEvent: {} + count: 1 + - name: runCommand + object: *database + arguments: + command: { ping: 1 } + commandName: &commandName ping + expectLogMessages: + # since the ping happens after the heartbeat, seeing events for only the ping + # implies the driver did not emit a log message for the heartbeat. + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: *commandName + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: *commandName diff --git a/source/command-logging-and-monitoring/tests/logging/operation-id.json b/source/command-logging-and-monitoring/tests/logging/operation-id.json new file mode 100644 index 0000000000..b1a3cec3d9 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/operation-id.json @@ -0,0 +1,198 @@ +{ + "description": "operation-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "Successful bulk write command log messages include operationIds", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "x": 1 + } + } + }, + { + "deleteOne": { + "filter": { + "x": 1 + } + } + } + ] + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "insert", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "delete", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "delete", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Failed bulk write command log message includes operationId", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "x": 1 + }, + "update": [ + { + "$invalidOperator": true + } + ] + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "update", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "update", + "operationId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/operation-id.yml b/source/command-logging-and-monitoring/tests/logging/operation-id.yml new file mode 100644 index 0000000000..f763e03b14 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/operation-id.yml @@ -0,0 +1,99 @@ +# This test only applies to drivers that generate operationIds to enable users to link +# together bulk writes. +description: "operation-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + - description: "Successful bulk write command log messages include operationIds" + operations: + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { x: 1 } + - deleteOne: + filter: { x: 1 } + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: insert + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: delete + operationId: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: delete + operationId: { $$type: [int, long] } + + - description: "Failed bulk write command log message includes operationId" + operations: + - name: bulkWrite + object: *collection + arguments: + requests: + - updateOne: + filter: { x: 1 } + update: [{ $invalidOperator: true }] + expectError: + isClientError: false + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: update + operationId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command failed" + commandName: update + operationId: { $$type: [int, long] } diff --git a/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json b/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json new file mode 100644 index 0000000000..d5ebd86590 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.json @@ -0,0 +1,119 @@ +{ + "description": "pre-42-server-connection-id", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages 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 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serverConnectionId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serverConnectionId": { + "$$exists": false + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml b/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml new file mode 100644 index 0000000000..7dc80eea07 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/pre-42-server-connection-id.yml @@ -0,0 +1,66 @@ +description: "pre-42-server-connection-id" + +schemaVersion: "1.13" + +runOnRequirements: + - maxServerVersion: "4.0.99" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages 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 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serverConnectionId: { $$exists: false } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serverConnectionId: { $$exists: false } diff --git a/source/command-logging-and-monitoring/tests/logging/redacted-commands.json b/source/command-logging-and-monitoring/tests/logging/redacted-commands.json new file mode 100644 index 0000000000..43b9ff74f2 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/redacted-commands.json @@ -0,0 +1,1438 @@ +{ + "description": "redacted-commands", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "minServerVersion": "5.0", + "auth": false + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-redaction-tests" + } + } + ], + "tests": [ + { + "description": "authenticate command and resulting server-generated error are redacted", + "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": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "authenticate", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "authenticate", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to authenticate is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "authenticate" + ], + "closeConnection": true + } + } + } + }, + { + "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" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "authenticate", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "authenticate", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "saslStart command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": 1, + "payload": "definitely-invalid-payload", + "db": "admin" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslStart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "saslStart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to saslStart is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslStart" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslStart", + "command": { + "saslStart": 1, + "payload": "ZmFrZXNhc2xwYXlsb2Fk", + "mechanism": "MONGODB-X509" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslStart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "saslStart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "saslContinue command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": 1, + "conversationId": 0, + "payload": "definitely-invalid-payload" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslContinue", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "saslContinue", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to saslContinue is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "saslContinue", + "command": { + "saslContinue": 1, + "conversationId": 0, + "payload": "ZmFrZXNhc2xwYXlsb2Fk" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "saslContinue", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "saslContinue", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "getnonce command and server reply are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "getnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "getnonce", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "network error in response to getnonce is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "6.1.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "getnonce" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "getnonce", + "command": { + "getnonce": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "getnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "getnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "createUser command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "createUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "createUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to createUser is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "createUser" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "createUser", + "command": { + "createUser": "private", + "pwd": "pwd", + "roles": [] + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "createUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "createUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "updateUser command and resulting server-generated error are redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private", + "pwd": {}, + "roles": [] + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "updateUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "updateUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to updateUser is not redacted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "updateUser" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "updateUser", + "command": { + "updateUser": "private", + "pwd": "pwd", + "roles": [] + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "updateUser", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "updateUser", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydbgetnonce command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "3.6.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydbgetnonce is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "3.6.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydbgetnonce" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbgetnonce", + "command": { + "copydbgetnonce": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydbsaslstart command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbsaslstart", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydbsaslstart", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydbsaslstart is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydbsaslstart" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydbsaslstart", + "command": { + "copydbsaslstart": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydbgetnonce", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydbgetnonce", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "copydb command and resulting server-generated error are redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydb", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": true, + "data": { + "message": "Command failed", + "commandName": "copydb", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "network error in response to copydb is not redacted", + "runOnRequirements": [ + { + "maxServerVersion": "4.0.99" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "copydb" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "copydb", + "command": { + "copydb": "private" + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "copydb", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "failureIsRedacted": false, + "data": { + "message": "Command failed", + "commandName": "copydb", + "failure": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "hello with speculative authenticate command and server reply are redacted", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1, + "speculativeAuthenticate": { + "saslStart": 1 + } + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "hello", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "hello", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "legacy hello with speculative authenticate command and server reply are redacted", + "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 + } + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "ismaster", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ismaster", + "reply": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "isMaster", + "command": { + "$$matchAsDocument": {} + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "isMaster", + "reply": { + "$$matchAsDocument": {} + } + } + } + ] + } + ] + }, + { + "description": "hello without speculative authenticate command and server reply are not redacted", + "runOnRequirements": [ + { + "minServerVersion": "4.9" + } + ], + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "hello", + "command": { + "hello": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "hello", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "hello": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "hello", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "isWritablePrimary": true + } + } + } + } + } + ] + } + ] + }, + { + "description": "legacy hello without speculative authenticate command and server reply are not redacted", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ismaster", + "command": { + "ismaster": 1 + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "isMaster", + "command": { + "isMaster": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "ismaster", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ismaster": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "ismaster", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "ismaster": true + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-redaction-tests", + "commandName": "isMaster", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "isMaster": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "isMaster", + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1, + "ismaster": true + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/redacted-commands.yml b/source/command-logging-and-monitoring/tests/logging/redacted-commands.yml new file mode 100644 index 0000000000..05d61465e5 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/redacted-commands.yml @@ -0,0 +1,850 @@ +description: "redacted-commands" + +schemaVersion: "1.13" + +runOnRequirements: + - minServerVersion: "5.0" + auth: false + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeLogMessages: + command: debug + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: &databaseName logging-redaction-tests + +tests: + - description: "authenticate command and resulting server-generated error are redacted" + 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" + # An authentication error is expected, but we want to check that the + # CommandStartedEvent is redacted + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: authenticate + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: authenticate + failure: { $$exists: true } + + - description: "network error in response to authenticate is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["authenticate"] + closeConnection: true + - 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" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: authenticate + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: authenticate + failure: { $$exists: true } + + - description: "saslStart command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslStart + command: + saslStart: 1 + payload: "definitely-invalid-payload" + db: "admin" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslStart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: saslStart + failure: { $$exists: true } + + - description: "network error in response to saslStart is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslStart"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: saslStart + command: + saslStart: 1 + payload: ZmFrZXNhc2xwYXlsb2Fk + mechanism: MONGODB-X509 + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslStart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: saslStart + failure: { $$exists: true } + + - description: "saslContinue command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: saslContinue + command: + saslContinue: 1 + conversationId: 0 + payload: "definitely-invalid-payload" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslContinue + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: saslContinue + failure: { $$exists: true } + + - description: "network error in response to saslContinue is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslContinue"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: saslContinue + command: + saslContinue: 1 + conversationId: 0 + payload: ZmFrZXNhc2xwYXlsb2Fk + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: saslContinue + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: saslContinue + failure: { $$exists: true } + + - description: "getnonce command and server reply are redacted" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: runCommand + object: *database + arguments: + commandName: getnonce + command: + getnonce: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: getnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: getnonce + reply: + $$matchAsDocument: {} + + - description: "network error in response to getnonce is not redacted" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["getnonce"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: getnonce + command: + getnonce: 1 + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: getnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: getnonce + failure: { $$exists: true } + + - description: "createUser command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: createUser + command: + createUser: "private" + # Passing an object is prohibited and we want to trigger a command + # failure + pwd: {} + roles: [] + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: createUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: createUser + failure: { $$exists: true } + + - description: "network error in response to createUser is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["createUser"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: createUser + command: + createUser: "private" + pwd: "pwd" + roles: [] + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: createUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: createUser + failure: { $$exists: true } + + - description: "updateUser command and resulting server-generated error are redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: updateUser + command: + updateUser: "private" + pwd: {} + roles: [] + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: updateUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: updateUser + failure: { $$exists: true } + + - description: "network error in response to updateUser is not redacted" + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["updateUser"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: updateUser + command: + updateUser: "private" + pwd: "pwd" + roles: [] + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: updateUser + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: updateUser + failure: { $$exists: true } + + - description: "copydbgetnonce command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 3.6.99 # copydbgetnonce was removed as of 4.0 via SERVER-32276 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbgetnonce + command: + copydbgetnonce: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "network error in response to copydbgetnonce is not redacted" + runOnRequirements: + - maxServerVersion: 3.6.99 # copydbgetnonce was removed as of 4.0 via SERVER-32276 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydbgetnonce"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydbgetnonce + command: + copydbgetnonce: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "copydbsaslstart command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydbsaslstart was removed as of 4.2 via SERVER-36211 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydbsaslstart + command: + copydbsaslstart: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbsaslstart + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydbsaslstart + failure: { $$exists: true } + + - description: "network error in response to copydbsaslstart is not redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydbsaslstart was removed as of 4.2 via SERVER-36211 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydbsaslstart"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydbsaslstart + command: + copydbsaslstart: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydbgetnonce + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydbgetnonce + failure: { $$exists: true } + + - description: "copydb command and resulting server-generated error are redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydb was removed as of 4.2 via SERVER-36257 + operations: + - name: runCommand + object: *database + arguments: + commandName: copydb + command: + copydb: "private" + expectError: + isClientError: false + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydb + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: true + data: + message: "Command failed" + commandName: copydb + failure: { $$exists: true } + + - description: "network error in response to copydb is not redacted" + runOnRequirements: + - maxServerVersion: 4.0.99 # copydb was removed as of 4.2 via SERVER-36257 + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["copydb"] + closeConnection: true + - name: runCommand + object: *database + arguments: + commandName: copydb + command: + copydb: "private" + expectError: + isClientError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: copydb + command: + $$matchAsDocument: {} + - level: debug + component: command + failureIsRedacted: false + data: + message: "Command failed" + commandName: copydb + failure: { $$exists: true } + + - description: "hello with speculative authenticate command and server reply are redacted" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: 1 + speculativeAuthenticate: + saslStart: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: hello + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: hello + reply: + $$matchAsDocument: {} + + + - description: "legacy hello with speculative authenticate command and server reply are redacted" + 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 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: ismaster + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: ismaster + reply: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: isMaster + command: + $$matchAsDocument: {} + - level: debug + component: command + data: + message: "Command succeeded" + commandName: isMaster + reply: + $$matchAsDocument: {} + + - description: "hello without speculative authenticate command and server reply are not redacted" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *database + arguments: + commandName: hello + command: + hello: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: hello + command: + $$matchAsDocument: + $$matchAsRoot: + hello: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: hello + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + isWritablePrimary: true + + - description: "legacy hello without speculative authenticate command and server reply are not redacted" + operations: + - name: runCommand + object: *database + arguments: + commandName: ismaster + command: + ismaster: 1 + - name: runCommand + object: *database + arguments: + commandName: isMaster + command: + isMaster: 1 + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: ismaster + command: + $$matchAsDocument: + $$matchAsRoot: + ismaster: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: ismaster + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + ismaster: true + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: isMaster + command: + $$matchAsDocument: + $$matchAsRoot: + isMaster: 1 + - level: debug + component: command + data: + message: "Command succeeded" + commandName: isMaster + reply: + $$matchAsDocument: + $$matchAsRoot: + ok: 1 + ismaster: true diff --git a/source/command-logging-and-monitoring/tests/logging/server-connection-id.json b/source/command-logging-and-monitoring/tests/logging/server-connection-id.json new file mode 100644 index 0000000000..abbbbc7442 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/server-connection-id.json @@ -0,0 +1,131 @@ +{ + "description": "server-connection-id", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages include server connection id", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serverConnectionId": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/server-connection-id.yml b/source/command-logging-and-monitoring/tests/logging/server-connection-id.yml new file mode 100644 index 0000000000..4f54d1207e --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/server-connection-id.yml @@ -0,0 +1,66 @@ +description: "server-connection-id" + +schemaVersion: "1.13" + +runOnRequirements: + - minServerVersion: "4.2" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages include server connection id" + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serverConnectionId: { $$type: [int, long] } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serverConnectionId: { $$type: [int, long] } diff --git a/source/command-logging-and-monitoring/tests/logging/service-id.json b/source/command-logging-and-monitoring/tests/logging/service-id.json new file mode 100644 index 0000000000..ea39d61231 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/service-id.json @@ -0,0 +1,207 @@ +{ + "description": "service-id", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-server-connection-id-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection" + } + } + ], + "initialData": [ + { + "databaseName": "logging-server-connection-id-tests", + "collectionName": "logging-tests-collection", + "documents": [] + } + ], + "tests": [ + { + "description": "command log messages include serviceId when in LB mode", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serviceId": { + "$$type": "string" + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serviceId": { + "$$type": "string" + } + } + } + ] + } + ] + }, + { + "description": "command log messages omit serviceId when not in LB mode", + "runOnRequirements": [ + { + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "insert", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "commandName": "find", + "serviceId": { + "$$exists": false + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command failed", + "commandName": "find", + "serviceId": { + "$$exists": false + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/service-id.yml b/source/command-logging-and-monitoring/tests/logging/service-id.yml new file mode 100644 index 0000000000..0c0f444e23 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/service-id.yml @@ -0,0 +1,111 @@ +description: "service-id" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client client + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + documents: [] + +tests: + - description: "command log messages include serviceId when in LB mode" + runOnRequirements: + - topologies: + - load-balanced + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serviceId: { $$type: string } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serviceId: { $$type: string } + + - description: "command log messages omit serviceId when not in LB mode" + runOnRequirements: + - topologies: + - single + - replicaset + - sharded + operations: + - name: insertOne + object: *collection + arguments: + document: { x: 1 } + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isError: true + expectLogMessages: + - client: *client + messages: + - level: debug + component: command + data: + message: "Command started" + commandName: insert + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command started" + commandName: find + serviceId: { $$exists: false } + - level: debug + component: command + data: + message: "Command failed" + commandName: find + serviceId: { $$exists: false } diff --git a/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.json b/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.json new file mode 100644 index 0000000000..0d33c020d5 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.json @@ -0,0 +1,151 @@ +{ + "description": "unacknowledged-write", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeLogMessages": { + "command": "debug" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "logging-tests-collection", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "initialData": [ + { + "collectionName": "logging-tests-collection", + "databaseName": "logging-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ], + "tests": [ + { + "description": "An unacknowledged write generates a succeeded log message with ok: 1 reply", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "messages": [ + { + "level": "debug", + "component": "command", + "data": { + "message": "Command started", + "databaseName": "logging-tests", + "commandName": "insert", + "command": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "logging-tests-collection", + "$db": "logging-tests" + } + } + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "command", + "data": { + "message": "Command succeeded", + "commandName": "insert", + "reply": { + "$$matchAsDocument": { + "ok": 1 + } + }, + "requestId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.yml b/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.yml new file mode 100644 index 0000000000..faeae0495c --- /dev/null +++ b/source/command-logging-and-monitoring/tests/logging/unacknowledged-write.yml @@ -0,0 +1,76 @@ +description: "unacknowledged-write" + +schemaVersion: "1.16" + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeLogMessages: + command: debug + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName logging-tests-collection + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + +tests: + - description: "An unacknowledged write generates a succeeded log message with ok: 1 reply" + operations: + - name: insertOne + object: *collection + arguments: + document: { _id: 2 } + # Force completion of the w: 0 write by executing a find on the same connection + - name: find + object: *collection + arguments: + filter: { } + expectResult: [ + { _id: 1 }, + { _id: 2 } + ] + expectLogMessages: + - client: *client + ignoreExtraMessages: true + messages: + - level: debug + component: command + data: + message: "Command started" + databaseName: *databaseName + commandName: insert + command: + $$matchAsDocument: + $$matchAsRoot: + insert: *collectionName + $db: *databaseName + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: command + data: + message: "Command succeeded" + commandName: insert + reply: + $$matchAsDocument: + ok: 1 + requestId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + diff --git a/source/command-logging-and-monitoring/tests/monitoring/bulkWrite.json b/source/command-logging-and-monitoring/tests/monitoring/bulkWrite.json new file mode 100644 index 0000000000..49c728442e --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/bulkWrite.yml b/source/command-logging-and-monitoring/tests/monitoring/bulkWrite.yml new file mode 100644 index 0000000000..607b2d1ca7 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/bulkWrite.yml @@ -0,0 +1,68 @@ +description: "bulkWrite" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + documents: + - { _id: 4, x: 44 } + ordered: true + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: insert + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: {_id: 3 } + u: { $set: { x: 333 } } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + ordered: true + commandName: update + databaseName: *databaseName + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update diff --git a/source/command-logging-and-monitoring/tests/monitoring/command.json b/source/command-logging-and-monitoring/tests/monitoring/command.json new file mode 100644 index 0000000000..c28af95fed --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/command.yml b/source/command-logging-and-monitoring/tests/monitoring/command.yml new file mode 100644 index 0000000000..f8c3363606 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/command.yml @@ -0,0 +1,50 @@ +description: "command" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + commandName: ping + +# The legacy "A failed command event" test was removed in the test conversion, as the +# behavior when a command fails is already covered by the test "A failed find event" +# in find.yml. diff --git a/source/command-logging-and-monitoring/tests/monitoring/deleteMany.json b/source/command-logging-and-monitoring/tests/monitoring/deleteMany.json new file mode 100644 index 0000000000..78ebad1f98 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/deleteMany.yml b/source/command-logging-and-monitoring/tests/monitoring/deleteMany.yml new file mode 100644 index 0000000000..2804b26613 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/deleteMany.yml @@ -0,0 +1,79 @@ +description: "deleteMany" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + deletes: + - { q: { _id: { $gt: 1 }}, limit: 0 } + ordered: true + commandName: delete + databaseName: *databaseName + - 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: *collectionName + deletes: + - { q: { _id: { $unsupported: 1 }}, limit: 0 } + ordered: true + commandName: delete + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: delete diff --git a/source/command-logging-and-monitoring/tests/monitoring/deleteOne.json b/source/command-logging-and-monitoring/tests/monitoring/deleteOne.json new file mode 100644 index 0000000000..2420794fe5 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/deleteOne.yml b/source/command-logging-and-monitoring/tests/monitoring/deleteOne.yml new file mode 100644 index 0000000000..4081567d5b --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/deleteOne.yml @@ -0,0 +1,79 @@ +description: "deleteOne" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + deletes: + - { q: { _id: { $gt: 1 }}, limit: 1 } + ordered: true + commandName: delete + databaseName: *databaseName + - 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: *collectionName + deletes: + - { q: { _id: { $unsupported: 1 }}, limit: 1 } + ordered: true + commandName: delete + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: delete diff --git a/source/command-logging-and-monitoring/tests/monitoring/find.json b/source/command-logging-and-monitoring/tests/monitoring/find.json new file mode 100644 index 0000000000..bc9668499b --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/find.json @@ -0,0 +1,558 @@ +{ + "description": "find", + "schemaVersion": "1.15", + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + }, + { + "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", + "databaseName": "command-monitoring-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/monitoring/find.yml b/source/command-logging-and-monitoring/tests/monitoring/find.yml new file mode 100644 index 0000000000..479e4a460c --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/find.yml @@ -0,0 +1,262 @@ +description: "find" + +schemaVersion: "1.15" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +_yamlAnchors: + namespace: &namespace "command-monitoring-tests.test" + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + filter: { _id: 1 } + commandName: find + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + firstBatch: + - { _id: 1, x: 11 } + commandName: find + databaseName: *databaseName + + - 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: *collectionName + 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: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + firstBatch: + - { x: 33 } + - { x: 22 } + commandName: find + databaseName: *databaseName + + - 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: *collectionName + showRecordId: true + returnKey: true + commandName: find + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + firstBatch: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + commandName: find + databaseName: *databaseName + + - 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: *collectionName + filter: { _id: { $gte: 1 }} + sort: { _id: 1 } + batchSize: 3 + commandName: find + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: { $$type: [ int, long ] } + ns: *namespace + firstBatch: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + commandName: find + databaseName: *databaseName + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collectionName + batchSize: 3 + commandName: getMore + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + nextBatch: + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + commandName: getMore + databaseName: *databaseName + + - 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: *collectionName + filter: { _id: { $gte: 1 } } + sort: { _id: 1 } + batchSize: 3 + limit: 4 + commandName: find + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: { $$type: [ int, long ] } + ns: *namespace + firstBatch: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + commandName: find + databaseName: *databaseName + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collectionName + batchSize: 1 + commandName: getMore + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + nextBatch: + - { _id: 4, x: 44 } + commandName: getMore + databaseName: *databaseName + + - description: "A failed find event" + operations: + - name: find + object: *collection + arguments: + filter: { $or: true } + expectError: + isClientError: false + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + find: *collectionName + filter: { $or: true } + commandName: find + databaseName: *databaseName + - commandFailedEvent: + commandName: find + databaseName: *databaseName diff --git a/source/command-logging-and-monitoring/tests/monitoring/insertMany.json b/source/command-logging-and-monitoring/tests/monitoring/insertMany.json new file mode 100644 index 0000000000..a80a218c67 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/insertMany.yml b/source/command-logging-and-monitoring/tests/monitoring/insertMany.yml new file mode 100644 index 0000000000..88c7e14900 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/insertMany.yml @@ -0,0 +1,79 @@ +description: "insertMany" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + documents: + - { _id: 2, x: 22 } + ordered: true + commandName: insert + databaseName: *databaseName + - 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: *collectionName + documents: + - { _id: 1, x: 11 } + ordered: true + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: insert diff --git a/source/command-logging-and-monitoring/tests/monitoring/insertOne.json b/source/command-logging-and-monitoring/tests/monitoring/insertOne.json new file mode 100644 index 0000000000..6ff732e41b --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/insertOne.yml b/source/command-logging-and-monitoring/tests/monitoring/insertOne.yml new file mode 100644 index 0000000000..cd4431295b --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/insertOne.yml @@ -0,0 +1,77 @@ +description: "insertOne" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + documents: + - { _id: 2, x: 22 } + ordered: true + commandName: insert + databaseName: *databaseName + - 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: *collectionName + documents: + - { _id: 1, x: 11 } + ordered: true + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: insert diff --git a/source/command-logging-and-monitoring/tests/monitoring/pre-42-server-connection-id.json b/source/command-logging-and-monitoring/tests/monitoring/pre-42-server-connection-id.json new file mode 100644 index 0000000000..141fbe584f --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/pre-42-server-connection-id.yml b/source/command-logging-and-monitoring/tests/monitoring/pre-42-server-connection-id.yml new file mode 100644 index 0000000000..483a8a3c19 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/pre-42-server-connection-id.yml @@ -0,0 +1,56 @@ +description: "pre-42-server-connection-id" + +schemaVersion: "1.6" + +runOnRequirements: + - maxServerVersion: "4.0.99" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + 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/source/command-logging-and-monitoring/tests/monitoring/redacted-commands.json b/source/command-logging-and-monitoring/tests/monitoring/redacted-commands.json new file mode 100644 index 0000000000..4302ba8900 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/redacted-commands.yml b/source/command-logging-and-monitoring/tests/monitoring/redacted-commands.yml new file mode 100644 index 0000000000..adeab99b18 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/redacted-commands.yml @@ -0,0 +1,348 @@ +description: "redacted-commands" + +schemaVersion: "1.5" + +runOnRequirements: + - minServerVersion: "5.0" + auth: false + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + observeSensitiveCommands: true + - database: + id: &database database + client: *client + databaseName: &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" + # An authentication error is expected, but we want to check that the + # CommandStartedEvent is redacted + expectError: + isError: true + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: authenticate + # We cannot simply assert that command is an empty document + # because it's at root-level, so we make a best effort to make + # sure sensitive fields are redacted. + 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 # getnonce removed as of 6.2 via SERVER-71007 + 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" + # Passing an object is prohibited and we want to trigger a command + # failure + 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 # copydbgetnonce was removed as of 4.0 via SERVER-32276 + 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 # copydbsaslstart was removed as of 4.2 via SERVER-36211 + 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 # copydb was removed as of 4.2 via SERVER-36257 + 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: + # Even though authentication above fails and the reply does not + # contain sensitive information, we're expecting the reply to be + # redacted as well. + isWritablePrimary: { $$exists: false } + # This assertion will currently always hold true since we're + # not expecting successful authentication, in which case this + # field is missing anyways. + 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/source/command-logging-and-monitoring/tests/monitoring/server-connection-id.json b/source/command-logging-and-monitoring/tests/monitoring/server-connection-id.json new file mode 100644 index 0000000000..a8f27637fc --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/server-connection-id.yml b/source/command-logging-and-monitoring/tests/monitoring/server-connection-id.yml new file mode 100644 index 0000000000..abd34dad6e --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/server-connection-id.yml @@ -0,0 +1,56 @@ +description: "server-connection-id" + +schemaVersion: "1.6" + +runOnRequirements: + - minServerVersion: "4.2" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName server-connection-id-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - databaseName: *databaseName + collectionName: *collectionName + 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/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.json b/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.json new file mode 100644 index 0000000000..14740cea34 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.json @@ -0,0 +1,225 @@ +{ + "description": "unacknowledged-client-bulkWrite", + "schemaVersion": "1.7", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ], + "uriOptions": { + "w": 0 + } + } + }, + { + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "command-monitoring-tests.test" + }, + "tests": [ + { + "description": "A successful mixed client bulkWrite", + "operations": [ + { + "object": "client", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "command-monitoring-tests.test", + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "namespace": "command-monitoring-tests.test", + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "x": 333 + } + } + } + } + ], + "ordered": false + }, + "expectResult": { + "$$unsetOrMatches": { + "acknowledged": { + "$$unsetOrMatches": false + }, + "insertedCount": { + "$$unsetOrMatches": 0 + }, + "upsertedCount": { + "$$unsetOrMatches": 0 + }, + "matchedCount": { + "$$unsetOrMatches": 0 + }, + "modifiedCount": { + "$$unsetOrMatches": 0 + }, + "deletedCount": { + "$$unsetOrMatches": 0 + }, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + }, + { + "object": "collection", + "name": "find", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 333 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": false, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 3 + }, + "updateMods": { + "$set": { + "x": 333 + } + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "command-monitoring-tests.test" + } + ] + } + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite", + "reply": { + "ok": 1, + "nInserted": { + "$$exists": false + }, + "nMatched": { + "$$exists": false + }, + "nModified": { + "$$exists": false + }, + "nUpserted": { + "$$exists": false + }, + "nDeleted": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.yml b/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.yml new file mode 100644 index 0000000000..f43f097c3d --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/unacknowledged-client-bulkWrite.yml @@ -0,0 +1,105 @@ +description: "unacknowledged-client-bulkWrite" + +schemaVersion: "1.7" + +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + uriOptions: + w: 0 + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "command-monitoring-tests.test" + +tests: + - description: 'A successful mixed client bulkWrite' + operations: + - object: *client + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + - updateOne: + namespace: *namespace + filter: { _id: 3 } + update: { $set: { x: 333 } } + ordered: false + expectResult: + $$unsetOrMatches: + acknowledged: { $$unsetOrMatches: false } + insertedCount: { $$unsetOrMatches: 0 } + upsertedCount: { $$unsetOrMatches: 0 } + matchedCount: { $$unsetOrMatches: 0 } + modifiedCount: { $$unsetOrMatches: 0 } + deletedCount: { $$unsetOrMatches: 0 } + insertResults: { $$unsetOrMatches: {} } + updateResults: { $$unsetOrMatches: {} } + deleteResults: { $$unsetOrMatches: {} } + # Force completion of the w:0 write by executing a find on the same connection + - object: *collection + name: find + arguments: + filter: {} + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 333 } + - { _id: 4, x: 44 } + + expectEvents: + - + client: *client + ignoreExtraEvents: true + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: false + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 3 } + updateMods: { $set: { x: 333 } } + multi: false + nsInfo: + - ns: *namespace + - commandSucceededEvent: + commandName: bulkWrite + reply: + ok: 1 + nInserted: { $$exists: false } + nMatched: { $$exists: false } + nModified: { $$exists: false } + nUpserted: { $$exists: false } + nDeleted: { $$exists: false } diff --git a/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.json b/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.json new file mode 100644 index 0000000000..78ddde767f --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.json @@ -0,0 +1,117 @@ +{ + "description": "unacknowledgedBulkWrite", + "schemaVersion": "1.7", + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "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 + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "ignoreExtraEvents": true, + "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/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.yml b/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.yml new file mode 100644 index 0000000000..c526fab325 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/unacknowledgedBulkWrite.yml @@ -0,0 +1,62 @@ +description: "unacknowledgedBulkWrite" + +schemaVersion: "1.7" + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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 + # Force completion of the w: 0 write by executing a find on the same connection + - name: find + object: *collection + arguments: + filter: { } + expectEvents: + - client: *client + ignoreExtraEvents: true + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: "unorderedBulkWriteInsertW0", x: 44 } + ordered: false + writeConcern: { w: 0 } + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: { $$exists: false } + commandName: insert diff --git a/source/command-logging-and-monitoring/tests/monitoring/updateMany.json b/source/command-logging-and-monitoring/tests/monitoring/updateMany.json new file mode 100644 index 0000000000..b15434226c --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/updateMany.yml b/source/command-logging-and-monitoring/tests/monitoring/updateMany.yml new file mode 100644 index 0000000000..244bad94f6 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/updateMany.yml @@ -0,0 +1,87 @@ +description: "updateMany" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: true + ordered: true + commandName: update + databaseName: *databaseName + - 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $unsupported: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: true + ordered: true + commandName: update + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: update diff --git a/source/command-logging-and-monitoring/tests/monitoring/updateOne.json b/source/command-logging-and-monitoring/tests/monitoring/updateOne.json new file mode 100644 index 0000000000..a0ae99e88d --- /dev/null +++ b/source/command-logging-and-monitoring/tests/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/source/command-logging-and-monitoring/tests/monitoring/updateOne.yml b/source/command-logging-and-monitoring/tests/monitoring/updateOne.yml new file mode 100644 index 0000000000..7abc7bae00 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/updateOne.yml @@ -0,0 +1,118 @@ +description: "updateOne" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + ordered: true + commandName: update + databaseName: *databaseName + - 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: *collectionName + updates: + - q: { _id: 4 } + u: { $inc: { x: 1 } } + upsert: true + multi: { $$unsetOrMatches: false } + ordered: true + commandName: update + databaseName: *databaseName + - 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $unsupported: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + ordered: true + commandName: update + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 0 + # The legacy version of this test included an assertion that writeErrors contained a single document + # with index=0, a "code" value, and a non-empty "errmsg". However, writeErrors can contain extra fields + # beyond these, and the unified format currently does not permit allowing extra fields in sub-documents, + # so those assertions are not present here. + writeErrors: { $$type: array } + commandName: update diff --git a/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.json b/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.json new file mode 100644 index 0000000000..455e5422b7 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.json @@ -0,0 +1,155 @@ +{ + "description": "writeConcernError", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "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" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91 + } + } + } + } + }, + { + "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, + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91 + } + }, + "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/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.yml b/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.yml new file mode 100644 index 0000000000..b63db8eef7 --- /dev/null +++ b/source/command-logging-and-monitoring/tests/monitoring/writeConcernError.yml @@ -0,0 +1,81 @@ +description: "writeConcernError" +schemaVersion: "1.4" +runOnRequirements: + - + minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: + - replicaset + serverless: "forbid" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName command-monitoring-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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 ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 # ShutdownInProgress + - name: insertOne + object: *collection + arguments: + document: { _id: 2, x: 22 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2, x: 22 } + ordered: true + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 1 + errorLabels: [ "RetryableWriteError" ] + writeConcernError: { code: 91 } + commandName: insert + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2, x: 22 } + ordered: true + commandName: insert + databaseName: *databaseName + - commandSucceededEvent: + reply: + ok: 1 + n: 1 + commandName: insert diff --git a/source/command-monitoring/command-monitoring.rst b/source/command-monitoring/command-monitoring.rst deleted file mode 100644 index 65b697a8c9..0000000000 --- a/source/command-monitoring/command-monitoring.rst +++ /dev/null @@ -1,448 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -================== -Command Monitoring -================== - -:Spec: 200 -:Title: Command Monitoring -:Authors: Durran Jordan -:Status: Approved -:Type: Standards -:Minimum Server Version: 2.4 -:Last Modified: Apr 16, 2018 -:Version: 1.7 - -.. contents:: - --------- - -Specification -============= - -The performance monitoring specification defines a set of behaviour in the drivers for providing runtime information about commands to any 3rd party APM library as well internal driver use, such as logging. - -This specification intends to abstract the wire protocol so listeners receive similar events no matter what version of MongoDB the driver is connected to. - ------------ -Definitions ------------ - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. - - -Terms ------ - -Document - The term ``Document`` refers to the implementation in the driver's language of a BSON document. - -GLE - The term "GLE" refers to a ``getLastError`` command. - - --------- -Guidance --------- - -Documentation -------------- - -The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation for the driver. - - -Operations ----------- - -All drivers MUST implement the API. Implementation details are noted in the comments when a specific implementation is required. Within each API, all methods are REQUIRED unless noted otherwise in the comments. - - -Operation Parameters --------------------- - -All drivers MUST include the specified parameters in each operation, with the exception of the options parameter which is OPTIONAL. - - -Naming ------- - -All drivers MUST name operations, parameters and topic names as defined in the following sections. Exceptions to this rule are noted in the appropriate section. Class and interface names may vary according to the driver and language best practices. - - -Publishing & Subscribing ------------------------- - -The driver SHOULD publish events in a manner that is standard to the driver's language publish/subscribe patterns and is not strictly mandated in this specification. - - -Guarantees ----------- - -The driver MUST guarantee that every ``CommandStartedEvent`` has either a correlating ``CommandSucceededEvent`` or ``CommandFailedEvent``. - -The driver MUST guarantee that the ``requestId`` of the ``CommandStartedEvent`` and the corresponding ``CommandSucceededEvent`` or ``CommandFailedEvent`` is the same. In the case of acknowledged writes on server version 2.4 [where the driver sends an operation followed by a ``getLastError``] the ``requestId`` MUST be either the ``requestId`` from the original message or the ``requestId`` from the ``getLastError``. - -Unacknowledged/Acknowledged Writes ----------------------------------- - -For server versions that do not support write commands, the driver MUST treat an acknowledged write as a single command event, where the GLE command is ignored as a started event and the response to the GLE is treated as the reply in the ``CommandSucceededEvent``. Unacknowledged writes must provide a ``CommandSucceededEvent`` with a ``{ ok: 1 }`` reply. - -A non-default write concern MUST be included in the published command. The default write concern is not required to be included. - -Succeeded or Failed -------------------- - -Commands that executed on the server and return a status of ``{ ok: 1.0 }`` are considered -successful commands and MUST fire a ``CommandSucceededEvent``. Commands that have write errors -are included since the actual command did succeed, only writes failed. - -``CommandSucceededEvent`` and ``CommandFailedEvent`` events MUST have a ``requestId`` that matches their -originating ``CommandStartedEvent``. - -Error Handling --------------- - -If an exception occurs while sending the operation to the server, the driver MUST generate a ``CommandFailedEvent`` with the exception or message and re-raise the exception. - -Upconversion ------------- - -All legacy operations MUST be converted to their equivalent commands in the 3.2 server in the event's -``command`` and ``reply`` fields. This includes OP_INSERT, OP_DELETE, OP_UPDATE, OP_QUERY, OP_GET_MORE and -OP_KILL_CURSORS. Upconversion expectations are provided in the tests. - -For cases where the upconverted commands would exceed the server's ``maxBsonObjectSize``, the driver MUST NOT -split the upconverted commands and leave the original upconversion intact. - -Bulk Writes ------------ - -This specification defines the monitoring of individual commands and in that respect MUST generate -an event for each command a bulk write executes. Each of these commands, however, must be linked -together via the same ``operationId``. - -Implementation Notes --------------------- - -Legacy wire protocol messages MUST be up-converted to the corresponding commands in order to ensure -that the data in the events follows the same format across all server versions. The provided tests -assert these conversions take place. - -.. list-table:: - :header-rows: 1 - :widths: 50 50 - - * - Legacy Message - - Upconverted Command - - * - ``OP_QUERY`` - - find command - - * - ``OP_QUERY`` with ``$explain`` - - explain command - - * - ``OP_QUERY`` to ``$cmd`` collection - - command - - * - ``OP_GET_MORE`` - - getMore command - - * - ``OP_KILL_CURSORS`` - - killCursors command - - * - ``OP_INSERT`` - - insert command - - * - ``OP_UPDATE`` - - update command - - * - ``OP_DELETE`` - - delete command - -When a driver sends an OP_MSG with a document sequence, it MUST include the document sequence as a BSON array in CommandStartedEvent.command. The array's field name MUST be the OP_MSG sequence identifier. For example, if the driver sends an "update" command using OP_MSG, and sends a document sequence as a separate section of payload type 1 with identifier "updates", the driver MUST include the documents as a BSON array in CommandStartedEvent.command with field name "updates". - -When a driver receives an OP_MSG with a document sequence, it MUST include the document sequence as a BSON array in CommandSucceededEvent.reply. The array's field name MUST be the OP_MSG sequence identifier. For example, if the driver receives an OP_MSG reply to a "find" command with a section of payload type 1 with identifier "cursor.firstBatch", it MUST include the documents as a BSON array in CommandSucceededEvent.reply like: - -.. code:: javascript - - { - cursor: { - firstBatch: [ { document 1 }, { document 2 }, ... ] - } - } - -A document sequence in an OP_MSG reply to "aggregate" or "getMore" MUST be similarly included in CommandSucceededEvent.reply. - -See "Why are document sequences included as BSON arrays?" in the `rationale`_. - -Read Preference -^^^^^^^^^^^^^^^ - -In cases where queries or commands are embedded in a ``$query`` parameter when a read preference -is provided, they MUST be unwrapped and the value of the ``$query`` attribute becomes the -``filter`` or the command in the started event. The read preference will subsequently be dropped -as it is considered metadata and metadata is not currently provided in the command events. - ---------- -Rationale ---------- - -*1. Why does the specification treat all events as commands, even those that are not sent as such?* - -As a public facing API, subscribers to the events should need no knowledge of the MongoDB wire -protocol or variations in messages depending on server versions. The core motivation behind the -specification was to eliminate changes in our drivers' implementations breaking third party APM -solutions. Providing a unified view of operations satisfies this requirement. - -*2. Why are commands with* ``{ ok: 1 }`` *treated as successful and* ``{ ok: 0 }`` *as failed?* - -The specification is consistent with what the server deems as a successful or failed command and -reports this as so. This also allows for server changes around this behaviour in the future to -require no change in the drivers to continue to be compliant. - -The command listener API is responsible only for receiving and handling events sent from the lowest -level of the driver, and is only about informing listeners about what commands are sent and what -replies are received. As such, it would be innappropiate at this level for a driver to execute -custom logic around particular commands to determine what failure or success means for a particular -command. Implementators of the API are free to handle these events as they see fit, which may include -code that futher interprets replies to specific commands based on the presence or absence of other -fields in the reply beyond the ‘ok’ field. - -*3. Why are document sequences included as BSON arrays?* - -The OP_MSG wire protocol was introduced in MongoDB 3.6, with document sequences as an optimization for bulk writes. The same optimization will be introduced for "find" and "aggregate" replies in 3.8. We have chosen to represent these OP_MSGs as single command or reply documents for now, until a need for a more accurate (and perhaps better-performing) command monitoring API for document sequences has been demonstrated. - -*4. Why is BSON serialization and deserialization optional to include in durations?* - -Different drivers will serialize and deserialize BSON at different levels of -the driver architecture. For example, some parts of a command (e.g. inserted -document structs) could be pre-encoded early into a "raw" BSON form and the -final command with late additions like a session ID could encoded just before -putting it on the wire. - -Rather than specify a duration rule that would be hard to satisfy consistently, -we allow duration to include BSON serialization/deserialization or not based on -the architecture needs of each driver. - --------- -Security --------- - -Some commands and replies will contain sensitive data and in order to not risk the leaking of this -data to external sources or logs their commands AND replies MUST be redacted from the events. The -value MUST be replaced with an empty BSON document. The list is as follows: - -.. list-table:: - :header-rows: 1 - :widths: 50 - - * - Command - * - ``authenticate`` - * - ``saslStart`` - * - ``saslContinue`` - * - ``getnonce`` - * - ``createUser`` - * - ``updateUser`` - * - ``copydbgetnonce`` - * - ``copydbsaslstart`` - * - ``copydb`` - ---- -API ---- - -.. code:: typescript - - interface CommandStartedEvent { - - /** - * Returns the command. - */ - command: Document; - - /** - * Returns the database name. - */ - databaseName: String; - - /** - * Returns the command name. - */ - commandName: String; - - /** - * Returns the driver generated request id. - */ - requestId: Int64; - - /** - * Returns the driver generated operation id. This is used to link events together such - * as bulk write operations. OPTIONAL. - */ - operationId: Int64; - - /** - * Returns the connection id for the command. For languages that do not have this, - * this MUST return the driver equivalent which MUST include the server address and port. - * The name of this field is flexible to match the object that is returned from the driver. - */ - connectionId: ConnectionId; - } - - interface CommandSucceededEvent { - - /** - * Returns the execution time of the event in the highest possible resolution for the platform. - * The calculated value MUST be the time to send the message and receive the reply from the server - * and MAY include BSON serialization and/or deserialization. The name can imply the units in which the - * value is returned, i.e. durationMS, durationNanos. - */ - duration: Int64; - - /** - * Returns the command reply. - */ - reply: Document; - - /** - * Returns the command name. - */ - commandName: String; - - /** - * Returns the driver generated request id. - */ - requestId: Int64; - - /** - * Returns the driver generated operation id. This is used to link events together such - * as bulk write operations. OPTIONAL. - */ - operationId: Int64; - - /** - * Returns the connection id for the command. For languages that do not have this, - * this MUST return the driver equivalent which MUST include the server address and port. - * The name of this field is flexible to match the object that is returned from the driver. - */ - connectionId: ConnectionId; - } - - interface CommandFailedEvent { - - /** - * Returns the execution time of the event in the highest possible resolution for the platform. - * The calculated value MUST be the time to send the message and receive the reply from the server - * and MAY include BSON serialization and/or deserialization. The name can imply the units in which the - * value is returned, i.e. durationMS, durationNanos. - */ - duration: Int64; - - /** - * Returns the command name. - */ - commandName: String; - - /** - * Returns the failure. Based on the language, this SHOULD be a message string, exception - * object, or error document. - */ - failure: String,Exception,Document; - - /** - * Returns the client generated request id. - */ - requestId: Int64; - - /** - * Returns the driver generated operation id. This is used to link events together such - * as bulk write operations. OPTIONAL. - */ - operationId: Int64; - - /** - * Returns the connection id for the command. For languages that do not have this, - * this MUST return the driver equivalent which MUST include the server address and port. - * The name of this field is flexible to match the object that is returned from the driver. - */ - connectionId: ConnectionId; - } - - --------- -Examples --------- - -A Ruby subscriber to a query series and how it could handle it with respect to logging. - -Ruby: - -.. code:: ruby - - class LoggingSubscriber - def logger - Logger.new(STDERR) - end - - def started(event) - logger.info("COMMAND.#{event.command_name} #{event.address} STARTED: #{event.command.inspect}") - end - - def succeeded(event) - logger.info("COMMAND.#{event.command_name} #{event.address} COMPLETED: #{event.reply.inspect} (#{event.duration}s)") - end - - def failed(event) - logger.info("COMMAND.#{event.command_name} #{event.address} FAILED: #{event.message.inspect}: #{event.failure.inspect} (#{event.duration}s)") - end - end - - subscriber = LoggingSubscriber.new - Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, subscriber) - - # When the subscriber handles the events the log could show: - # COMMAND.query 127.0.0.1:27017 STARTED: { $query: { name: 'testing' }} - # COMMAND.query 127.0.0.1:27017 COMPLETED: { number_returned: 50 } (0.050s) - - -------- -Testing -------- - -See the README in the test directory for requirements and guidance. - - -Changelog -========= - -22 NOV 2015: - - Specify how to merge OP_MSG document sequences into command-started events. - -16 SEP 2015: - - Removed ``limit`` from find test with options to support 3.2. - - Changed find test read preference to ``primaryPreferred``. - -1 OCT 2015: - - Changed find test with a kill cursors to not run on server versions greater than 3.0 - - Added a find test with no kill cursors command which only runs on 3.1 and higher. - - Added notes on which tests should run based on server versions. - -19 OCT 2015: - - Changed batchSize in the 3.2 find tests to expect the remaining value. - -31 OCT 2015: - - Changed find test on 3.1 and higher to ignore being run on sharded clusters. - -29 MAR 2016: - - Added note on guarantee of the request ids. - -2 NOV 2016: - - Added clause for not upconverting commands larger than maxBsonSize. - -16 APR 2018: - - Made inclusion of BSON serialization/deserialization in command durations - to be optional. diff --git a/source/command-monitoring/tests/README.rst b/source/command-monitoring/tests/README.rst deleted file mode 100644 index a129d3bee6..0000000000 --- a/source/command-monitoring/tests/README.rst +++ /dev/null @@ -1,122 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -================== -Command Monitoring -================== - -.. contents:: - --------- - -Testing -======= - -Tests are provided in YML and JSON format to assert proper upconversion of commands. - -Database and Collection Names ------------------------------ - -The collection under test is specified in each test file with the fields -``database_name`` and ``collection_name``. - -Data ----- - -The ``data`` at the beginning of each test file is the data that should exist in the -collection under test before each test run. - -Expectations ------------- - -Fake Placeholder Values -``````````````````````` - -When an attribute in an expectation contains the value ``"42"``, ``42`` or ``""``, this is a fake -placeholder value indicating that a special case MUST be tested that could not be -expressed in a YAML or JSON test. These cases are as follows: - -Cursor Matching -^^^^^^^^^^^^^^^ - -When encountering a ``cursor`` or ``getMore`` value of ``"42"`` in a test, the driver MUST assert -that the values are equal to each other and greater than zero. - -Errors -^^^^^^ - -For write errors, ``code`` values of ``42`` MUST assert that the value is present and -greater than zero. ``errmsg`` values of ``""`` MUST assert that the value is not empty -(a string of length greater than 1). - -OK Values -^^^^^^^^^ - -The server is inconsistent on whether the ok values returned are integers or doubles so -for simplicity the tests specify all expected values as doubles. Server 'ok' values of -integers MUST be converted to doubles for comparison with the expected values. - -Additional Values -````````````````` - -The expected events provide the minimum data that is required and can be tested. It is -possible for more values to be present in the events, such as extra data provided when -using sharded clusters or ``nModified`` field in updates. The driver MUST assert the -expected data is present and also MUST allow for additional data to be present as well -at the top level of the command document or reply document. - -For example, say the client sends a causally-consistent "distinct" command with -readConcern level "majority", like:: - - { - "distinct": "collection", - "key": "key", - "readConcern":{ - "afterClusterTime": {"$timestamp":{"t":1522336030,"i":1}}, - "level":"majority" - }, - "$clusterTime": { - "clusterTime": { "$timestamp": { "i": 1, "t": 1522335530 } }, - "signature": { - "hash": { "$binary": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "$type": "00" }, - "keyId": { "$numberLong": "0" } - } - }, - "lsid": { - "id": { "$binary": "RaigP3oASqu+galPvRAfcg==", "$type": "04" } - } - } - -Then it would pass a command-started event like the following YAML, because the -fields not mentioned in the YAML are ignored:: - - command: - distinct: collection - key: key - -However, if there are fields in command subdocuments that are not mentioned in -the YAML, then the command does *not* pass the test:: - - command: - distinct: collection - key: key - # Fails because the expected readConcern has no "afterClusterTime". - readConcern: - level: majority - -Ignoring Tests Based On Server Version -`````````````````````````````````````` - -Due to variations in server behaviour, some tests may not be valid on a specific range -of server versions and MUST NOT be run. These tests are indicated with the fields -``ignore_if_server_version_greater_than`` and ``ignore_if_server_version_less_than`` which -are optionally provided at the description level of the test. When determining if the test -must be run or not, use only the minor server version. - -Example: - -If ``ignore_if_server_version_greater_than`` is ``3.0``, then the tests MUST NOT run on -``3.1`` and higher, but MUST run on ``3.0.3``. - -Tests which do not have either one of these fields MUST run on all supported server -versions. diff --git a/source/command-monitoring/tests/bulkWrite.json b/source/command-monitoring/tests/bulkWrite.json deleted file mode 100644 index c5cd5a2399..0000000000 --- a/source/command-monitoring/tests/bulkWrite.json +++ /dev/null @@ -1,112 +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 - } - }, - "upsert": false, - "multi": false - } - ], - "ordered": true - }, - "command_name": "update", - "database_name": "command-monitoring-tests" - } - }, - { - "command_succeeded_event": { - "reply": { - "ok": 1, - "n": 1 - }, - "command_name": "update" - } - } - ] - } - ] -} diff --git a/source/command-monitoring/tests/bulkWrite.yml b/source/command-monitoring/tests/bulkWrite.yml deleted file mode 100644 index eec361d0a3..0000000000 --- a/source/command-monitoring/tests/bulkWrite.yml +++ /dev/null @@ -1,49 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - documents: - - { _id: 4, x: 44 } - ordered: true - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, n: 1 } - command_name: "insert" - - - command_started_event: - command: - update: *collection_name - updates: - - { q: {_id: 3 }, u: { $set: { x: 333 } }, upsert: false, multi: false } - ordered: true - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, n: 1 } - command_name: "update" diff --git a/source/command-monitoring/tests/command.json b/source/command-monitoring/tests/command.json deleted file mode 100644 index 7e1e347be0..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/command.yml b/source/command-monitoring/tests/command.yml deleted file mode 100644 index 62da8a2a10..0000000000 --- a/source/command-monitoring/tests/command.yml +++ /dev/null @@ -1,61 +0,0 @@ -data: - - { _id: 1, x: 11 } - -collection_name: &collection_name "test" -database_name: &database_name "command-monitoring-tests" - -tests: - - - description: "A successful command" - operation: - name: "count" - arguments: - filter: { _id: 1 } - expectations: - - - command_started_event: - command: - count: *collection_name - query: { _id: 1 } - command_name: "count" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, n: 1 } - command_name: "count" - - - description: "A failed command event" - operation: - name: "count" - arguments: - filter: { $or: true } - expectations: - - - command_started_event: - command: - count: *collection_name - query: { $or: true } - command_name: "count" - database_name: *database_name - - - 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: *collection_name - query: { _id: 1 } - command_name: "count" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, n: 1 } - command_name: "count" diff --git a/source/command-monitoring/tests/deleteMany.json b/source/command-monitoring/tests/deleteMany.json deleted file mode 100644 index 7cd396806c..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/deleteMany.yml b/source/command-monitoring/tests/deleteMany.yml deleted file mode 100644 index 8658e1b063..0000000000 --- a/source/command-monitoring/tests/deleteMany.yml +++ /dev/null @@ -1,55 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - deletes: - - { q: { _id: { $gt: 1 }}, limit: 0 } - ordered: true - command_name: "delete" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - deletes: - - { q: { _id: { $nothing: 1 }}, limit: 0 } - ordered: true - command_name: "delete" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - n: 0 - writeErrors: - - { index: 0, code: 42, errmsg: "" } - command_name: "delete" diff --git a/source/command-monitoring/tests/deleteOne.json b/source/command-monitoring/tests/deleteOne.json deleted file mode 100644 index 0971dfcf2c..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/deleteOne.yml b/source/command-monitoring/tests/deleteOne.yml deleted file mode 100644 index 953a552ca1..0000000000 --- a/source/command-monitoring/tests/deleteOne.yml +++ /dev/null @@ -1,55 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - deletes: - - { q: { _id: { $gt: 1 }}, limit: 1 } - ordered: true - command_name: "delete" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - deletes: - - { q: { _id: { $nothing: 1 }}, limit: 1 } - ordered: true - command_name: "delete" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - n: 0 - writeErrors: - - { index: 0, code: 42, errmsg: "" } - command_name: "delete" diff --git a/source/command-monitoring/tests/find.json b/source/command-monitoring/tests/find.json deleted file mode 100644 index 039c5fead1..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/find.yml b/source/command-monitoring/tests/find.yml deleted file mode 100644 index dea5194daf..0000000000 --- a/source/command-monitoring/tests/find.yml +++ /dev/null @@ -1,266 +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: &collection_name "test" -database_name: &database_name "command-monitoring-tests" -namespace: &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: *collection_name - filter: { _id: 1 } - command_name: "find" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "0" } - ns: *namespace - 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: *collection_name - 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: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "0" } - ns: *namespace - 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: *collection_name - filter: { _id: { $gte : 1 }} - sort: { _id: 1 } - batchSize: {"$numberLong": "3"} - command_name: "find" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "42" } - ns: *namespace - firstBatch: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - command_name: "find" - - - command_started_event: - command: - getMore: { $numberLong: "42" } - collection: *collection_name - batchSize: {"$numberLong": "3"} - command_name: "getMore" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "0" } - ns: *namespace - 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: *collection_name - filter: { _id: { $gte : 1 }} - sort: { _id: 1 } - batchSize: {"$numberLong": "3"} - limit: {"$numberLong": "4"} - command_name: "find" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "42" } - ns: *namespace - firstBatch: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - command_name: "find" - - - command_started_event: - command: - getMore: { $numberLong: "42" } - collection: *collection_name - batchSize: { $numberLong: "1" } - command_name: "getMore" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "42" } - ns: *namespace - nextBatch: - - { _id: 4, x: 44 } - command_name: "getMore" - - - command_started_event: - command: - killCursors: *collection_name - cursors: - - { $numberLong: "42" } - command_name: "killCursors" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - 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: *collection_name - filter: { _id: { $gte : 1 }} - sort: { _id: 1 } - batchSize: {"$numberLong": "3"} - limit: {"$numberLong": "4"} - command_name: "find" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "42" } - ns: *namespace - firstBatch: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - command_name: "find" - - - command_started_event: - command: - getMore: { $numberLong: "42" } - collection: *collection_name - batchSize: { $numberLong: "1" } - command_name: "getMore" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - cursor: - id: { $numberLong: "0" } - ns: *namespace - 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: *collection_name - filter: { $or: true } - command_name: "find" - database_name: *database_name - - - command_failed_event: - command_name: "find" diff --git a/source/command-monitoring/tests/insertMany.json b/source/command-monitoring/tests/insertMany.json deleted file mode 100644 index 0becf928e4..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/insertMany.yml b/source/command-monitoring/tests/insertMany.yml deleted file mode 100644 index 8c6dc1603a..0000000000 --- a/source/command-monitoring/tests/insertMany.yml +++ /dev/null @@ -1,75 +0,0 @@ -data: - - { _id: 1, x: 11 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - documents: - - { _id: 2, x: 22 } - ordered: true - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - documents: - - { _id: 1, x: 11 } - ordered: true - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - 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: *collection_name - documents: - - { _id: 2, x: 22 } - ordered: false - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, n: 1 } - command_name: "insert" diff --git a/source/command-monitoring/tests/insertOne.json b/source/command-monitoring/tests/insertOne.json deleted file mode 100644 index 877bca1a61..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/insertOne.yml b/source/command-monitoring/tests/insertOne.yml deleted file mode 100644 index 6d34eef804..0000000000 --- a/source/command-monitoring/tests/insertOne.yml +++ /dev/null @@ -1,51 +0,0 @@ -data: - - { _id: 1, x: 11 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - documents: - - { _id: 2, x: 22 } - ordered: true - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - documents: - - { _id: 1, x: 11 } - ordered: true - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - n: 0 - writeErrors: - - { index: 0, code: 42, errmsg: "" } - command_name: "insert" diff --git a/source/command-monitoring/tests/unacknowledgedBulkWrite.json b/source/command-monitoring/tests/unacknowledgedBulkWrite.json deleted file mode 100644 index ae116289eb..0000000000 --- a/source/command-monitoring/tests/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/source/command-monitoring/tests/unacknowledgedBulkWrite.yml b/source/command-monitoring/tests/unacknowledgedBulkWrite.yml deleted file mode 100644 index faf7852050..0000000000 --- a/source/command-monitoring/tests/unacknowledgedBulkWrite.yml +++ /dev/null @@ -1,35 +0,0 @@ -data: - - { _id: 1, x: 11 } - -collection_name: &collection_name "test-unacknowledged-bulk-write" -database_name: &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: *collection_name - documents: - - { _id: "unorderedBulkWriteInsertW0", x: 44 } - ordered: false - writeConcern: { w: 0 } - command_name: "insert" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0 } - command_name: "insert" diff --git a/source/command-monitoring/tests/updateMany.json b/source/command-monitoring/tests/updateMany.json deleted file mode 100644 index 8e98fc92fd..0000000000 --- a/source/command-monitoring/tests/updateMany.json +++ /dev/null @@ -1,137 +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, - "upsert": false - } - ] - }, - "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, - "upsert": false - } - ] - }, - "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/source/command-monitoring/tests/updateMany.yml b/source/command-monitoring/tests/updateMany.yml deleted file mode 100644 index c09602ce36..0000000000 --- a/source/command-monitoring/tests/updateMany.yml +++ /dev/null @@ -1,67 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - ordered: true - updates: - - - q: { _id: { $gt: 1 }} - u: { $inc: { x: 1 }} - multi: true - upsert: false - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - ordered: true - updates: - - - q: { _id: { $gt: 1 }} - u: { $nothing: { x: 1 }} - multi: true - upsert: false - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - n: 0 - writeErrors: - - { index: 0, code: 42, errmsg: "" } - command_name: "update" diff --git a/source/command-monitoring/tests/updateOne.json b/source/command-monitoring/tests/updateOne.json deleted file mode 100644 index 565b749704..0000000000 --- a/source/command-monitoring/tests/updateOne.json +++ /dev/null @@ -1,195 +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 - } - }, - "multi": false, - "upsert": false - } - ] - }, - "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 - } - }, - "multi": false, - "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 - } - }, - "multi": false, - "upsert": false - } - ] - }, - "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/source/command-monitoring/tests/updateOne.yml b/source/command-monitoring/tests/updateOne.yml deleted file mode 100644 index d9fe28921f..0000000000 --- a/source/command-monitoring/tests/updateOne.yml +++ /dev/null @@ -1,95 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - -collection_name: &collection_name "test" -database_name: &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: *collection_name - ordered: true - updates: - - - q: { _id: { $gt: 1 }} - u: { $inc: { x: 1 }} - multi: false - upsert: false - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - ordered: true - updates: - - - q: { _id: 4 } - u: { $inc: { x: 1 } } - multi: false - upsert: true - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: { ok: 1.0, 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: *collection_name - ordered: true - updates: - - - q: { _id: { $gt: 1 }} - u: { $nothing: { x: 1 }} - multi: false - upsert: false - command_name: "update" - database_name: *database_name - - - command_succeeded_event: - reply: - ok: 1.0 - n: 0 - writeErrors: - - { index: 0, code: 42, errmsg: "" } - command_name: "update" diff --git a/source/compression/OP_COMPRESSED.md b/source/compression/OP_COMPRESSED.md new file mode 100644 index 0000000000..67c4f920ba --- /dev/null +++ b/source/compression/OP_COMPRESSED.md @@ -0,0 +1,431 @@ +# Wire Compression in Drivers + +- Status: Accepted + +- Minimum Server Version: 3.4 + +## Abstract + +This specification describes how to implement Wire Protocol Compression between MongoDB drivers and mongo[d|s]. + +Compression is achieved through a new bi-directional wire protocol opcode, referred to as `OP_COMPRESSED`. + +Server side compressor support is checked during the initial MongoDB Handshake, and is compatible with all historical +versions of MongoDB. If a client detects a compatible compressor it will use the compressor for all valid requests. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Terms + +*Compressor* - A compression algorithm. There are currently three supported algorithms: snappy, zlib, and zstd. + +## Specification + +### MongoDB Handshake Amendment + +The [MongoDB Handshake Protocol](../mongodb-handshake/handshake.md) describes an argument passed to the handshake +command, `client`. This specification adds an additional argument, `compression`, that SHOULD be provided to the +handshake command if a driver supports wire compression. + +The value of this argument is an array of supported compressors. + +Example: + +```javascript +{ + hello: 1, + client: {}, /* See MongoDB Handshake */ + compression: ["snappy", "zlib", "zstd"] +} +``` + +When no compression is enabled on the client, drivers SHOULD send an empty compression argument. + +Example: + +```javascript +{ + hello: 1, + client: {}, /* See MongoDB Handshake */ + compression: [] +} +``` + +Clients that want to compress their messages need to send a list of the algorithms - in the order they are specified in +the client configuration - that they are willing to support to the server during the initial handshake call. For +example, a client wishing to compress with the snappy algorithm, should send: + +```javascript +{ hello: 1, ... , compression: [ "snappy" ] } +``` + +The server will respond with the intersection of its list of supported compressors and the client's. For example, if the +server had both snappy and zlib enabled and the client requested snappy, it would respond with: + +```javascript +{ ... , compression: [ "snappy" ], ok: 1 } +``` + +If the client included both snappy and zlib, the server would respond with something like: + +```javascript +{ ... , compression: [ "snappy", "zlib" ], ok: 1 } +``` + +If the server has no compression algorithms in common with the client, it sends back a handshake response without a +compression field. Clients MAY issue a log level event to inform the user, but MUST NOT error. + +When MongoDB server receives a compressor it can support it MAY reply to any and all requests using the selected +compressor, including the reply of the initial MongoDB Handshake. As each `OP_COMPRESSED` message contains the +compressor ID, clients MUST NOT assume which compressor each message uses, but MUST decompress the message using the +compressor identified in the `OP_COMPRESSED` opcode header. + +When compressing, clients MUST use the first compressor in the client's configured compressors list that is also in the +servers list. + +### Connection String Options + +Two new connection string options: + +#### compressors + +Comma separated list of compressors the client should present to the server. Unknown compressors MUST yield a warning, +as per the Connection String specification, and MUST NOT be included in the handshake. By default, no compressor is +configured and thus compression will not be used. When multiple compressors are provided, the list should be treated as +a priority ordered list of compressors to use, with highest priority given to the first compressor in the list, and +lowest priority to the last compressor in the list. + +Example: + +```text +mongodb://localhost/?compressors=zstd,snappy,zlib +``` + +#### zlibCompressionLevel + +Integer value from `-1` - `9`. This configuration option only applies to the zlib compressor. When zlib is not one of +the compressors enumerated by the `compressors` configuration option then providing this option has no meaning, but +clients SHOULD NOT issue a warning. + +| Level | Description | +| ----- | ------------------------------- | +| -1 | Default Compression (usually 6) | +| 0 | No compression | +| 1 | Best Speed | +| 9 | Best Compression | + +Note that this value only applies to the client side compression level, not the response. + +### OP_COMPRESSED + +The new opcode, called `OP_COMPRESSED`, has the following structure: + +```c +struct OP_COMPRESSED { + struct MsgHeader { + int32 messageLength; + int32 requestID; + int32 responseTo; + int32 opCode = 2012; + }; + int32_t originalOpcode; + int32_t uncompressedSize; + uint8_t compressorId; + char *compressedMessage; +}; +``` + +| Field | Description | +| ----------------- | ------------------------------------------------------------------------ | +| originalOpcode | Contains the value of the wrapped opcode. | +| uncompressedSize | The size of the deflated compressedMessage, which excludes the MsgHeader | +| compressorId | The ID of the compressor that compressed the message | +| compressedMessage | The opcode itself, excluding the MsgHeader | + +### Compressor IDs + +Each compressor is assigned a predefined compressor ID. + +| compressorId | Handshake Value | Description | +| ------------ | --------------- | ------------------------------------------------------------------------------------------ | +| 0 | noop | The content of the message is uncompressed.
This is realistically only used for testing | +| 1 | snappy | The content of the message is compressed using snappy. | +| 2 | zlib | The content of the message is compressed using zlib. | +| 3 | zstd | The content of the message is compressed using zstd. | +| 4-255 | reserved | Reserved for future use. | + +### Compressible messages + +Any opcode can be compressed and wrapped in an `OP_COMPRESSED` header. The `OP_COMPRESSED` is strictly a wire protocol +without regards to what opcode it wraps, be it `OP_QUERY`, `OP_REPLY`, `OP_MSG` or any other future or past opcode. The +`compressedMessage` contains the original opcode, excluding the standard `MsgHeader`. The `originalOpcode` value +therefore effectively replaces the standard `MsgHeader` of the compressed opcode. + +There is no guarantee that a response will be compressed even though compression was negotiated for in the handshake. +Clients MUST be able to parse both compressed and uncompressed responses to both compressed and uncompressed requests. + +MongoDB 3.4 will always reply with a compressed response when compression has been negotiated, but future versions may +not. + +A client MAY choose to implement compression for only `OP_QUERY`, `OP_REPLY`, and `OP_MSG`, and perhaps for future +opcodes, but not to implement it for `OP_INSERT`, `OP_UPDATE`, `OP_DELETE`, `OP_GETMORE`, and `OP_KILLCURSORS`. + +Note that certain messages, such as authentication commands, MUST NOT be compressed. All other messages MUST be +compressed, when compression has been negotiated and the driver has implemented compression for the opcode in use. + +### Messages not allowed to be compressed + +In efforts to mitigate against current and previous attacks, certain messages MUST NOT be compressed, such as +authentication requests. + +Messages using the following commands MUST NOT be compressed: + +- `hello` +- legacy hello (see [MongoDB Handshake Protocol](../mongodb-handshake/handshake.md) for details) +- `saslStart` +- `saslContinue` +- `getnonce` +- `authenticate` +- `createUser` +- `updateUser` +- `copydbSaslStart` +- `copydbgetnonce` +- `copydb` + +## Test Plan + +There are no automated tests accompanying this specification, instead the following is a description of test scenarios +clients should implement. + +In general, after implementing this functionality and the test cases, running the traditional client test suite against +a server with compression enabled, and ensuring the test suite is configured to provide a valid compressor as part of +the connection string, is a good idea. MongoDB-supported drivers MUST add such variant to their CI environment. + +The following cases assume a standalone MongoDB 3.4 (or later) node configured with: + +```shell +mongod --networkMessageCompressors "snappy" -vvv +``` + +Create an example application which connects to a provided connection string, runs `ping: 1`, and then quits the program +normally. + +### Connection strings, and results + +- `mongodb://localhost:27017/?compressors=snappy` + + mongod should have logged the following (the exact log output may differ depending on server version): + + ```javascript + {"t":{"$date":"2021-04-08T13:28:38.885-06:00"},"s":"I", "c":"NETWORK", "id":22943, "ctx":"listener","msg":"Connection accepted","attr":{"remote":"127.0.0.1:50635","uuid":"03961627-aec7-4543-8a17-9690f87273a6","connectionId":2,"connectionCount":1}} + {"t":{"$date":"2021-04-08T13:28:38.886-06:00"},"s":"D3", "c":"EXECUTOR", "id":22983, "ctx":"listener","msg":"Starting new executor thread in passthrough mode"} + {"t":{"$date":"2021-04-08T13:28:38.887-06:00"},"s":"D3", "c":"-", "id":5127801, "ctx":"thread27","msg":"Setting the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.887-06:00"},"s":"D2", "c":"COMMAND", "id":21965, "ctx":"conn2","msg":"About to run the command","attr":{"db":"admin","commandArgs":{"hello":1,"client":{"application":{"name":"MongoDB Shell"},"driver":{"name":"MongoDB Internal Client","version":"4.9.0-alpha7-555-g623aa8f"},"os":{"type":"Darwin","name":"Mac OS X","architecture":"x86_64","version":"19.6.0"}},"compression":["snappy"],"apiVersion":"1","apiStrict":true,"$db":"admin"}}} + {"t":{"$date":"2021-04-08T13:28:38.888-06:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn2","msg":"client metadata","attr":{"remote":"127.0.0.1:50635","client":"conn2","doc":{"application":{"name":"MongoDB Shell"},"driver":{"name":"MongoDB Internal Client","version":"4.9.0-alpha7-555-g623aa8f"},"os":{"type":"Darwin","name":"Mac OS X","architecture":"x86_64","version":"19.6.0"}}}} + {"t":{"$date":"2021-04-08T13:28:38.889-06:00"},"s":"D3", "c":"NETWORK", "id":22934, "ctx":"conn2","msg":"Starting server-side compression negotiation"} + {"t":{"$date":"2021-04-08T13:28:38.889-06:00"},"s":"D3", "c":"NETWORK", "id":22937, "ctx":"conn2","msg":"supported compressor","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.889-06:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn2","msg":"Slow query","attr":{"type":"command","ns":"admin.$cmd","appName":"MongoDB Shell","command":{"hello":1,"client":{"application":{"name":"MongoDB Shell"},"driver":{"name":"MongoDB Internal Client","version":"4.9.0-alpha7-555-g623aa8f"},"os":{"type":"Darwin","name":"Mac OS X","architecture":"x86_64","version":"19.6.0"}},"compression":["snappy"],"apiVersion":"1","apiStrict":true,"$db":"admin"},"numYields":0,"reslen":351,"locks":{},"remote":"127.0.0.1:50635","protocol":"op_query","durationMillis":1}} + {"t":{"$date":"2021-04-08T13:28:38.890-06:00"},"s":"D2", "c":"QUERY", "id":22783, "ctx":"conn2","msg":"Received interrupt request for unknown op","attr":{"opId":596,"knownOps":[]}} + {"t":{"$date":"2021-04-08T13:28:38.890-06:00"},"s":"D3", "c":"-", "id":5127803, "ctx":"conn2","msg":"Released the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.890-06:00"},"s":"D3", "c":"-", "id":5127801, "ctx":"conn2","msg":"Setting the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.891-06:00"},"s":"D3", "c":"NETWORK", "id":22927, "ctx":"conn2","msg":"Decompressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.891-06:00"},"s":"D2", "c":"COMMAND", "id":21965, "ctx":"conn2","msg":"About to run the command","attr":{"db":"admin","commandArgs":{"whatsmyuri":1,"apiStrict":false,"$db":"admin","apiVersion":"1"}}} + {"t":{"$date":"2021-04-08T13:28:38.892-06:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn2","msg":"Slow query","attr":{"type":"command","ns":"admin.$cmd","appName":"MongoDB Shell","command":{"whatsmyuri":1,"apiStrict":false,"$db":"admin","apiVersion":"1"},"numYields":0,"reslen":63,"locks":{},"remote":"127.0.0.1:50635","protocol":"op_msg","durationMillis":0}} + {"t":{"$date":"2021-04-08T13:28:38.892-06:00"},"s":"D2", "c":"QUERY", "id":22783, "ctx":"conn2","msg":"Received interrupt request for unknown op","attr":{"opId":597,"knownOps":[]}} + {"t":{"$date":"2021-04-08T13:28:38.892-06:00"},"s":"D3", "c":"NETWORK", "id":22925, "ctx":"conn2","msg":"Compressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.893-06:00"},"s":"D3", "c":"-", "id":5127803, "ctx":"conn2","msg":"Released the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.893-06:00"},"s":"D3", "c":"-", "id":5127801, "ctx":"conn2","msg":"Setting the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.895-06:00"},"s":"D3", "c":"NETWORK", "id":22927, "ctx":"conn2","msg":"Decompressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.895-06:00"},"s":"D2", "c":"COMMAND", "id":21965, "ctx":"conn2","msg":"About to run the command","attr":{"db":"admin","commandArgs":{"buildinfo":1.0,"apiStrict":false,"$db":"admin","apiVersion":"1"}}} + {"t":{"$date":"2021-04-08T13:28:38.896-06:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn2","msg":"Slow query","attr":{"type":"command","ns":"admin.$cmd","appName":"MongoDB Shell","command":{"buildinfo":1.0,"apiStrict":false,"$db":"admin","apiVersion":"1"},"numYields":0,"reslen":2606,"locks":{},"remote":"127.0.0.1:50635","protocol":"op_msg","durationMillis":0}} + {"t":{"$date":"2021-04-08T13:28:38.896-06:00"},"s":"D2", "c":"QUERY", "id":22783, "ctx":"conn2","msg":"Received interrupt request for unknown op","attr":{"opId":598,"knownOps":[]}} + {"t":{"$date":"2021-04-08T13:28:38.897-06:00"},"s":"D3", "c":"NETWORK", "id":22925, "ctx":"conn2","msg":"Compressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.897-06:00"},"s":"D3", "c":"-", "id":5127803, "ctx":"conn2","msg":"Released the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.897-06:00"},"s":"D3", "c":"-", "id":5127801, "ctx":"conn2","msg":"Setting the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.898-06:00"},"s":"D3", "c":"NETWORK", "id":22927, "ctx":"conn2","msg":"Decompressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.899-06:00"},"s":"D2", "c":"COMMAND", "id":21965, "ctx":"conn2","msg":"About to run the command","attr":{"db":"admin","commandArgs":{"endSessions":[{"id":{"$uuid":"c4866af5-ed6b-4f01-808b-51a3f8aaaa08"}}],"$db":"admin","apiVersion":"1","apiStrict":true}}} + {"t":{"$date":"2021-04-08T13:28:38.899-06:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn2","msg":"Slow query","attr":{"type":"command","ns":"admin.$cmd","appName":"MongoDB Shell","command":{"endSessions":[{"id":{"$uuid":"c4866af5-ed6b-4f01-808b-51a3f8aaaa08"}}],"$db":"admin","apiVersion":"1","apiStrict":true},"numYields":0,"reslen":38,"locks":{},"remote":"127.0.0.1:50635","protocol":"op_msg","durationMillis":0}} + {"t":{"$date":"2021-04-08T13:28:38.900-06:00"},"s":"D2", "c":"QUERY", "id":22783, "ctx":"conn2","msg":"Received interrupt request for unknown op","attr":{"opId":599,"knownOps":[]}} + {"t":{"$date":"2021-04-08T13:28:38.900-06:00"},"s":"D3", "c":"NETWORK", "id":22925, "ctx":"conn2","msg":"Compressing message","attr":{"compressor":"snappy"}} + {"t":{"$date":"2021-04-08T13:28:38.900-06:00"},"s":"D3", "c":"-", "id":5127803, "ctx":"conn2","msg":"Released the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.901-06:00"},"s":"D3", "c":"-", "id":5127801, "ctx":"conn2","msg":"Setting the Client","attr":{"client":"conn2"}} + {"t":{"$date":"2021-04-08T13:28:38.901-06:00"},"s":"D2", "c":"NETWORK", "id":22986, "ctx":"conn2","msg":"Session from remote encountered a network error during SourceMessage","attr":{"remote":"127.0.0.1:50635","error":{"code":6,"codeName":"HostUnreachable","errmsg":"Connection closed by peer"}}} + {"t":{"$date":"2021-04-08T13:28:38.902-06:00"},"s":"D1", "c":"-", "id":23074, "ctx":"conn2","msg":"User assertion","attr":{"error":"HostUnreachable: Connection closed by peer","file":"src/mongo/transport/service_state_machine.cpp","line":410}} + {"t":{"$date":"2021-04-08T13:28:38.902-06:00"},"s":"W", "c":"EXECUTOR", "id":4910400, "ctx":"conn2","msg":"Terminating session due to error","attr":{"error":{"code":6,"codeName":"HostUnreachable","errmsg":"Connection closed by peer"}}} + {"t":{"$date":"2021-04-08T13:28:38.902-06:00"},"s":"I", "c":"NETWORK", "id":5127900, "ctx":"conn2","msg":"Ending session","attr":{"error":{"code":6,"codeName":"HostUnreachable","errmsg":"Connection closed by peer"}}} + {"t":{"$date":"2021-04-08T13:28:38.903-06:00"},"s":"I", "c":"NETWORK", "id":22944, "ctx":"conn2","msg":"Connection ended","attr":{"remote":"127.0.0.1:50635","uuid":"03961627-aec7-4543-8a17-9690f87273a6","connectionId":2,"connectionCount":0}} + {"t":{"$date":"2021-04-08T13:28:38.903-06:00"},"s":"D3", "c":"-", "id":5127803, "ctx":"conn2","msg":"Released the Client","attr":{"client":"conn2"}} + ``` + + The result of the program should have been: + + ```javascript + { "ok" : 1 } + ``` + +- `mongodb://localhost:27017/?compressors=snoopy` + + mongod should have logged the following: + + ```javascript + {"t":{"$date":"2021-04-20T09:57:26.823-06:00"},"s":"D2", "c":"COMMAND", "id":21965, "ctx":"conn5","msg":"About to run the command","attr":{"db":"admin","commandArgs":{"hello":1,"client":{"driver":{"name":"mongo-csharp-driver","version":"2.12.2.0"},"os":{"type":"macOS","name":"Darwin 19.6.0 Darwin Kernel Version 19.6.0: Tue Jan 12 22:13:05 PST 2021; root:xnu-6153.141.16~1/RELEASE_X86_64","architecture":"x86_64","version":"19.6.0"},"platform":".NET 5.0.3"},"compression":[],"$readPreference":{"mode":"secondaryPreferred"},"$db":"admin"}}} + {"t":{"$date":"2021-04-20T09:57:26.823-06:00"},"s":"I", "c":"NETWORK", "id":51800, "ctx":"conn5","msg":"client metadata","attr":{"remote":"127.0.0.1:54372","client":"conn5","doc":{"driver":{"name":"mongo-csharp-driver","version":"2.12.2.0"},"os":{"type":"macOS","name":"Darwin 19.6.0 Darwin Kernel Version 19.6.0: Tue Jan 12 22:13:05 PST 2021; root:xnu-6153.141.16~1/RELEASE_X86_64","architecture":"x86_64","version":"19.6.0"},"platform":".NET 5.0.3"}}} + {"t":{"$date":"2021-04-20T09:57:26.824-06:00"},"s":"D3", "c":"NETWORK", "id":22934, "ctx":"conn5","msg":"Starting server-side compression negotiation"} + {"t":{"$date":"2021-04-20T09:57:26.824-06:00"},"s":"D3", "c":"NETWORK", "id":22936, "ctx":"conn5","msg":"No compressors provided"} + {"t":{"$date":"2021-04-20T09:57:26.825-06:00"},"s":"I", "c":"COMMAND", "id":51803, "ctx":"conn5","msg":"Slow query","attr":{"type":"command","ns":"admin.$cmd","command":{"hello":1,"client":{"driver":{"name":"mongo-csharp-driver","version":"2.12.2.0"},"os":{"type":"macOS","name":"Darwin 19.6.0 Darwin Kernel Version 19.6.0: Tue Jan 12 22:13:05 PST 2021; root:xnu-6153.141.16~1/RELEASE_X86_64","architecture":"x86_64","version":"19.6.0"},"platform":".NET 5.0.3"},"compression":[],"$readPreference":{"mode":"secondaryPreferred"},"$db":"admin"},"numYields":0,"reslen":319,"locks":{},"remote":"127.0.0.1:54372","protocol":"op_query","durationMillis":1}} + ``` + + e.g., empty compression: [] array. No operations should have been compressed. + + The results of the program should have been: + + ```javascript + WARNING: Unsupported compressor: 'snoopy' + { "ok" : 1 } + ``` + +- `mongodb://localhost:27017/?compressors=snappy,zlib` + + mongod should have logged the following: + + ```javascript + {"t":{"$date":"2021-04-08T13:28:38.898-06:00"},"s":"D3", "c":"NETWORK", "id":22927, "ctx":"conn2","msg":"Decompressing message","attr":{"compressor":"snappy"}} + ``` + + The results of the program should have been: + + ```javascript + { "ok" : 1 } + ``` + +- `mongodb://localhost:27017/?compressors=zlib,snappy` + + mongod should have logged the following: + + ```javascript + {"t":{"$date":"2021-04-08T13:28:38.898-06:00"},"s":"D3", "c":"NETWORK", "id":22927, "ctx":"conn2","msg":"Decompressing message","attr":{"compressor":"zlib"}} + ``` + + The results of the program should have been: + + ```javascript + { "ok" : 1 } + ``` + +- Create example program that authenticates to the server using SCRAM-SHA-1, then creates another user (MONGODB-CR), + then runs `hello` followed with `serverStatus`. + +- Reconnect to the same server using the created MONGODB-CR credentials. Observe that the only command that was + decompressed on the server was `serverStatus`, while the server replied with `OP_COMPRESSED` for at least the + `serverStatus` command. + +## Motivation For Change + +Drivers provide the canonical interface to MongoDB. Most tools for MongoDB are written with the aid of MongoDB drivers. +There exist a lot of tools for MongoDB that import massive datasets which could stand to gain a lot from compression. +Even day-to-day applications stand to gain from reduced bandwidth utilization at low cpu costs, especially when doing +large reads off the network. + +Not all use cases fit compression, but we will allow users to decide if wire compression is right for them. + +## Design rationale + +Snappy has minimal cost and provides a reasonable compression ratio, but it is not expected to be available for all +languages MongoDB Drivers support. Supporting zlib is therefore important to the ecosystem, but for languages that do +support snappy we expected it to be the default choice. While snappy has no knobs to tune, zlib does have support for +specifying the compression level (tuned from speed to compression). As we don’t anticipate adding support for +compression libraries with complex knobs to tune this specification has opted not to define a complex configuration +structure and only define the currently relevant `zlibCompressionLevel`. When other compression libraries are supported, +adding support for configuring that library (if any is needed) should be handled on a case by case basis. + +More recently, the MongoDB server added Zstandard (zstd) support for another modern alternative to zlib. + +## Backwards Compatibility + +The new argument provided to the MongoDB Handshake has no backwards compatible implications as servers that do not +expect it will simply ignore it. This means a server will therefore never reply with a list of acceptable compressors +which in turns means a client CANNOT use the `OP_COMPRESSED` opcode. + +## Reference Implementation + +- [mongoc](https://jira.mongodb.org/browse/CDRIVER-2116) + +## Future Work + +Possible future improvements include defining an API to determine compressor and configuration per operation, rather +than needing to create two different client pools, one for compression and one without, when the user is expecting only +needing to (not) compress very few operations. + +## Q & A + +- Statistics? + + - See [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus/) in the server + +- How to try this/enable it? + + - `mongod --networkMessageCompressors "snappy"` + +- The server MAY reply with compressed data even if the request was not compressed? + + - Yes, and this is in fact the behaviour of MongoDB 3.4 + +- Can drivers compress the initial MongoDB Handshake/hello request? + + - No. + +- Can the server reply to the MongoDB Handshake/hello be compressed? + + - Yes, yes it can. Be aware it is completely acceptable for the server to use compression for any and all replies, + using any supported compressor, when the client announced support for compression - this includes the reply to the + actual MongoDB Handshake/`hello` where the support was announced. + +- This is billed a MongoDB 3.6 feature -- but I hear it works with MongoDB 3.4? + + - Yes, it does. All MongoDB versions support the `compression` argument to the initial handshake and all MongoDB + versions will reply with an intersection of compressors it supports. This works even with MongoDB 3.0, as it will + not reply with any compressors. It also works with MongoDB 3.4 which will reply with `snappy` if it was part of + the driver's list. MongoDB 3.6 will likely include zlib support. + +- Which compressors are currently supported? + + - MongoDB 3.4 supports `snappy` + - MongoDB 3.6 supports `snappy` and `zlib` + - MongoDB 4.2 supports `snappy`, `zlib`, and `zstd` + +- My language supports xyz compressor, should I announce them all in the handshake? + + - No. But you are allowed to if you really want to make sure you can use that compressor with MongoDB 4.2 and your + current driver versions. + +- My language does not support xzy compressor. What do I do? + + - That is OK. You don’t have to support xyz. + +- No MongoDB supported compressors are available for my language + + - That is OK. You don’t have to support compressors you can’t support. All it means is you can’t compress the request, + and since you never declared support for any compressor, you won’t be served with compressed responses either. + +- Why did the server not support zlib in MongoDB 3.4? + + - Snappy was selected for its very low performance hit, while giving reasonable compression, resulting in quite + significant bandwidth reduction. Zlib characteristics are slightly different out-of-the-box and did not make sense + for the initial goal of reducing bandwidth between replica set nodes. + +- If snappy is preferable to zlib, why add support for zlib in MongoDB 3.6? + + - Zlib is available on every platform known to man. Snappy is not. Having zlib support makes sense for client traffic, + which could originate on any type of platform, which may or may not support snappy. + +## Changelog + +- 2024-02-16: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter and reformat changelog. +- 2021-04-06: Use 'hello' command +- 2019-05-13: Add zstd as supported compression algorithm +- 2017-06-13: Don't require clients to implement legacy opcodes +- 2017-05-10: Initial commit. diff --git a/source/compression/OP_COMPRESSED.rst b/source/compression/OP_COMPRESSED.rst index 2d3192ac42..985c729091 100644 --- a/source/compression/OP_COMPRESSED.rst +++ b/source/compression/OP_COMPRESSED.rst @@ -1,481 +1,3 @@ -=========================== -Wire Compression in Drivers -=========================== - - -:Title: Wire Compression In Drivers -:Author: Hannes Magnússon -:Advisory Group: Jonathan Reams, Christian Kvalheim, Ross Lawley -:Approvers: David Golden (2017-04-26), - Christian Kvalheim (2017-04-27), - Jeff Yemin (2017-04-27), - Bernie Hackett (2017-05-09), - Dana Groff (2017-05-02) -:Status: Accepted -:Type: Standards -:Last Modified: 2017-06-13 -:Minimum Server Version: 3.4 -:Version: 1.1 - - -Abstract -======== - -This specification describes how to implement Wire Protocol Compression between -MongoDB drivers and mongo[d|s]. - -Compression is achieved through a new bi-directional wire protocol opcode, -referred to as OP_COMPRESSED. - -Server side compressor support is checked during the initial MongoDB Handshake, -and is compatible with all historical versions of MongoDB. If a client detects -a compatible compressor it will use the compressor for all valid requests. - - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - - - -Terms -===== - -*Compressor* - A compression algorithm, such as snappy or zlib. - - -Specification -============= - -MongoDB Handshake Amendment ---------------------------- - -The `MongoDB Handshake Protocol -`_ -describes an argument passed to isMaster, ``client``. This specification adds -an additional argument, ``compression``, that SHOULD be provided to isMaster if -a driver supports wire compression. - -The value of this argument is an array of supported compressors. - -Example:: - - { - isMaster: 1, - client: {}, /* See MongoDB Handshake */ - compression: ["snappy", "zlib"] - } - -When no compression is enabled on the client, drivers SHOULD send an empty -compression argument. - -Example:: - - { - isMaster: 1, - client: {}, /* See MongoDB Handshake */ - compression: [] - } - - - -Clients that want to compress their messages need to send a list of the -algorithms - in the order they are specified in the client configuration - that -they are willing to support to the server during the initial isMaster call. For -example, a client wishing to compress with the snappy algorithm, should send:: - - { isMaster: 1, ... , compression: [ "snappy" ] } - -The server will respond with the intersection of its list of supported -compressors and the client's. For example, if the server had both snappy and -zlib enabled and the client requested snappy, it would respond with:: - - { ... , compression: [ "snappy" ], ok: 1 } - -If the client included both snappy and zlib, the server would respond with -something like:: - - { ... , compression: [ "snappy", "zlib" ], ok: 1 } - -If the server has no compression algorithms in common with the client, it sends -back an isMaster response without a compression field. Clients MAY issue a log -level event to inform the user, but MUST NOT error. - -When MongoDB server receives a compressor it can support it MAY reply to any -and all requests using the selected compressor, including the reply of the -initial MongoDB Handshake. -As each OP_COMPRESSED message contains the compressor ID, clients MUST NOT -assume which compressor each message uses, but MUST decompress the message -using the compressor identified in the OP_COMPRESSED opcode header. - -When compressing, clients MUST use the first compressor in the client's -configured compressors list that is also in the servers list. - - -Connection String Options -------------------------- - -Two new connection string options: - -compressors -~~~~~~~~~~~ -Comma separated list of compressors the client should present to the server. -Unknown compressors MUST yield a warning, as per the Connection String -specification, and MUST NOT be included in the handshake. -By default, no compressor is configured and thus compression will not be used. -When multiple compressors are provided, the list should be treated as a -priority ordered list of compressors to use, with highest priority given to the -first compressor in the list, and lowest priority to the last compressor in the -list. - -Example:: - - mongodb://localhost/?compressors=snappy,zlib - - -zlibCompressionLevel -~~~~~~~~~~~~~~~~~~~~ -Integer value from ``-1`` - ``9``. This configuration option only applies to -the zlib compressor. When zlib is not one of the compressors enumerated by the -``compressors`` configuration option then providing this option has no meaning, -but clients SHOULD NOT issue a warning. - -+-------+---------------------------------+ -| -1 | Default Compression (usually 6) | -+-------+---------------------------------+ -| 0 | No compression | -+-------+---------------------------------+ -| 1 | Best Speed | -+-------+---------------------------------+ -| 9 | Best Compression | -+-------+---------------------------------+ - -Note that this value only applies to the client side compression level, not the -response. - - -OP_COMPRESSED -------------- - -The new opcode, called OP_COMPRESSED, has the following structure:: - - struct OP_COMPRESSED { - struct MsgHeader { - int32 messageLength; - int32 requestID; - int32 responseTo; - int32 opCode = 2012; - }; - int32_t originalOpcode; - int32_t uncompressedSize; - uint8_t compressorId; - char *compressedMessage; - }; - - -+-------------------+--------------------------------------------------------------------------+ -| Field | Description | -+===================+==========================================================================+ -| originalOpcode | Contains the value of the wrapped opcode. | -+-------------------+--------------------------------------------------------------------------+ -| uncompressedSize | The size of the deflated compressedMessage, which excludes the MsgHeader | -+-------------------+--------------------------------------------------------------------------+ -| compressorId | The ID of the compressor that compressed the message | -+-------------------+--------------------------------------------------------------------------+ -| compressedMessage | The opcode itself, excluding the MsgHeader | -+-------------------+--------------------------------------------------------------------------+ - -Compressor IDs --------------- - -Each compressor is assigned a predefined compressor ID. - -+-----------------+----------------+-------------------------------------------------------+ -| compressorId | isMaster Value | Description | -+=================+================+=======================================================+ -| 0 | noop | The content of the message is uncompressed. | -| | | This is realistically only used for testing | -+-----------------+----------------+-------------------------------------------------------+ -| 1 | snappy | The content of the message is compressed using snappy | -+-----------------+----------------+-------------------------------------------------------+ -| 2 | zlib | The content of the message is compressed using zlib | -+-----------------+----------------+-------------------------------------------------------+ -| 3-255 | reserved | Reserved for future used | -+-----------------+----------------+-------------------------------------------------------+ - - -Compressible messages ---------------------- - -Any opcode can be compressed and wrapped in an ``OP_COMPRESSED`` header. -The ``OP_COMPRESSED`` is strictly a wire protocol without regards to what -opcode it wraps, be it ``OP_QUERY``, ``OP_REPLY``, ``OP_MSG`` or any other -future or past opcode. -The ``compressedMessage`` contains the original opcode, excluding the standard -``MsgHeader``. The ``originalOpcode`` value therefore effectively replaces the -standard ``MsgHeader`` of the compressed opcode. - -There is no guarantee that a response will be compressed even though -compression was negotiated for in the handshake. Clients MUST be able to parse -both compressed and uncompressed responses to both compressed and uncompressed -requests. - -MongoDB 3.4 will always reply with a compressed response when compression has -been negotiated, but future versions may not. - -A client MAY choose to implement compression for only ``OP_QUERY``, -``OP_REPLY``, and ``OP_MSG``, and perhaps for future opcodes, but not to -implement it for ``OP_INSERT``, ``OP_UPDATE``, ``OP_DELETE``, ``OP_GETMORE``, -and ``OP_KILLCURSORS``. - -Note that certain messages, such as authentication commands, MUST NOT be -compressed. All other messages MUST be compressed, when compression has been -negotiated and the driver has implemented compression for the opcode in use. - - -Messages not allowed to be compressed -------------------------------------- - -In efforts to mitigate against current and previous attacks, certain messages -MUST NOT be compressed, such as authentication requests. - -Messages using the following commands MUST NOT be compressed: - -* isMaster -* saslStart -* saslContinue -* getnonce -* authenticate -* createUser -* updateUser -* copydbSaslStart -* copydbgetnonce -* copydb - - -Test Plan -========= - -There are no automated tests accompanying this specification, instead the -following is a description of test scenarios clients should implement. - -In general, after implementing this functionality and the test cases, running -the traditional client test suite against a server with compression enabled, -and ensuring the test suite is configured to provide a valid compressor as part -of the connection string, is a good idea. MongoDB-supported drivers MUST add -such variant to their CI environment. - - -The following cases assume a standalone MongoDB 3.4 (or later) node configured -with:: - - mongod --networkMessageCompressors "snappy" -vvv - -Create an example application which connects to a provided connection string, -runs ``ping: 1``, and then quits the program normally. - -Connection strings, and results -------------------------------- - -* mongodb://localhost:27017/?compressors=snappy - - mongod should have logged the following:: - - 2017-04-17T15:04:37.756-0700 I NETWORK [thread1] connection accepted from 127.0.0.1:34294 #6 (1 connection now open) - 2017-04-17T15:04:37.756-0700 D COMMAND [conn6] run command admin.$cmd { isMaster: 1, client: { driver: { name: "mongoc", version: "1.7.0-dev" }, os: { type: "Linux", name: "Ubuntu", version: "16.10", architecture: "x86_64" }, platform: "cfg=0xeb8e9 posix=200809 stdc=201112 CC=GCC 6.2.0 20161005 CFLAGS="" LDFLAGS=""" }, compression: [ "snappy" ] } - 2017-04-17T15:04:37.756-0700 I NETWORK [conn6] received client metadata from 127.0.0.1:34294 conn6: { driver: { name: "mongoc", version: "1.7.0-dev" }, os: { type: "Linux", name: "Ubuntu", version: "16.10", architecture: "x86_64" }, platform: "cfg=0xeb8e9 posix=200809 stdc=201112 CC=GCC 6.2.0 20161005 CFLAGS="" LDFLAGS=""" } - 2017-04-17T15:04:37.756-0700 D NETWORK [conn6] Starting server-side compression negotiation - 2017-04-17T15:04:37.756-0700 D NETWORK [conn6] snappy is supported - 2017-04-17T15:04:37.756-0700 I COMMAND [conn6] command admin.$cmd command: isMaster { isMaster: 1, client: { driver: { name: "mongoc", version: "1.7.0-dev" }, os: { type: "Linux", name: "Ubuntu", version: "16.10", architecture: "x86_64" }, platform: "cfg=0xeb8e9 posix=200809 stdc=201112 CC=GCC 6.2.0 20161005 CFLAGS="" LDFLAGS=""" }, compression: [ "snappy" ] } numYields:0 reslen:221 locks:{} protocol:op_query 0ms - 2017-04-17T15:04:37.756-0700 D NETWORK [conn6] Compressing message with snappy - 2017-04-17T15:04:37.757-0700 D NETWORK [conn6] Decompressing message with snappy - 2017-04-17T15:04:37.757-0700 D COMMAND [conn6] run command test.$cmd { ping: 1 } - 2017-04-17T15:04:37.757-0700 I COMMAND [conn6] command test.$cmd command: ping { ping: 1 } numYields:0 reslen:37 locks:{} protocol:op_query 0ms - 2017-04-17T15:04:37.757-0700 D NETWORK [conn6] Compressing message with snappy - 2017-04-17T15:04:37.757-0700 D NETWORK [conn6] Socket recv() conn closed? 127.0.0.1:34294 - 2017-04-17T15:04:37.757-0700 D NETWORK [conn6] SocketException: remote: 127.0.0.1:34294 error: 9001 socket exception [CLOSED] server [127.0.0.1:34294] - 2017-04-17T15:04:37.757-0700 I - [conn6] end connection 127.0.0.1:34294 (1 connection now open) - - The result of the program should have been:: - - { "ok" : 1.0 } - - -* mongodb://localhost:27017/?compressors=snoopy - - mongod should have logged the following:: - - 2017-02-27T04:00:00.000-0700 D COMMAND [conn654] run command admin.$cmd { isMaster: 1, client: { ... }, compression: [] } - - e.g., empty compression: [] array. No operations should have been compressed. - The results of the program should have been:: - - WARNING: Unsupported compressor: 'snoopy' - { "ok" : { "$numberDouble" : "1.0" } } - - -* mongodb://localhost:27017/?compressors=snappy,zlib - - mongod should have logged the following:: - - 2017-02-27T04:00:00.000-0700 D NETWORK [conn645] Decompressing message with snappy - - The results of the program should have been:: - - { "ok" : { "$numberDouble" : "1.0" } } - - -* mongodb://localhost:27017/?compressors=zlib,snappy - - mongod should have logged the following:: - - 2017-02-27T04:00:00.000-0700 D NETWORK [connXXX] Decompressing message with zlib - - The results of the program should have been:: - - { "ok" : { "$numberDouble" : "1.0" } } - -* Create example program that authenticates to the server using SCRAM-SHA-1, - then creates another user (MONGODB-CR), then runs isMaster followed with - serverStatus. -* Reconnect to the same server using the created MONGODB-CR credentials. - Observe that the only command that was decompressed on the server was - ``serverStatus``, while the server replied with OP_COMPRESSED for at least - the serverStatus command. - - - - - - -Motivation For Change -===================== - -Drivers provide the canonical interface to MongoDB. Most tools for MongoDB are -written with the aid of MongoDB drivers. There exist a lot of tools for MongoDB -that import massive datasets which could stand to gain a lot from compression. -Even day-to-day applications stand to gain from reduced bandwidth utilization -at low cpu costs, especially when doing large reads off the network. - -Not all use cases fit compression, but we will allow users to decide if wire -compression is right for them. - - -Design rationale -================ - -Snappy has minimal cost and provides a reasonable compression ratio, but it is -not expected to be available for all languages MongoDB Drivers support. -Supporting zlib is therefore important to the ecosystem, but for languages that -do support snappy we expected it to be the default choice. While snappy has no -knobs to tune, zlib does have support for specifying the compression level -(tuned from speed to compression). As we don’t anticipate adding support for -compression libraries with complex knobs to tune this specification has opted -not to define a complex configuration structure and only define the currently -relevant ``zlibCompressionLevel``. When other compression libraries are -supported, adding support for configuring that library (if any is needed) -should be handled on a case by case basis. - - -Backwards Compatibility -======================= - -The new argument provided to the MongoDB Handshake has no backwards compatible -implications as servers that do not expect it will simply ignore it. This -means a server will therefore never reply with a list of acceptable compressors -which in turns means a client CANNOT use the OP_COMPRESSED opcode. - - -Reference Implementation -======================== - -* `mongoc `_ - - -Future Work -=========== - -Possible future improvements include defining an API to determine compressor -and configuration per operation, rather than needing to create two different -client pools, one for compression and one without, when the user is expecting -only needing to (not) compress very few operations. - - - -Q & A -===== -* Statistics? - * See `serverStatus - `_ in the - server - -* How to try this/enable it? - * mongod --networkMessageCompressors "snappy" - -* The server MAY reply with compressed data even if the request was not compressed? - * Yes, and this is in fact the behaviour of MongoDB 3.4 - -* Can drivers compress the initial MongoDB Handshake/isMaster request? - * No. - -* Can the server reply to the MongoDB Handshake/isMaster compressed? - * Yes, yes it can. Be aware it is completely acceptable for the server to - use compression for any and all replies, using any supported - compressor, when the client announced support for compression - this - includes the reply to the actual MongoDB Handshake/isMaster where the - support was announced. - -* This is billed a MongoDB 3.6 feature -- but I hear it works with MongoDB3.4? - * Yes, it does. All MongoDB versions support the ``compression`` argument - to ``isMaster`` and all MongoDB versions will reply with an intersection - of compressors it supports. This works even with MongoDB 3.0, as it - will not reply with any compressors. It also works with MongoDB 3.4 - which will reply with ``snappy`` if it was part of the driver's list. - MongoDB 3.6 will likely include zlib support. - -* Which compressors are currently supported? - * MongoDB 3.4 supports ``snappy`` - * MongoDB 3.6 supports ``snappy`` and likely ``zlib`` - -* My language supports xyz compressor, should I announce them all in the handshake? - * No. But you are allowed to if you really want to make sure you can use - that compressor with MongoDB 42 and your current driver versions. - -* My language does not support snappy. What do I do? - * That is OK. You don’t have to support snappy - -* My language does not support zlib. What do I do? - * That is OK. You don’t have to support zlib - -* No MongoDB supported compressors are available for my language - * That is OK. You don’t have to support compressors you can’t support. - All it means is you can’t compress the request, and since you never - declared support for any compressor, you won’t be served with - compressed responses either. - -* Why did the server not support zlib in MongoDB 3.4? - * Snappy was selected for its very low performance hit, while giving - reasonable compression, resulting in quite significant bandwidth - reduction. Zlib characteristics are slightly different out-of-the-box - and did not make sense for the initial goal of reducing bandwidth - between replica set nodes. - -* If snappy is preferable to zlib, why add support for zlib in MongoDB 3.6? - * Zlib is available on every platform known to man. Snappy is not. Having - zlib support makes sense for client traffic, which could originate on - any type of platform, which may or may not support snappy. - - - -Changelog -========= - -+------------+---------------------------------------------------+ -| 2017-06-13 | Don't require clients to implement legacy opcodes | -+------------+---------------------------------------------------+ -| 2017-05-10 | Initial commit | -+------------+---------------------------------------------------+ - +.. note:: + This specification has been converted to Markdown and renamed to + `OP_COMPRESSED.md `_. diff --git a/source/conf.py b/source/conf.py new file mode 100644 index 0000000000..c204326b29 --- /dev/null +++ b/source/conf.py @@ -0,0 +1,41 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- + +project = 'MongoDB Specifications' +copyright = '2022, MongoDB' +author = 'MongoDB' + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = [] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['Thumbs.db', '.DS_Store'] + +# Warn about all missing references +nitpicky = True + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] diff --git a/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md new file mode 100644 index 0000000000..c581351e8c --- /dev/null +++ b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md @@ -0,0 +1,1432 @@ +# Connection Monitoring and Pooling + +- Status: Accepted +- Minimum Server Version: N/A + +## Abstract + +Drivers currently support a variety of options that allow users to configure connection pooling behavior. Users are +confused by drivers supporting different subsets of these options. Additionally, drivers implement their connection +pools differently, making it difficult to design cross-driver pool functionality. By unifying and codifying pooling +options and behavior across all drivers, we will increase user comprehension and code base maintainability. + +This specification does not apply to drivers that do not support multitasking. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Definitions + +### Connection + +A Connection (when linked) refers to the `Connection` type defined in the +[Connection Pool Members](#connection-pool-members) section of this specification. It does not refer to an actual TCP +connection to an Endpoint. A `Connection` will attempt to create and wrap such a TCP connection over the course of its +existence, but it is not equivalent to one nor does it wrap an active one at all times. + +For the purposes of testing, a mocked `Connection` type could be used with the pool that never actually creates a TCP +connection or performs any I/O. + +### Endpoint + +For convenience, an Endpoint refers to either a **mongod** or **mongos** instance. + +### Thread + +For convenience, a Thread refers to: + +- A shared-address-space process (a.k.a. a thread) in multi-threaded drivers +- An Execution Frame / Continuation in asynchronous drivers +- A goroutine in Go + +## Behavioral Description + +### Which Drivers this applies to + +This specification is solely concerned with drivers that implement a connection pool. A driver SHOULD implement a +connection pool, but is not required to. + +### Connection Pool Options + +All drivers that implement a connection pool MUST implement and conform to the same MongoClient options. There can be +slight deviation in naming to make the options idiomatic to the driver language. + +### Connection Pool Behaviors + +All driver connection pools MUST provide an API that allows the driver to check out a connection, check in a connection +back to the pool, and clear all connections in the pool. This API is for internal use only, and SHOULD NOT be documented +as a public API. + +### Connection Pool Monitoring + +All drivers that implement a connection pool MUST provide an API that allows users to subscribe to events emitted from +the pool. Conceptually, event emission is instantaneous, i.e., one may talk about the instant an event is emitted, and +represents the start of an activity of delivering the event to a subscribed user. + +## Detailed Design + +### Connection Pool Options + +Drivers that implement a Connection Pool MUST support the following ConnectionPoolOptions: + +```typescript +interface ConnectionPoolOptions { + /** + * The maximum number of Connections that may be associated + * with a pool at a given time. This includes in use and + * available connections. + * If specified, MUST be an integer >= 0. + * A value of 0 means there is no limit. + * Defaults to 100. + */ + maxPoolSize?: number; + + /** + * The minimum number of Connections that MUST exist at any moment + * in a single connection pool. + * If specified, MUST be an integer >= 0. If maxPoolSize is > 0 + * then minPoolSize must be <= maxPoolSize + * Defaults to 0. + */ + minPoolSize?: number; + + /** + * The maximum amount of time a Connection should remain idle + * in the connection pool before being marked idle. + * If specified, MUST be a number >= 0. + * A value of 0 means there is no limit. + * Defaults to 0. + */ + maxIdleTimeMS?: number; + + /** + * The maximum number of Connections a Pool may be establishing concurrently. + * Establishment of a Connection is a part of its life cycle + * starting after a ConnectionCreatedEvent and ending before a ConnectionReadyEvent. + * If specified, MUST be a number > 0. + * Defaults to 2. + */ + maxConnecting?: number; +} +``` + +Additionally, Drivers that implement a Connection Pool MUST support the following ConnectionPoolOptions UNLESS that +driver meets ALL of the following conditions: + +- The driver/language currently has an idiomatic timeout mechanism implemented +- The timeout mechanism conforms to [the aggressive requirement of timing out a thread in the WaitQueue](#waitqueue) + +```typescript +interface ConnectionPoolOptions { + /** + * NOTE: This option has been deprecated in favor of timeoutMS. + * + * The maximum amount of time a thread can wait for + * either an available non-perished connection (limited by `maxPoolSize`), + * or a pending connection (limited by `maxConnecting`). + * If specified, MUST be a number >= 0. + * A value of 0 means there is no limit. + * Defaults to 0. + */ + waitQueueTimeoutMS?: number; +} +``` + +These options MUST be specified at the MongoClient level, and SHOULD be named in a manner idiomatic to the driver's +language. All connection pools created by a MongoClient MUST use the same ConnectionPoolOptions. + +When parsing a mongodb connection string, a user MUST be able to specify these options using the default names specified +above. + +#### Deprecated Options + +The following ConnectionPoolOptions are considered deprecated. They MUST NOT be implemented if they do not already exist +in a driver, and they SHOULD be deprecated and removed from drivers that implement them as early as possible: + +```typescript +interface ConnectionPoolOptions { + /** + * The maximum number of threads that can simultaneously wait + * for a Connection to become available. + */ + waitQueueSize?: number; + + /** + * An alternative way of setting waitQueueSize, it specifies + * the maximum number of threads that can wait per connection. + * waitQueueSize === waitQueueMultiple * maxPoolSize + */ + waitQueueMultiple?: number +} +``` + +### Connection Pool Members + +#### Connection + +A driver-defined wrapper around a single TCP connection to an Endpoint. A [Connection](#connection) has the following +properties: + +- **Single Endpoint:** A [Connection](#connection) MUST be associated with a single Endpoint. A + [Connection](#connection) MUST NOT be associated with multiple Endpoints. +- **Single Lifetime:** A [Connection](#connection) MUST NOT be used after it is closed. +- **Single Owner:** A [Connection](#connection) MUST belong to exactly one Pool, and MUST NOT be shared across multiple + pools +- **Single Track:** A [Connection](#connection) MUST limit itself to one request / response at a time. A + [Connection](#connection) MUST NOT multiplex/pipeline requests to an Endpoint. +- **Monotonically Increasing ID:** A [Connection](#connection) MUST have an ID number associated with it. + [Connection](#connection) IDs within a Pool MUST be assigned in order of creation, starting at 1 and increasing by 1 + for each new Connection. +- **Valid Connection:** A connection MUST NOT be checked out of the pool until it has successfully and fully completed a + MongoDB Handshake and Authentication as specified in the [Handshake](../mongodb-handshake/handshake.md), + [OP_COMPRESSED](../compression/OP_COMPRESSED.md), and [Authentication](../auth/auth.md) specifications. +- **Perishable**: it is possible for a [Connection](#connection) to become **Perished**. A [Connection](#connection) is + considered perished if any of the following are true: + - **Stale:** The [Connection](#connection) 's generation does not match the generation of the parent pool + - **Idle:** The [Connection](#connection) is currently "available" (as defined below) and has been for longer than + **maxIdleTimeMS**. + - **Errored:** The [Connection](#connection) has experienced an error that indicates it is no longer recommended for + use. Examples include, but are not limited to: + - Network Error + - Network Timeout + - Endpoint closing the connection + - Driver-Side Timeout + - Wire-Protocol Error + +```typescript +interface Connection { + /** + * An id number associated with the Connection + */ + id: number; + + /** + * The address of the pool that owns this Connection + */ + address: string; + + /** + * An integer representing the "generation" of the pool + * when this Connection was created. + */ + generation: number; + + /** + * The current state of the Connection. + * + * Possible values are the following: + * - "pending": The Connection has been created but has not yet been established. Contributes to + * totalConnectionCount and pendingConnectionCount. + * + * - "available": The Connection has been established and is waiting in the pool to be checked + * out. Contributes to both totalConnectionCount and availableConnectionCount. + * + * - "in use": The Connection has been established, checked out from the pool, and has yet + * to be checked back in. Contributes to totalConnectionCount. + * + * - "closed": The Connection has had its socket closed and cannot be used for any future + * operations. Does not contribute to any connection counts. + * + * Note: this field is mainly used for the purposes of describing state + * in this specification. It is not required that drivers + * actually include this field in their implementations of Connection. + */ + state: "pending" | "available" | "in use" | "closed"; +} +``` + +#### WaitQueue + +A concept that represents pending requests for [Connections](#connection). When a thread requests a +[Connection](#connection) from a Pool, the thread enters the Pool's WaitQueue. A thread stays in the WaitQueue until it +either receives a [Connection](#connection) or times out. A WaitQueue has the following traits: + +- **Thread-Safe**: When multiple threads attempt to enter or exit a WaitQueue, they do so in a thread-safe manner. +- **Ordered/fair**: When [Connections](#connection) are made available, they SHOULD be issued out to threads in the + order that the threads entered the WaitQueue. If this is behavior poses too much of an implementation burden, then + at the very least threads that have entered the queue more recently MUST NOT be intentionally prioritized over those + that entered it earlier. +- **Timeout aggressively:** Members of a WaitQueue MUST timeout if they are enqueued for longer than the computed + timeout and MUST leave the WaitQueue immediately in this case. + +The implementation details of a WaitQueue are left to the driver. Example implementations include: + +- A fair Semaphore +- A Queue of callbacks + +#### Connection Pool + +A driver-defined entity that encapsulates all non-monitoring [Connections](#connection) associated with a single +Endpoint. The pool has the following properties: + +- **Thread Safe:** All Pool behaviors MUST be thread safe. +- **Not Fork-Safe:** A Pool is explicitly not fork-safe. If a Pool detects that is it being used by a forked process, it + MUST immediately clear itself and update its pid +- **Single Owner:** A Pool MUST be associated with exactly one Endpoint, and MUST NOT be shared between Endpoints. +- **Emit Events and Log Messages:** A Pool MUST emit pool events and log messages when dictated by this spec (see + [Connection Pool Monitoring](#connection-pool-monitoring)). Users MUST be able to subscribe to emitted events and + log messages in a manner idiomatic to their language and driver. +- **Closeable:** A Pool MUST be able to be manually closed. When a Pool is closed, the following behaviors change: + - Checking in a [Connection](#connection) to the Pool automatically closes the [Connection](#connection) + - Attempting to check out a [Connection](#connection) from the Pool results in an Error +- **Clearable:** A Pool MUST be able to be cleared. Clearing the pool marks all pooled and checked out + [Connections](#connection) as stale and lazily closes them as they are checkedIn or encountered in checkOut. + Additionally, all requests are evicted from the WaitQueue and return errors that are considered non-timeout network + errors. +- **Pausable:** A Pool MUST be able to be paused and resumed. A Pool is paused automatically when it is cleared, and it + can be resumed by being marked as "ready". While the Pool is paused, it exhibits the following behaviors: + - Attempting to check out a [Connection](#connection) from the Pool results in a non-timeout network error + - Connections are not created in the background to satisfy minPoolSize +- **Capped:** a pool is capped if **maxPoolSize** is set to a non-zero value. If a pool is capped, then its total number + of [Connections](#connection) (including available and in use) MUST NOT exceed **maxPoolSize** +- **Rate-limited:** A Pool MUST limit the number of [Connections](#connection) being + [established](#establishing-a-connection-internal-implementation) concurrently via the **maxConnecting** + [pool option](#connection-pool-options). +- **Backpressure-enabled** - The pool MUST add the error labels `SystemOverloadedError` and `RetryableError` to network + errors or network timeouts it encounters during the connection establishment or the `hello` message. These labels + are used by the + [SDAM error handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling-pseudocode) + to avoid clearing the pool. The pool MUST NOT add the backpressure error labels during an authentication step + after the `hello` message. For errors that the driver can distinguish as never occurring due to server overload, + such as DNS lookup failures, non‑I/O TLS errors (e.g., certificate validation or hostname‑mismatch failures), or + errors encountered while establishing a connection to a SOCKS5 proxy, the driver MUST NOT add backpressure error + labels for these error types. In contrast, if an I/O error (for example, an EOF error) occurs during the TLS + handshake, the driver MUST treat it as a potential overload condition and MUST add the backpressure error labels. + +```typescript +interface ConnectionPool { + /** + * The Queue of threads waiting for a Connection to be available + */ + waitQueue: WaitQueue; + + /** + * A generation number representing the SDAM generation of the pool. + */ + generation: number; + + /** + * A map representing the various generation numbers for various services + * when in load balancer mode. + */ + serviceGenerations: Map; + + /** + * The state of the pool. + * + * Possible values are the following: + * - "paused": The initial state of the pool. Connections may not be checked out nor can they + * be established in the background to satisfy minPoolSize. Clearing a pool + * transitions it to this state. + * + * - "ready": The healthy state of the pool. It can service checkOut requests and create + * connections in the background. The pool can be set to this state via the + * ready() method. + * + * - "closed": The pool is destroyed. No more Connections may ever be checked out nor any + * created in the background. The pool can be set to this state via the close() + * method. The pool cannot transition to any other state after being closed. + */ + state: "paused" | "ready" | "closed"; + + // Any of the following connection counts may be computed rather than + // actually stored on the pool. + + /** + * An integer expressing how many total Connections + * ("pending" + "available" + "in use") the pool currently has + */ + totalConnectionCount: number; + + /** + * An integer expressing how many Connections are currently + * available in the pool. + */ + availableConnectionCount: number; + + /** + * An integer expressing how many Connections are currently + * being established. + */ + pendingConnectionCount: number; + + /** + * Returns a Connection for use + */ + checkOut(): Connection; + + /** + * Check in a Connection back to the Connection pool + */ + checkIn(connection: Connection): void; + + /** + * Mark all current Connections as stale, clear the WaitQueue, and mark the pool as "paused". + * No connections may be checked out or created in this pool until ready() is called again. + * interruptInUseConnections specifies whether the pool will force interrupt "in use" connections as part of the clear. + * Default false. + */ + clear(interruptInUseConnections: Optional): void; + + /** + * Mark the pool as "ready", allowing checkOuts to resume and connections to be created in the background. + * A pool can only transition from "paused" to "ready". A "closed" pool + * cannot be marked as "ready" via this method. + */ + ready(): void; + + /** + * Marks the pool as "closed", preventing the pool from creating and returning new Connections + */ + close(): void; +} +``` + +### Connection Pool Behaviors + +#### Creating a Connection Pool + +This specification does not define how a pool is to be created, leaving it up to the driver. Creation of a connection +pool is generally an implementation detail of the driver, i.e., is not a part of the public API of the driver. The SDAM +specification defines +[when](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#connection-pool-management) the driver +should create connection pools. + +When a pool is created, its state MUST initially be set to "paused". Even if minPoolSize is set, the pool MUST NOT begin +being [populated](#populating-the-pool-with-a-connection-internal-implementation) with [Connections](#connection) until +it has been marked as "ready". SDAM will mark the pool as "ready" on each successful check. See +[Connection Pool Management](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#connection-pool-management) +section in the SDAM specification for more information. + +```text +set generation to 0 +set state to "paused" +emit PoolCreatedEvent and equivalent log message +``` + +#### Closing a Connection Pool + +When a pool is closed, it MUST first close all available [Connections](#connection) in that pool. This results in the +following behavior changes: + +- In use [Connections](#connection) MUST be closed when they are checked in to the closed pool. +- Attempting to check out a [Connection](#connection) MUST result in an error. + +```text +mark pool as "closed" +for connection in availableConnections: + close connection +emit PoolClosedEvent and equivalent log message +``` + +#### Marking a Connection Pool as Ready + +Connection Pools start off as "paused", and they are marked as "ready" by monitors after they perform successful server +checks. Once a pool is "ready", it can start checking out [Connections](#connection) and populating them in the +background. + +If the pool is already "ready" when this method is invoked, then this method MUST immediately return and MUST NOT emit a +PoolReadyEvent. + +```text +mark pool as "ready" +emit PoolReadyEvent and equivalent log message +allow background thread to create connections +``` + +Note that the PoolReadyEvent MUST be emitted before the background thread is allowed to resume creating new connections, +and it must be the case that no observer is able to observe actions of the background thread related to creating new +connections before observing the PoolReadyEvent event. + +#### Creating a Connection (Internal Implementation) + +When creating a [Connection](#connection), the initial [Connection](#connection) is in a "pending" state. This only +creates a "virtual" [Connection](#connection), and performs no I/O. + +```text +connection = new Connection() +increment totalConnectionCount +increment pendingConnectionCount +set connection state to "pending" +tConnectionCreated = current instant (use a monotonic clock if possible) +emit ConnectionCreatedEvent and equivalent log message +return connection +``` + +#### Establishing a Connection (Internal Implementation) + +Before a [Connection](#connection) can be marked as either "available" or "in use", it must be established. This process +involves performing the initial handshake, handling OP_COMPRESSED, and performing authentication. + +```text +try: + connect connection via TCP / TLS + perform connection handshake + handle OP_COMPRESSED + perform connection authentication + tConnectionReady = current instant (use a monotonic clock if possible) + emit ConnectionReadyEvent(duration = tConnectionReady - tConnectionCreated) and equivalent log message + return connection +except error: + close connection + add `SystemOverloadedError` and `RetryableError` labels if appropriate (see "backpressure-enabled" in [Connection Pool](#connection-pool)) + throw error # Propagate error in manner idiomatic to language. +``` + +#### Closing a Connection (Internal Implementation) + +When a [Connection](#connection) is closed, it MUST first be marked as "closed", removing it from being counted as +"available" or "in use". Once that is complete, the [Connection](#connection) can perform whatever teardown is necessary +to close its underlying socket. The Driver SHOULD perform this teardown in a non-blocking manner, such as via the use of +a background thread or async I/O. + +```text +original state = connection state +set connection state to "closed" + +if original state is "available": + decrement availableConnectionCount +else if original state is "pending": + decrement pendingConnectionCount + +decrement totalConnectionCount +emit ConnectionClosedEvent and equivalent log message + +# The following can happen at a later time (i.e. in background +# thread) or via non-blocking I/O. +connection.socket.close() +``` + +#### Marking a Connection as Available (Internal Implementation) + +A [Connection](#connection) is "available" if it is able to be checked out. A [Connection](#connection) MUST NOT be +marked as "available" until it has been established. The pool MUST keep track of the number of currently available +[Connections](#connection). + +```text +increment availableConnectionCount +set connection state to "available" +add connection to availableConnections +``` + +#### Populating the Pool with a Connection (Internal Implementation) + +"Populating" the pool involves preemptively creating and establishing a [Connection](#connection) which is marked as +"available" for use in future operations. + +Populating the pool MUST NOT block any application threads. For example, it could be performed on a background thread or +via the use of non-blocking/async I/O. Populating the pool MUST NOT be performed unless the pool is "ready". + +If an error is encountered while populating a connection, it MUST be handled via the SDAM machinery according to the +[Application Errors](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#application-errors) section +in the SDAM specification. + +If minPoolSize is set, the [Connection](#connection) Pool MUST be populated until it has at least minPoolSize total +[Connections](#connection). This MUST occur only while the pool is "ready". If the pool implements a background thread, +it can be used for this. If the pool does not implement a background thread, the checkOut method is responsible for +ensuring this requirement is met. + +When populating the Pool, pendingConnectionCount has to be decremented after establishing a [Connection](#connection) +similarly to how it is done in [Checking Out a Connection](#checking-out-a-connection) to signal that another +[Connection](#connection) is allowed to be established. Such a signal MUST become observable to any [Thread](#thread) +after the action that +[marks the established Connection as "available"](#marking-a-connection-as-available-internal-implementation) becomes +observable to the [Thread](#thread). Informally, this order guarantees that no [Thread](#thread) tries to start +establishing a [Connection](#connection) when there is an "available" [Connection](#connection) established as a result +of populating the Pool. + +```text +wait until pendingConnectionCount < maxConnecting and pool is "ready" +create connection +try: + establish connection + mark connection as available +except error: + # Defer error handling to SDAM. + topology.handle_pre_handshake_error(error) +``` + +#### Checking Out a Connection + +A Pool MUST have a method that allows the driver to check out a [Connection](#connection). Checking out a +[Connection](#connection) involves submitting a request to the WaitQueue and, once that request reaches the front of the +queue, having the Pool find or create a [Connection](#connection) to fulfill that request. Requests MUST be subject to a +timeout which is computed per the rules in +[Client Side Operations Timeout: Server Selection](../client-side-operations-timeout/client-side-operations-timeout.md#server-selection). + +To service a request for a [Connection](#connection), the Pool MUST first iterate over the list of available +[Connections](#connection), searching for a non-perished one to be returned. If a perished [Connection](#connection) is +encountered, such a [Connection](#connection) MUST be closed (as described in +[Closing a Connection](#closing-a-connection-internal-implementation)) and the iteration of available +[Connections](#connection) MUST continue until either a non-perished available [Connection](#connection) is found or the +list of available [Connections](#connection) is exhausted. + +If the list is exhausted, the total number of [Connections](#connection) is less than maxPoolSize, and +pendingConnectionCount < maxConnecting, the pool MUST create a [Connection](#connection), establish it, mark it as "in +use" and return it. If totalConnectionCount == maxPoolSize or pendingConnectionCount == maxConnecting, then the pool +MUST wait to service the request until neither of those two conditions are met or until a [Connection](#connection) +becomes available, re-entering the checkOut loop in either case. This waiting MUST NOT prevent +[Connections](#connection) from being checked into the pool. Additionally, the Pool MUST NOT service any newer checkOut +requests before fulfilling the original one which could not be fulfilled. For drivers that implement the WaitQueue via a +fair semaphore, a condition variable may also be needed to meet this requirement. Waiting on the condition variable +SHOULD also be limited by the WaitQueueTimeout, if the driver supports one and it was specified by the user. + +If the pool is "closed" or "paused", any attempt to check out a [Connection](#connection) MUST throw an Error. The error +thrown as a result of the pool being "paused" MUST be considered a retryable error and MUST NOT be an error that marks +the SDAM state unknown. + +If the pool does not implement a background thread, the checkOut method is responsible for ensuring that the pool is +[populated](#populating-the-pool-with-a-connection-internal-implementation) with at least minPoolSize +[Connections](#connection). + +A [Connection](#connection) MUST NOT be checked out until it is established. In addition, the Pool MUST NOT prevent +other threads from checking out [Connections](#connection) while establishing a [Connection](#connection). + +Before a given [Connection](#connection) is returned from checkOut, it must be marked as "in use", and the pool's +availableConnectionCount MUST be decremented. + +```text +connection = Null +tConnectionCheckOutStarted = current instant (use a monotonic clock if possible) +emit ConnectionCheckOutStartedEvent and equivalent log message +try: + enter WaitQueue + wait until at top of wait queue + # Note that in a lock-based implementation of the wait queue would + # only allow one thread in the following block at a time + while connection is Null: + if a connection is available: + while connection is Null and a connection is available: + connection = next available connection + if connection is perished: + close connection + connection = Null + else if totalConnectionCount < maxPoolSize: + if pendingConnectionCount < maxConnecting: + connection = create connection + else: + # this waiting MUST NOT prevent other threads from checking Connections + # back in to the pool. + wait until pendingConnectionCount < maxConnecting or a connection is available + continue + +except pool is "closed": + tConnectionCheckOutFailed = current instant (use a monotonic clock if possible) + emit ConnectionCheckOutFailedEvent(reason="poolClosed", duration = tConnectionCheckOutFailed - tConnectionCheckOutStarted) and equivalent log message + throw PoolClosedError +except pool is "paused": + tConnectionCheckOutFailed = current instant (use a monotonic clock if possible) + emit ConnectionCheckOutFailedEvent(reason="connectionError", duration = tConnectionCheckOutFailed - tConnectionCheckOutStarted) and equivalent log message + throw PoolClearedError +except timeout: + tConnectionCheckOutFailed = current instant (use a monotonic clock if possible) + emit ConnectionCheckOutFailedEvent(reason="timeout", duration = tConnectionCheckOutFailed - tConnectionCheckOutStarted) and equivalent log message + throw WaitQueueTimeoutError +finally: + # This must be done in all drivers + leave wait queue + +# If the Connection has not been established yet (TCP, TLS, +# handshake, compression, and auth), it must be established +# before it is returned. +# This MUST NOT block other threads from acquiring connections. +if connection state is "pending": + try: + establish connection + except connection establishment error: + tConnectionCheckOutFailed = current instant (use a monotonic clock if possible) + emit ConnectionCheckOutFailedEvent(reason="connectionError", duration = tConnectionCheckOutFailed - tConnectionCheckOutStarted) and equivalent log message + decrement totalConnectionCount + throw + finally: + decrement pendingConnectionCount +else: + decrement availableConnectionCount +set connection state to "in use" + +# If there is no background thread, the pool MUST ensure that +# there are at least minPoolSize total connections. +do asynchronously: + while totalConnectionCount < minPoolSize: + populate the pool with a connection + +tConnectionCheckedOut = current instant (use a monotonic clock if possible) +emit ConnectionCheckedOutEvent(duration = tConnectionCheckedOut - tConnectionCheckOutStarted) and equivalent log message +return connection +``` + +#### Checking In a Connection + +A Pool MUST have a method of allowing the driver to check in a [Connection](#connection). The driver MUST NOT be allowed +to check in a [Connection](#connection) to a Pool that did not create that [Connection](#connection), and MUST throw an +Error if this is attempted. + +When the [Connection](#connection) is checked in, it MUST be [closed](#closing-a-connection-internal-implementation) if +any of the following are true: + +- The [Connection](#connection) is perished. +- The pool has been closed. + +Otherwise, the [Connection](#connection) is marked as available. + +```text +emit ConnectionCheckedInEvent and equivalent log message +if connection is perished OR pool is closed: + close connection +else: + mark connection as available +``` + +#### Clearing a Connection Pool + +Clearing the pool involves different steps depending on whether the pool is in load balanced mode or not. The +traditional / non-load balanced clearing behavior MUST NOT be used by pools in load balanced mode, and the load balanced +pool clearing behavior MUST NOT be used in non-load balanced pools. + +##### Clearing a non-load balanced pool + +A Pool MUST have a method of clearing all [Connections](#connection) when instructed. Rather than iterating through +every [Connection](#connection), this method should simply increment the generation of the Pool, implicitly marking all +current [Connections](#connection) as stale. It should also transition the pool's state to "paused" to halt the creation +of new connections until it is marked as "ready" again. The checkOut and checkIn algorithms will handle clearing out +stale [Connections](#connection). If a user is subscribed to Connection Monitoring events and/or connection log +messages, a PoolClearedEvent and log message MUST be emitted after incrementing the generation / marking the pool as +"paused". If the pool is already "paused" when it is cleared, then the pool MUST NOT emit a PoolCleared event or log +message. + +As part of clearing the pool, the WaitQueue MUST also be cleared, meaning all requests in the WaitQueue MUST fail with +errors indicating that the pool was cleared while the checkOut was being performed. The error returned as a result of +the pool being cleared MUST be considered a retryable error and MUST NOT be an error that marks the SDAM state unknown. +Clearing the WaitQueue MUST happen eagerly so that any operations waiting on [Connections](#connection) can retry as +soon as possible. The pool MUST NOT rely on WaitQueueTimeoutMS to clear requests from the WaitQueue. + +The clearing method MUST provide the option to interrupt any in-use connections as part of the clearing (henceforth +referred to as the interruptInUseConnections flag in this specification). "Interrupting a Connection" is defined as +canceling whatever task the Connection is currently performing and marking the Connection as perished (e.g. by closing +its underlying socket). The interrupting of these Connections MUST be performed as soon as possible but MUST NOT block +the pool or prevent it from processing further requests. If the pool has a background thread, and it is responsible for +interrupting in-use connections, its next run MUST be scheduled as soon as possible. + +The pool MUST only interrupt in-use Connections whose generation is less than or equal to the generation of the pool at +the moment of the clear (before the increment) that used the interruptInUseConnections flag. Any operations that have +their Connections interrupted in this way MUST fail with a retryable error. If possible, the error SHOULD be a +PoolClearedError with the following message: `"Connection to interrupted due to server monitor timeout"`. + +##### Clearing a load balanced pool + +A Pool MUST also have a method of clearing all [Connections](#connection) for a specific `serviceId` for use when in +load balancer mode. This method increments the generation of the pool for that specific `serviceId` in the generation +map. A PoolClearedEvent and log message MUST be emitted after incrementing the generation. Note that this method MUST +NOT transition the pool to the "paused" state and MUST NOT clear the WaitQueue. + +#### Load Balancer Mode + +For load-balanced deployments, pools MUST maintain a map from `serviceId` to a tuple of (generation, connection count) +where the connection count refers to the total number of connections that exist for a specific `serviceId`. The pool +MUST remove the entry for a `serviceId` once the connection count reaches 0. Once the MongoDB handshake is done, the +connection MUST get the generation number that applies to its `serviceId` from the map and update the map to increment +the connection count for this `serviceId`. + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#connection-pooling) for details. + +#### Forking + +A [Connection](#connection) is explicitly not fork-safe. The proper behavior in the case of a fork is to ResetAfterFork +by: + +- clear all Connection Pools in the child process +- closing all [Connections](#connection) in the child-process. + +Drivers that support forking MUST document that [Connections](#connection) to an Endpoint are not fork-safe, and +document the proper way to ResetAfterFork in the driver. + +Drivers MAY aggressively ResetAfterFork if the driver detects it has been forked. + +#### Optional Behaviors + +The following features of a Connection Pool SHOULD be implemented if they make sense in the driver and driver's +language. + +##### Background Thread + +A Pool SHOULD have a background Thread that is responsible for monitoring the state of all available +[Connections](#connection). This background thread SHOULD + +- Populate [Connections](#connection) to ensure that the pool always satisfies minPoolSize. +- Remove and close perished available [Connections](#connection) including "in use" connections if + `interruptInUseConnections` option was set to true in the most recent pool clear. +- Apply timeouts to connection establishment per + [Client Side Operations Timeout: Background Connection Pooling](../client-side-operations-timeout/client-side-operations-timeout.md#background-connection-pooling). + +A pool SHOULD allow immediate scheduling of the next background thread iteration after a clear is performed. + +Conceptually, the aforementioned activities are organized into sequential Background Thread Runs. A Run MUST do as much +work as readily available and then end instead of waiting for more work. For example, instead of waiting for +pendingConnectionCount to become less than maxConnecting when satisfying minPoolSize, a Run MUST either proceed with the +rest of its duties, e.g., closing available perished connections, or end. + +The duration of intervals between the end of one Run and the beginning of the next Run is not specified, but the +[Test Format and Runner Specification](../connection-monitoring-and-pooling/tests/README.md) may restrict this duration, +or introduce other restrictions to facilitate testing. + +##### withConnection + +A Pool SHOULD implement a scoped resource management mechanism idiomatic to their language to prevent +[Connections](#connection) from not being checked in. Examples include +[Python's "with" statement](https://docs.python.org/3/whatsnew/2.6.html#pep-343-the-with-statement) and +[C#'s "using" statement](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement). If +implemented, drivers SHOULD use this method as the default method of checking out and checking in +[Connections](#connection). + +### Connection Pool Monitoring + +All drivers that implement a connection pool MUST provide an API that allows users to subscribe to events emitted from +the pool. If a user subscribes to Connection Monitoring events, these events MUST be emitted when specified in +"Connection Pool Behaviors". Events SHOULD be created and subscribed to in a manner idiomatic to their language and +driver. + + + +#### Events + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#events) for details on the `serviceId` field. + +```typescript +/** + * Emitted when a Connection Pool is created + */ +interface PoolCreatedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * Any non-default pool options that were set on this Connection Pool. + */ + options: {...} +} + +/** + * Emitted when a Connection Pool is marked as ready. + */ +interface PoolReadyEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; +} + +/** + * Emitted when a Connection Pool is cleared + */ +interface PoolClearedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The service id for which the pool was cleared for in load balancing mode. + * See load balancer specification for more information about this field. + */ + serviceId: Optional; + + /** + * A flag whether the pool forced interrupting "in use" connections as part of the clear. + */ + interruptInUseConnections: Optional; +} + +/** + * Emitted when a Connection Pool is closed + */ +interface PoolClosedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; +} + +/** + * Emitted when a Connection Pool creates a Connection object. + * NOTE: This does not mean that the Connection is ready for use. + */ +interface ConnectionCreatedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: int64; +} + +/** + * Emitted when a Connection has finished its setup, and is now ready to use + */ +interface ConnectionReadyEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: int64; + + /** + * The time it took to establish the connection. + * In accordance with the definition of establishment of a connection + * specified by `ConnectionPoolOptions.maxConnecting`, + * it is the time elapsed between emitting a `ConnectionCreatedEvent` + * and emitting this event as part of the same checking out. + * + * Naturally, when establishing a connection is part of checking out, + * this duration is not greater than + * `ConnectionCheckedOutEvent`/`ConnectionCheckOutFailedEvent.duration`. + * + * A driver MAY choose the type idiomatic to the driver. + * If the type chosen does not convey units, e.g., `int64`, + * then the driver MAY include units in the name, e.g., `durationMS`. + */ + duration: Duration; +} + +/** + * Emitted when a Connection Pool closes a Connection + */ +interface ConnectionClosedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: int64; + + /** + * A reason explaining why this Connection was closed. + * Can be implemented as a string or enum. + * Current valid values are: + * - "stale": The pool was cleared, making the Connection no longer valid + * - "idle": The Connection became stale by being available for too long + * - "error": The Connection experienced an error, making it no longer valid + * - "poolClosed": The pool was closed, making the Connection no longer valid + */ + reason: string|Enum; +} + +/** + * Emitted when the driver starts attempting to check out a Connection + */ +interface ConnectionCheckOutStartedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting + * to connect to. + */ + address: string; +} + +/** + * Emitted when the driver's attempt to check out a Connection fails + */ +interface ConnectionCheckOutFailedEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * A reason explaining why Connection check out failed. + * Can be implemented as a string or enum. + * Current valid values are: + * - "poolClosed": The pool was previously closed, and cannot provide new Connections + * - "timeout": The Connection check out attempt exceeded the specified timeout + * - "connectionError": The Connection check out attempt experienced an error while setting up a new Connection + */ + reason: string|Enum; + + /** + * See `ConnectionCheckedOutEvent.duration`. + */ + duration: Duration; +} + +/** + * Emitted when the driver successfully checks out a Connection + */ +interface ConnectionCheckedOutEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: int64; + + /** + * The time it took to check out the connection. + * More specifically, the time elapsed between + * emitting a `ConnectionCheckOutStartedEvent` + * and emitting this event as part of the same checking out. + * + * Naturally, if a new connection was not created (`ConnectionCreatedEvent`) + * and established (`ConnectionReadyEvent`) as part of checking out, + * this duration is usually + * not greater than `ConnectionPoolOptions.waitQueueTimeoutMS`, + * but MAY occasionally be greater than that, + * because a driver does not provide hard real-time guarantees. + * + * A driver MAY choose the type idiomatic to the driver. + * If the type chosen does not convey units, e.g., `int64`, + * then the driver MAY include units in the name, e.g., `durationMS`. + */ + duration: Duration; +} + +/** + * Emitted when the driver checks in a Connection back to the Connection Pool + */ +interface ConnectionCheckedInEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The ID of the Connection + */ + connectionId: int64; +} +``` + +### Connection Pool Logging + +Please refer to the [logging specification](../logging/logging.md) for details on logging implementations in general, +including log levels, log components, handling of null values in log messages, and structured versus unstructured +logging. + +Drivers MUST support logging of connection pool information via the following types of log messages. These messages MUST +be logged at `Debug` level and use the `connection` log component. These messages MUST be emitted when specified in +"Connection Pool Behaviors". + +The log messages are intended to match the information contained in the events above. Drivers MAY implement connection +logging support via an event subscriber if it is convenient to do so. + +The types used in the structured message definitions below are demonstrative, and drivers MAY use similar types instead +so long as the information is present (e.g. a double instead of an integer, or a string instead of an integer if the +structured logging framework does not support numeric types). + +#### Common Fields + +All connection log messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| serverHost | String | the hostname, IP address, or Unix domain socket path for the endpoint the pool is for. | +| serverPort | Int | The port for the endpoint the pool is for. Optional; not present for Unix domain sockets. When the user does not specify a port and the default (27017) is used, the driver SHOULD include it here. | + +#### Pool Created Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Connection pool created" | +| maxIdleTimeMS | Int | The maxIdleTimeMS value for this pool. Optional; only required to include if the user specified a value. | +| minPoolSize | Int | The minPoolSize value for this pool. Optional; only required to include if the user specified a value. | +| maxPoolSize | Int | The maxPoolSize value for this pool. Optional; only required to include if the user specified a value. | +| maxConnecting | Int | The maxConnecting value for this pool. Optional; only required to include if the driver supports this option and the user specified a value. | +| waitQueueTimeoutMS | Int | The waitQueueTimeoutMS value for this pool. Optional; only required to include if the driver supports this option and the user specified a value. | +| waitQueueSize | Int | The waitQueueSize value for this pool. Optional; only required to include if the driver supports this option and the user specified a value. | +| waitQueueMultiple | Int | The waitQueueMultiple value for this pool. Optional; only required to include if the driver supports this option and the user specified a value. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection pool created for {{serverHost}}:{{serverPort}} using options maxIdleTimeMS={{maxIdleTimeMS}}, +> minPoolSize={{minPoolSize}}, maxPoolSize={{maxPoolSize}}, maxConnecting={{maxConnecting}}, +> waitQueueTimeoutMS={{waitQueueTimeoutMS}}, waitQueueSize={{waitQueueSize}}, waitQueueMultiple={{waitQueueMultiple}} + +#### Pool Ready Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | ----------------------- | +| message | String | "Connection pool ready" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection pool ready for {{serverHost}}:{{serverPort}} + +#### Pool Cleared Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| --------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Connection pool cleared" | +| serviceId | String | The hex string representation of the service ID which the pool was cleared for. Optional; only present in load balanced mode. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection pool for {{serverHost}}:{{serverPort}} cleared for serviceId {{serviceId}} + +#### Pool Closed Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | ------------------------ | +| message | String | "Connection pool closed" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection pool closed for {{serverHost}}:{{serverPort}} + +#### Connection Created Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | -------------- | ----------------------------------------------------------------------------------- | +| message | String | "Connection created" | +| driverConnectionId | Int64 | The driver-generated ID for the connection as defined in [Connection](#connection). | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection created: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +#### Connection Ready Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | ------------------ | ----------------------------------------------------------------------------------- | +| message | String | "Connection ready" | +| driverConnectionId | Int64 | The driver-generated ID for the connection as defined in [Connection](#connection). | +| durationMS | Int32/Int64/Double | `ConnectionReadyEvent.duration` converted to milliseconds. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection ready: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}}, established +> in={{durationMS}} ms + +#### Connection Closed Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Connection closed" | +| driverConnectionId | Int64 | The driver-generated ID for the connection as defined in a [Connection](#connection). | +| reason | String | A string describing the reason the connection was closed. The following strings MUST be used for each possible reason as defined in [Events](#events) above:
- Stale: "Connection became stale because the pool was cleared
- Idle: "Connection has been available but unused for longer than the configured max idle time"
- Error: "An error occurred while using the connection"
- Pool closed: "Connection pool was closed" | +| error | Flexible | If `reason` is `Error`, the associated error.
The type and format of this value is flexible; see the [logging specification](../logging/logging.md#representing-errors-in-log-messages) for details on representing errors in log messages. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection closed: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}}. Reason: +> {{reason}}. Error: {{error}} + +#### Connection Checkout Started Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | ----------------------------- | +| message | String | "Connection checkout started" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Checkout started for connection to {{serverHost}}:{{serverPort}} + +#### Connection Checkout Failed Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| message | String | "Connection checkout failed" | +| reason | String | A string describing the reason checkout. The following strings MUST be used for each possible reason as defined in [Events](#events) above:
- Timeout: "Wait queue timeout elapsed without a connection becoming available"
- ConnectionError: "An error occurred while trying to establish a new connection"
- Pool closed: "Connection pool was closed" | +| error | Flexible | If `reason` is `ConnectionError`, the associated error. The type and format of this value is flexible; see the [logging specification](../logging/logging.md#representing-errors-in-log-messages) for details on representing errors in log messages. | +| durationMS | Int32/Int64/Double | `ConnectionCheckOutFailedEvent.duration` converted to milliseconds. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Checkout failed for connection to {{serverHost}}:{{serverPort}}. Reason: {{reason}}. Error: {{error}}. Duration: +> {{durationMS}} ms + +#### Connection Checked Out + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | ------------------ | ----------------------------------------------------------------------------------- | +| message | String | "Connection checked out" | +| driverConnectionId | Int64 | The driver-generated ID for the connection as defined in [Connection](#connection). | +| durationMS | Int32/Int64/Double | `ConnectionCheckedOutEvent.duration` converted to milliseconds. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection checked out: address={serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}}, +> duration={{durationMS}} ms + +#### Connection Checked In + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------ | -------------- | ----------------------------------------------------------------------------------- | +| message | String | "Connection checked in" | +| driverConnectionId | Int64 | The driver-generated ID for the connection as defined in [Connection](#connection). | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection checked in: address={{serverHost}}:{{serverPort}}, driver-generated ID={{driverConnectionId}} + +### Connection Pool Errors + +A connection pool throws errors in specific circumstances. These Errors MUST be emitted by the pool. Errors SHOULD be +created and dispatched in a manner idiomatic to the Driver and Language. + +```typescript +/** + * Thrown when the driver attempts to check out a + * Connection from a closed Connection Pool + */ +interface PoolClosedError { + message: 'Attempted to check out a Connection from closed connection pool'; + address: ; +} + +/** + * Thrown when the driver attempts to check out a + * Connection from a paused Connection Pool + */ +interface PoolClearedError extends RetryableError { + message: 'Connection pool for was cleared because another operation failed with: '; + address: ; +} + +/** + * Thrown when a driver times out when attempting to check out + * a Connection from a Pool + */ +interface WaitQueueTimeoutError { + message: 'Timed out while checking out a Connection from connection pool'; + address: ; +} +``` + +## Test Plan + +See [tests/README](tests/README.md) + +## Design Rationale + +### Why do we set minPoolSize across all members of a replicaSet, when most traffic will be against a Primary? + +Currently, we are attempting to codify our current pooling behavior with minimal changes, and minPoolSize is currently +uniform across all members of a replicaSet. This has the benefit of offsetting connection swarming during a Primary +Step-Down, which will be further addressed in our [Advanced Pooling Behaviors](#advanced-pooling-behaviors). + +### Why do we have separate ConnectionCreated and ConnectionReady events, but only one ConnectionClosed event? + +ConnectionCreated and ConnectionReady each involve different state changes in the pool. + +- ConnectionCreated adds a new "pending" [Connection](#connection), meaning the totalConnectionCount and + pendingConnectionCount increase by one +- ConnectionReady establishes that the [Connection](#connection) is ready for use, meaning the availableConnectionCount + increases by one + +ConnectionClosed indicates that the [Connection](#connection) is no longer a member of the pool, decrementing +totalConnectionCount and potentially availableConnectionCount. After this point, the [Connection](#connection) is no +longer a part of the pool. Further hypothetical events would not indicate a change to the state of the pool, so they are +not specified here. + +### Why are waitQueueSize and waitQueueMultiple deprecated? + +These options were originally only implemented in three drivers (Java, C#, and Python), and provided little value. While +these fields would allow for faster diagnosis of issues in the connection pool, they would not actually prevent an error +from occurring. + +Additionally, these options have the effect of prioritizing newer requests over older requests, which is not necessarily +the behavior that users want. For example, consider a situation when a WaitQueue is full, and a request for +[Connection](#connection) gets rejected. Then a spot opens in the WaitQueue, and a newer request gets accepted. One may +say that the newer request was prioritized over the older one, which violates the fairness recommendation that the +WaitQueue normally adheres to. + +Because of these issues, it does not make sense to +[go against driver mantras and provide an additional knob](../driver-mantras.md#). We may eventually pursue an +alternative configuration to address wait queue size in [Advanced Pooling Behaviors](#advanced-pooling-behaviors). + +Users that wish to have this functionality can achieve similar results by utilizing other methods to limit concurrency. +Examples include implementing either a thread pool or an operation queue with a capped size in the user application. +Drivers that need to deprecate `waitQueueSize` and/or `waitQueueMultiple` SHOULD refer users to these examples. + +### Why is waitQueueTimeoutMS optional for some drivers? + +We are anticipating eventually introducing a single client-side timeout mechanism, making us hesitant to introduce +another granular timeout control. Therefore, if a driver/language already has an idiomatic way to implement their +timeouts, they should leverage that mechanism over implementing waitQueueTimeoutMS. + +### Why must populating the pool require the use of a background thread or async I/O? + +Without the use of a background thread, the pool is +[populated](#populating-the-pool-with-a-connection-internal-implementation) with enough connections to satisfy +minPoolSize during checkOut. [Connections](#connection) are established as part of populating the pool though, so if +[Connection](#connection) establishment were done in a blocking fashion, the first operations after a clearing of the +pool would experience unacceptably high latency, especially for larger values of minPoolSize. Thus, populating the pool +must occur on a background thread (which is acceptable to block) or via the usage of non-blocking (async) I/O. + +### Why should closing a connection be non-blocking? + +Because idle and perished [Connections](#connection) are cleaned up as part of checkOut, performing blocking I/O while +closing such [Connections](#connection) would block application threads, introducing unnecessary latency. Once a +[Connection](#connection) is marked as "closed", it will not be checked out again, so ensuring the socket is torn down +does not need to happen immediately and can happen at a later time, either via async I/O or a background thread. + +### Why can the pool be paused? + +The distinction between the "paused" state and the "ready" state allows the pool to determine whether or not the +endpoint it is associated with is available or not. This enables the following behaviors: + +1. The pool can halt the creation of background connection establishments until the endpoint becomes available again. + Without the "paused" state, the pool would have no way of determining when to begin establishing background + connections again, so it would just continually attempt, and often fail, to create connections until minPoolSize + was satisfied, even after repeated failures. This could unnecessarily waste resources both server and driver side. +2. The pool can evict requests that enter the WaitQueue after the pool was cleared but before the server was in a known + state again. Such requests can occur when a server is selected at the same time as it becomes marked as Unknown in + highly concurrent workloads. Without the "paused" state, the pool would attempt to service these requests, since it + would assume they were routed to the pool because its endpoint was available, not because of a race between SDAM + and Server Selection. These requests would then likely fail with potentially high latency, again wasting resources + both server and driver side. + +### Why not emit PoolCleared events and log messages when clearing a paused pool? + +If a pool is already paused when it is cleared, that means it was previously cleared and no new connections have been +created since then. Thus, clearing the pool in this case is essentially a no-op, so there is no need to notify any +listeners that it has occurred. The generation is still incremented, however, to ensure future errors that caused the +duplicate clear will stop attempting to clear the pool again. This situation is possible if the pool is cleared by the +background thread after it encounters an error establishing a connection, but the ServerDescription for the endpoint was +not updated accordingly yet. + +### Why does the pool need to support interrupting in use connections as part of its clear logic? + +If a SDAM monitor has observed a network timeout, we assume that all connections including "in use" connections are no +longer healthy. In some cases connections will fail to detect the network timeout fast enough. For example, a server +request can hang at the OS level in TCP retry loop up for 17 minutes before failing. Therefore these connections MUST be +proactively interrupted in the case of a server monitor network timeout. Requesting an immediate background thread run +will speed up this process. + +### Why don't we configure TCP_USER_TIMEOUT? + +Ideally, a reasonable TCP_USER_TIMEOUT can help with detecting stale connections as an alternative to +`interruptInUseConnections` in Clear. Unfortunately this approach is platform dependent and not each driver allows +easily configuring it. For example, C# driver can configure this socket option on linux only with target frameworks +higher or equal to .net 5.0. On macOS, there is no straight equivalent for this option, it's possible that we can find +some equivalent configuration, but this configuration will also require target frameworks higher than or equal to .net +5.0. The advantage of using Background Thread to manage perished connections is that it will work regardless of +environment setup. + +## Backwards Compatibility + +As mentioned in [Deprecated Options](#deprecated-options), some drivers currently implement the options `waitQueueSize` +and/or `waitQueueMultiple`. These options will need to be deprecated and phased out of the drivers that have implemented +them. + +## Reference Implementations + +- JAVA (JAVA-3079) +- RUBY (RUBY-1560) + +## Future Development + +### SDAM + +This specification does not dictate how SDAM Monitoring connections are managed. SDAM specifies that "A monitor SHOULD +NOT use the client's regular Connection pool". Some possible solutions for this include: + +- Having each Endpoint representation in the driver create and manage a separate dedicated [Connection](#connection) for + monitoring purposes +- Having each Endpoint representation in the driver maintain a separate pool of maxPoolSize 1 for monitoring purposes. +- Having each Pool maintain a dedicated [Connection](#connection) for monitoring purposes, with an API to expose that + Connection. + +### Advanced Pooling Behaviors + +This spec does not address all advanced pooling behaviors like predictive pooling or aggressive +[Connection](#connection) creation. Future work may address this. + +### Add support for OP_MSG exhaustAllowed + +Exhaust Cursors may require changes to how we close [Connections](#connection) in the future, specifically to add a way +to close and remove from its pool a [Connection](#connection) which has unread exhaust messages. + +## Changelog + +- 2026-01-12: Clarify handling TLS I/O errors for backpressure error labels. + +- 2025-11-21: Add handling of backpressure error labels. + +- 2025-01-22: Clarify durationMS in logs may be Int32/Int64/Double. + +- 2024-11-27: Relaxed the WaitQueue fairness requirement. + +- 2024-11-01: Fixed race condition in pool-checkout-returned-connection-maxConnecting.yml test. + +- 2024-01-23: Migrated from reStructuredText to Markdown. + +- 2019-06-06: Add "connectionError" as a valid reason for ConnectionCheckOutFailedEvent + +- 2020-09-03: Clarify Connection states and definition. Require the use of a background thread and/or async I/O. Add + tests to ensure ConnectionReadyEvents are fired after ConnectionCreatedEvents. + +- 2020-09-24: Introduce maxConnecting requirement + +- 2020-12-17: Introduce "paused" and "ready" states. Clear WaitQueue on pool clear. + +- 2021-01-12: Clarify "clear" method behavior in load balancer mode. + +- 2021-01-19: Require that timeouts be applied per the client-side operations timeout specification. + +- 2021-04-12: Adding in behaviour for load balancer mode. + +- 2021-06-02: Formalize the behavior of a [Background Thread](#background-thread). + +- 2021-11-08: Make maxConnecting configurable. + +- 2022-04-05: Preemptively cancel in progress operations when SDAM heartbeats timeout. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-10-14: Add connection pool log messages and associated tests. + +- 2023-04-17: Fix duplicate logging test description. + +- 2023-08-04: Add durations to connection pool events. + +- 2023-10-04: Commit to the currently specified requirements regarding durations in events. + +______________________________________________________________________ diff --git a/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst new file mode 100644 index 0000000000..18d7f87d9f --- /dev/null +++ b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst @@ -0,0 +1,3 @@ +.. note:: + This specification has been converted to Markdown and renamed to + `connection-monitoring-and-pooling.md `_. diff --git a/source/connection-monitoring-and-pooling/tests/README.md b/source/connection-monitoring-and-pooling/tests/README.md new file mode 100644 index 0000000000..3d8aee40aa --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/README.md @@ -0,0 +1,27 @@ +# Connection Monitoring and Pooling (CMAP) + +______________________________________________________________________ + +## Introduction + +Drivers MUST implement all of the following types of CMAP tests: + +- Pool unit and integration tests as described in [cmap-format/README](./cmap-format/README.md) +- Pool prose tests as described below in [Prose Tests](#prose-tests) +- Logging tests as described below in [Logging Tests](#logging-tests) + +## Prose Tests + +The following tests have not yet been automated, but MUST still be tested: + +1. All ConnectionPoolOptions MUST be specified at the MongoClient level +2. All ConnectionPoolOptions MUST be the same for all pools created by a MongoClient +3. A user MUST be able to specify all ConnectionPoolOptions via a URI string +4. A user MUST be able to subscribe to Connection Monitoring Events in a manner idiomatic to their language and driver +5. When a check out attempt fails because connection set up throws an error, assert that a ConnectionCheckOutFailedEvent + with reason="connectionError" is emitted. + +## Logging Tests + +Tests for connection pool logging can be found in the `/logging` subdirectory and are written in the +[Unified Test Format](../../unified-test-format/unified-test-format.md). diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/README.md b/source/connection-monitoring-and-pooling/tests/cmap-format/README.md new file mode 100644 index 0000000000..ced96961f5 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/README.md @@ -0,0 +1,167 @@ +# Connection Monitoring and Pooling (CMAP) Unit and Integration Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests that drivers can use to prove their conformance +to the Connection Monitoring and Pooling (CMAP) Spec. + +## Common Test Format + +Each YAML file has the following keys: + +- `version`: A version number indicating the expected format of the spec tests (current version = 1) +- `style`: A string indicating what style of tests this file contains. Contains one of the following: + - `"unit"`: a test that may be run without connecting to a MongoDB deployment. + - `"integration"`: a test that MUST be run against a real MongoDB deployment. +- `description`: A text description of what the test is meant to assert + +## Unit Test Format: + +All Unit Tests have some of the following fields: + +- `poolOptions`: If present, connection pool options to use when creating a pool; both + [standard ConnectionPoolOptions](../../connection-monitoring-and-pooling.md#connection-pool-options) and the + following test-specific options are allowed: + - `backgroundThreadIntervalMS`: A time interval between the end of a + [Background Thread Run](../../connection-monitoring-and-pooling.md#background-thread) and the beginning of the + next Run. If a Connection Pool does not implement a Background Thread, the Test Runner MUST ignore the option. If + the option is not specified, an implementation is free to use any value it finds reasonable. + + Possible values (0 is not allowed): + + - A negative value: never begin a Run. + - A positive value: the interval between Runs in milliseconds. +- `operations`: A list of operations to perform. All operations support the following fields: + - `name`: A string describing which operation to issue. + - `thread`: The name of the thread in which to run this operation. If not specified, runs in the default thread +- `error`: Indicates that the main thread is expected to error during this test. An error may include of the following + fields: + - `type`: the type of error emitted + - `message`: the message associated with that error + - `address`: Address of pool emitting error +- `events`: An array of all connection monitoring events expected to occur while running `operations`. An event may + contain any of the following fields + - `type`: The type of event emitted + - `address`: The address of the pool emitting the event + - `connectionId`: The id of a connection associated with the event + - `duration`: The event duration + - `options`: Options used to create the pool + - `reason`: A reason giving more information on why the event was emitted +- `ignore`: An array of event names to ignore + +Valid Unit Test Operations are the following: + +- `start(target)`: Starts a new thread named `target` + - `target`: The name of the new thread to start +- `wait(ms)`: Sleep the current thread for `ms` milliseconds + - `ms`: The number of milliseconds to sleep the current thread for +- `waitForThread(target)`: wait for thread `target` to finish executing. Propagate any errors to the main thread. + - `target`: The name of the thread to wait for. +- `waitForEvent(event, count, timeout)`: block the current thread until `event` has occurred `count` times + - `event`: The name of the event + - `count`: The number of times the event must occur (counting from the start of the test) + - `timeout`: If specified, time out with an error after waiting for this many milliseconds without seeing the required + events +- `label = pool.checkOut()`: call `checkOut` on pool, returning the checked out connection + - `label`: If specified, associate this label with the returned connection, so that it may be referenced in later + operations +- `pool.checkIn(connection)`: call `checkIn` on pool + - `connection`: A string label identifying which connection to check in. Should be a label that was previously set + with `checkOut` +- `pool.clear()`: call `clear` on Pool + - `interruptInUseConnections`: Determines whether "in use" connections should be also interrupted +- `pool.close()`: call `close` on Pool +- `pool.ready()`: call `ready` on Pool + +## Integration Test Format + +The integration test format is identical to the unit test format with the addition of the following fields to each test: + +- `runOn` (optional): An array of server version and/or topology requirements for which the tests can be run. If the + test environment satisfies one or more of these requirements, the tests may be executed; otherwise, this test should + be skipped. If this field is omitted, the tests can be assumed to have no particular requirements and should be + executed. Each element will have some or all of the following fields: + - `minServerVersion` (optional): The minimum server version (inclusive) required to successfully run the tests. If + this field is omitted, it should be assumed that there is no lower bound on the required server version. + - `maxServerVersion` (optional): The maximum server version (inclusive) against which the tests can be run + successfully. If this field is omitted, it should be assumed that there is no upper bound on the required server + version. +- `failPoint`: optional, a document containing a `configureFailPoint` command to run against the endpoint being used for + the test. +- `poolOptions.appName` (optional): appName attribute to be set in connections, which will be affected by the fail + point. + +## 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`: + +```text +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 +``` + +## Unit Test Runner: + +For the unit tests, the behavior of a Connection is irrelevant beyond the need to asserting `connection.id`. Drivers MAY +use a mock connection class for testing the pool behavior in unit tests + +For each YAML file with `style: unit`: + +- Create a Pool `pool`, subscribe and capture any Connection Monitoring events emitted in order. + - If `poolOptions` is specified, use those options to initialize both pools + - The returned pool must have an `address` set as a string value. +- Process each `operation` in `operations` (on the main thread) + - If a `thread` is specified, the main thread MUST schedule the operation to execute in the corresponding thread. + Otherwise, execute the operation directly in the main thread. +- If `error` is presented + - Assert that an actual error `actualError` was thrown by the main thread + - Assert that `actualError` MATCHES `error` +- Else: + - Assert that no errors were thrown by the main thread +- calculate `actualEvents` as every Connection Event emitted whose `type` is not in `ignore` +- if `events` is not empty, then for every `idx`/`expectedEvent` in `events` + - Assert that `actualEvents[idx]` exists + - Assert that `actualEvents[idx]` MATCHES `expectedEvent` + +It is important to note that the `ignore` list is used for calculating `actualEvents`, but is NOT used for the +`waitForEvent` command + +## Integration Test Runner + +The steps to run the integration tests are the same as those used to run the unit tests with the following +modifications: + +- The integration tests MUST be run against an actual endpoint. If the deployment being tested contains multiple + endpoints, then the runner MUST only use one of them to run the tests against. + +- For each test, if `failPoint` is specified, its value is a `configureFailPoint` command. Run the command on the admin + database of the endpoint being tested to enable the fail point. + +- At the end of each test, any enabled fail point MUST be disabled to avoid spurious failures in subsequent tests. The + fail point may be disabled like so: + + ```javascript + db.adminCommand({ + configureFailPoint: "", + mode: "off" + }); + ``` diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.json b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.json new file mode 100644 index 0000000000..f2d6fb95e9 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "style": "unit", + "description": "must have an ID number associated with it", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionPoolClosed", + "ConnectionReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.yml new file mode 100644 index 0000000000..b4a2cd3440 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-have-id.yml @@ -0,0 +1,29 @@ +version: 1 +style: unit +description: must have an ID number associated with it +operations: + - name: ready + - name: checkOut + - name: checkOut +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionPoolClosed + - ConnectionReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.json b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.json new file mode 100644 index 0000000000..b7c2751dd7 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "style": "unit", + "description": "must have IDs assigned in order of creation", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 2, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 2, + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionPoolClosed", + "ConnectionReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.yml new file mode 100644 index 0000000000..3e352e7160 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/connection-must-order-ids.yml @@ -0,0 +1,29 @@ +version: 1 +style: unit +description: must have IDs assigned in order of creation +operations: + - name: ready + - name: checkOut + - name: checkOut +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 1 + address: 42 + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 2 + address: 42 + - type: ConnectionCheckedOut + connectionId: 2 + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionPoolClosed + - ConnectionReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.json new file mode 100644 index 0000000000..55d0c03752 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "style": "unit", + "description": "must destroy checked in connection if pool has been closed", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "close" + }, + { + "name": "checkIn", + "connection": "conn" + } + ], + "events": [ + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionPoolClosed", + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "reason": "poolClosed", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionReady", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.yml new file mode 100644 index 0000000000..cd6f6bb27c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-closed.yml @@ -0,0 +1,29 @@ +version: 1 +style: unit +description: must destroy checked in connection if pool has been closed +operations: + - name: ready + - name: checkOut + label: conn + - name: close + - name: checkIn + connection: conn +events: + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionPoolClosed + address: 42 + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + - type: ConnectionClosed + connectionId: 1 + reason: poolClosed + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionCreated + - ConnectionReady + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.json new file mode 100644 index 0000000000..6ffb8f53d1 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.json @@ -0,0 +1,50 @@ +{ + "version": 1, + "style": "unit", + "description": "must destroy checked in connection if it is stale", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "clear" + }, + { + "name": "checkIn", + "connection": "conn" + } + ], + "events": [ + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "reason": "stale", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionReady", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.yml new file mode 100644 index 0000000000..f54ae7675d --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-destroy-stale.yml @@ -0,0 +1,29 @@ +version: 1 +style: unit +description: must destroy checked in connection if it is stale +operations: + - name: ready + - name: checkOut + label: conn + - name: clear + - name: checkIn + connection: conn +events: + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + - type: ConnectionClosed + connectionId: 1 + reason: stale + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionCreated + - ConnectionReady + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.json new file mode 100644 index 0000000000..3f37f188c0 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "style": "unit", + "description": "must make valid checked in connection available", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkIn", + "connection": "conn" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionReady", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.yml new file mode 100644 index 0000000000..9dbd5aebe9 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin-make-available.yml @@ -0,0 +1,28 @@ +version: 1 +style: unit +description: must make valid checked in connection available +operations: + - name: ready + - name: checkOut + label: conn + - name: checkIn + connection: conn + - name: checkOut +events: + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + duration: 42 + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + duration: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionCreated + - ConnectionReady + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.json new file mode 100644 index 0000000000..3b40cec6f4 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "style": "unit", + "description": "must have a method of allowing the driver to check in a connection", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkIn", + "connection": "conn" + } + ], + "events": [ + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionReady", + "ConnectionClosed", + "ConnectionCheckOutStarted", + "ConnectionCheckedOut" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.yml new file mode 100644 index 0000000000..b6a4320376 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkin.yml @@ -0,0 +1,21 @@ +version: 1 +style: unit +description: must have a method of allowing the driver to check in a connection +operations: + - name: ready + - name: checkOut + label: conn + - name: checkIn + connection: conn +events: + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionPoolReady + - ConnectionCreated + - ConnectionReady + - ConnectionClosed + - ConnectionCheckOutStarted + - ConnectionCheckedOut diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.json new file mode 100644 index 0000000000..c7e8914d45 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to check out a connection", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionReady", + "connectionId": 1, + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.yml new file mode 100644 index 0000000000..1d94778dd4 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-connection.yml @@ -0,0 +1,23 @@ +version: 1 +style: unit +description: must be able to check out a connection +operations: + - name: ready + - name: checkOut +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 1 + address: 42 + - type: ConnectionReady + connectionId: 1 + address: 42 + duration: 42 + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + duration: 42 +ignore: + - ConnectionPoolReady + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json new file mode 100644 index 0000000000..9a7c0174c5 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json @@ -0,0 +1,83 @@ +{ + "version": 1, + "style": "integration", + "description": "custom maxConnecting is enforced", + "runOn": [ + { + "minServerVersion": "4.4.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 500, + "appName": "PoolCheckoutCustomMaxConnectingIsEnforced" + } + }, + "poolOptions": { + "maxConnecting": 1, + "maxPoolSize": 2, + "waitQueueTimeoutMS": 5000, + "appName": "PoolCheckoutCustomMaxConnectingIsEnforced" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 1 + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "waitForEvent", + "event": "ConnectionReady", + "count": 2 + } + ], + "events": [ + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionReady" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionReady" + } + ], + "ignore": [ + "ConnectionCheckOutStarted", + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionClosed", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.yml new file mode 100644 index 0000000000..b57ee7fd01 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.yml @@ -0,0 +1,52 @@ +version: 1 +style: integration +description: custom maxConnecting is enforced +runOn: + - + minServerVersion: "4.4.0" +failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 500 + appName: "PoolCheckoutCustomMaxConnectingIsEnforced" +poolOptions: + maxConnecting: 1 + # gives opportunity for the checkout in thread2 to establish a new connection, which it must not do until thread1 establishes one + maxPoolSize: 2 + waitQueueTimeoutMS: 5000 + appName: "PoolCheckoutCustomMaxConnectingIsEnforced" +operations: + - name: ready + # thread1 exists to consume the single permit to open a connection, + # so that thread2 would be blocked acquiring a permit, which results in ordering its ConnectionCreated event after + # the ConnectionReady event from thread1. + - name: start + target: thread1 + - name: start + target: thread2 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCreated + count: 1 + - name: checkOut + thread: thread2 + - name: waitForEvent + event: ConnectionReady + count: 2 +events: + - type: ConnectionCreated + - type: ConnectionReady + - type: ConnectionCreated + - type: ConnectionReady +ignore: + - ConnectionCheckOutStarted + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionClosed + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.json new file mode 100644 index 0000000000..614403ef50 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.json @@ -0,0 +1,70 @@ +{ + "version": 1, + "style": "unit", + "description": "must throw error if checkOut is called on a closed pool", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn1" + }, + { + "name": "checkIn", + "connection": "conn1" + }, + { + "name": "close" + }, + { + "name": "checkOut" + } + ], + "error": { + "type": "PoolClosedError", + "message": "Attempted to check out a connection from closed connection pool" + }, + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42, + "connectionId": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckedIn", + "address": 42, + "connectionId": 42 + }, + { + "type": "ConnectionPoolClosed", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "address": 42, + "duration": 42, + "reason": "poolClosed" + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionReady", + "ConnectionClosed" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.yml new file mode 100644 index 0000000000..2d0ce8d111 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-error-closed.yml @@ -0,0 +1,40 @@ +version: 1 +style: unit +description: must throw error if checkOut is called on a closed pool +operations: + - name: ready + - name: checkOut + label: conn1 + - name: checkIn + connection: conn1 + - name: close + - name: checkOut +error: + type: PoolClosedError + message: Attempted to check out a connection from closed connection pool +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedOut + address: 42 + connectionId: 42 + duration: 42 + - type: ConnectionCheckedIn + address: 42 + connectionId: 42 + - type: ConnectionPoolClosed + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutFailed + address: 42 + duration: 42 + reason: poolClosed +ignore: + - ConnectionPoolReady + - ConnectionCreated + - ConnectionReady + - ConnectionClosed diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.json new file mode 100644 index 0000000000..418ec7986f --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.json @@ -0,0 +1,110 @@ +{ + "version": 1, + "style": "integration", + "description": "maxConnecting is enforced", + "runOn": [ + { + "minServerVersion": "4.4.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 800, + "appName": "PoolCheckoutMaxConnectingIsEnforced" + } + }, + "poolOptions": { + "maxPoolSize": 10, + "waitQueueTimeoutMS": 5000, + "appName": "PoolCheckoutMaxConnectingIsEnforced" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 1 + }, + { + "name": "wait", + "ms": 400 + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionReady", + "count": 3 + } + ], + "events": [ + { + "type": "ConnectionCreated", + "address": 42, + "connectionId": 1 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionReady", + "address": 42, + "connectionId": 1 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionReady", + "address": 42 + }, + { + "type": "ConnectionReady", + "address": 42 + } + ], + "ignore": [ + "ConnectionCheckOutStarted", + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionClosed", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.yml new file mode 100644 index 0000000000..1df900d21a --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-is-enforced.yml @@ -0,0 +1,81 @@ +version: 1 +style: integration +description: maxConnecting is enforced +runOn: + - + # required for blockConnection in fail point + minServerVersion: "4.4.0" +failPoint: + configureFailPoint: failCommand + # high amount to ensure not interfered with by monitor checks. + mode: { times: 50 } + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 800 + appName: "PoolCheckoutMaxConnectingIsEnforced" +poolOptions: + maxPoolSize: 10 + waitQueueTimeoutMS: 5000 + appName: "PoolCheckoutMaxConnectingIsEnforced" +operations: + - name: ready + # start 3 threads + - name: start + target: thread1 + - name: start + target: thread2 + - name: start + target: thread3 + # start creating a Connection. This will take a while + # due to the fail point. + - name: checkOut + thread: thread1 + # wait for thread1 to actually start creating a Connection + - name: waitForEvent + event: ConnectionCreated + count: 1 + # wait some more time to ensure thread1 has begun establishing a Connection + - name: wait + ms: 400 + # start 2 check out requests. Only one thread should + # start creating a Connection and the other one should be + # waiting for pendingConnectionCount to be less than maxConnecting, + # only starting once thread1 finishes creating its Connection. + - name: checkOut + thread: thread2 + - name: checkOut + thread: thread3 + # wait until all Connections have been created. + - name: waitForEvent + event: ConnectionReady + count: 3 +events: + # thread1 creates its connection + - type: ConnectionCreated + address: 42 + connectionId: 1 + # either thread2 or thread3 creates its connection + # the other thread is stuck waiting for maxConnecting to come down + - type: ConnectionCreated + address: 42 + # thread1 finishes establishing its connection, freeing + # up the blocked thread to start establishing + - type: ConnectionReady + address: 42 + connectionId: 1 + - type: ConnectionCreated + address: 42 + # the remaining two Connections finish establishing + - type: ConnectionReady + address: 42 + - type: ConnectionReady + address: 42 +ignore: + - ConnectionCheckOutStarted + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionClosed + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.json new file mode 100644 index 0000000000..443104675d --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.json @@ -0,0 +1,106 @@ +{ + "version": 1, + "style": "integration", + "description": "waiting on maxConnecting is limited by WaitQueueTimeoutMS", + "runOn": [ + { + "minServerVersion": "4.4.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 750, + "appName": "PoolCheckoutMaxConnectingTimeout" + } + }, + "poolOptions": { + "maxPoolSize": 10, + "waitQueueTimeoutMS": 50, + "appName": "PoolCheckoutMaxConnectingTimeout" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 2 + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + }, + { + "name": "waitForThread", + "target": "thread3" + } + ], + "error": { + "type": "WaitQueueTimeoutError", + "message": "Timed out while checking out a connection from connection pool" + }, + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "timeout", + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionClosed", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.yml new file mode 100644 index 0000000000..b1d345221c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-maxConnecting-timeout.yml @@ -0,0 +1,72 @@ +version: 1 +style: integration +description: waiting on maxConnecting is limited by WaitQueueTimeoutMS +runOn: + - + # required for blockConnection in fail point + minServerVersion: "4.4.0" +failPoint: + configureFailPoint: failCommand + # high amount to ensure not interfered with by monitor checks. + mode: { times: 50 } + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 750 + appName: "PoolCheckoutMaxConnectingTimeout" +poolOptions: + maxPoolSize: 10 + # Drivers that limit connection establishment by waitQueueTimeoutMS may skip + # this test. While waitQueueTimeoutMS is technically not supposed to limit establishment time, + # it will soon be deprecated, so it is easier for those drivers to just skip this test. + waitQueueTimeoutMS: 50 + appName: "PoolCheckoutMaxConnectingTimeout" +operations: + - name: ready + # start creating two connections simultaneously. + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: start + target: thread2 + - name: checkOut + thread: thread2 + # wait for other two threads to start establishing + - name: waitForEvent + event: ConnectionCreated + count: 2 + # start a third thread that will be blocked waiting for + # one of the other two to finish + - name: start + target: thread3 + - name: checkOut + thread: thread3 + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 + # rejoin thread3, should experience error + - name: waitForThread + target: thread3 +error: + type: WaitQueueTimeoutError + message: Timed out while checking out a connection from connection pool +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutFailed + reason: timeout + address: 42 + duration: 42 +ignore: + - ConnectionCreated + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionClosed + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.json new file mode 100644 index 0000000000..b53b002805 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.json @@ -0,0 +1,90 @@ +{ + "version": 1, + "style": "integration", + "description": "threads blocked by maxConnecting check out minPoolSize connections", + "runOn": [ + { + "minServerVersion": "4.4.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 500, + "appName": "PoolCheckoutMinPoolSizeConnectionMaxConnecting" + } + }, + "poolOptions": { + "minPoolSize": 2, + "maxPoolSize": 3, + "waitQueueTimeoutMS": 5000, + "appName": "PoolCheckoutMinPoolSizeConnectionMaxConnecting" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "wait", + "ms": 200 + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 2 + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckedOut", + "count": 2 + } + ], + "events": [ + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionClosed", + "ConnectionReady", + "ConnectionPoolCreated", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.yml new file mode 100644 index 0000000000..62c6154c3e --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.yml @@ -0,0 +1,65 @@ +version: 1 +style: integration +description: threads blocked by maxConnecting check out minPoolSize connections +runOn: + - + # required for blockConnection in fail point + minServerVersion: "4.4.0" +failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 500 + appName: "PoolCheckoutMinPoolSizeConnectionMaxConnecting" +poolOptions: + # allows both thread1 and the background thread to start opening connections concurrently + minPoolSize: 2 + # gives opportunity for the checkout in thread2 to open a new connection, which it must not do nonetheless + maxPoolSize: 3 + waitQueueTimeoutMS: 5000 + appName: "PoolCheckoutMinPoolSizeConnectionMaxConnecting" +operations: + - name: ready + # thread1 exists to hold on one of the two permits to open a connection (the other one is initially held by the background thread), + # so that thread2 would be blocked acquiring a permit, which opens an opportunity for it to grab the connection newly opened + # by the background thread instead of opening a third connection. + - name: start + target: thread1 + - name: start + target: thread2 + # Ideally, thread1 should be holding for its permit to open a connection till the end of the test, but we cannot express that. + # This delay emulates the above requirement: + # - it is long enough to make sure that the background thread opens a connection before thread1 releases its permit; + # - it is short enough to allow thread2 to become blocked acquiring a permit to open a connection, and then grab the connection + # opened by the background thread, before the background thread releases its permit. + - name: wait + ms: 200 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCreated + count: 2 + - name: checkOut + thread: thread2 + - name: waitForEvent + event: ConnectionCheckedOut + count: 2 +events: + # exactly 2 connections must be created and checked out + - type: ConnectionCreated + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionCheckedOut + address: 42 + - type: ConnectionCheckedOut + address: 42 +ignore: + - ConnectionPoolReady + - ConnectionClosed + - ConnectionReady + - ConnectionPoolCreated + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.json new file mode 100644 index 0000000000..07a4eda629 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.json @@ -0,0 +1,70 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to check out multiple connections at the same time", + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForThread", + "target": "thread1" + }, + { + "name": "waitForThread", + "target": "thread2" + }, + { + "name": "waitForThread", + "target": "thread3" + } + ], + "events": [ + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionPoolReady", + "ConnectionReady", + "ConnectionPoolCreated", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.yml new file mode 100644 index 0000000000..03a355df06 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-multiple.yml @@ -0,0 +1,39 @@ +version: 1 +style: unit +description: must be able to check out multiple connections at the same time +operations: + - name: ready + - name: start + target: thread1 + - name: start + target: thread2 + - name: start + target: thread3 + - name: checkOut + thread: thread1 + - name: checkOut + thread: thread2 + - name: checkOut + thread: thread3 + - name: waitForThread + target: thread1 + - name: waitForThread + target: thread2 + - name: waitForThread + target: thread3 +events: + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 +ignore: + - ConnectionCreated + - ConnectionPoolReady + - ConnectionReady + - ConnectionPoolCreated + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.json new file mode 100644 index 0000000000..0b0fe572ff --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.json @@ -0,0 +1,68 @@ +{ + "version": 1, + "style": "unit", + "description": "must destroy and must not check out an idle connection if found while iterating available connections", + "poolOptions": { + "maxIdleTimeMS": 10, + "backgroundThreadIntervalMS": -1 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkIn", + "connection": "conn" + }, + { + "name": "wait", + "ms": 50 + }, + { + "name": "checkOut" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckedOut", + "count": 2 + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "reason": "idle", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 2, + "address": 42 + } + ], + "ignore": [ + "ConnectionReady", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.yml new file mode 100644 index 0000000000..4df351211c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-idle.yml @@ -0,0 +1,41 @@ +version: 1 +style: unit +description: must destroy and must not check out an idle connection if found while iterating available connections +poolOptions: + maxIdleTimeMS: 10 + backgroundThreadIntervalMS: -1 +operations: + - name: ready + - name: checkOut + label: conn + - name: checkIn + connection: conn + - name: wait + ms: 50 + - name: checkOut + - name: waitForEvent + event: ConnectionCheckedOut + count: 2 +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + # In between these, wait so connection becomes idle + - type: ConnectionClosed + connectionId: 1 + reason: idle + address: 42 + - type: ConnectionCheckedOut + connectionId: 2 + address: 42 +ignore: + - ConnectionReady + - ConnectionPoolReady + - ConnectionCreated + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.json new file mode 100644 index 0000000000..ec76f4e9c8 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.json @@ -0,0 +1,73 @@ +{ + "version": 1, + "style": "unit", + "description": "must destroy and must not check out a stale connection if found while iterating available connections", + "poolOptions": { + "backgroundThreadIntervalMS": -1 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkIn", + "connection": "conn" + }, + { + "name": "clear" + }, + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckedOut", + "count": 2 + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "reason": "stale", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 2, + "address": 42 + } + ], + "ignore": [ + "ConnectionReady", + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.yml new file mode 100644 index 0000000000..02d827b773 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-no-stale.yml @@ -0,0 +1,41 @@ +version: 1 +style: unit +description: must destroy and must not check out a stale connection if found while iterating available connections +poolOptions: + backgroundThreadIntervalMS: -1 +operations: + - name: ready + - name: checkOut + label: conn + - name: checkIn + connection: conn + - name: clear + - name: ready + - name: checkOut + - name: waitForEvent + event: ConnectionCheckedOut + count: 2 +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionClosed + connectionId: 1 + reason: stale + address: 42 + - type: ConnectionCheckedOut + connectionId: 2 + address: 42 +ignore: + - ConnectionReady + - ConnectionPoolReady + - ConnectionCreated + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.json new file mode 100644 index 0000000000..0bac21ae64 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.json @@ -0,0 +1,118 @@ +{ + "version": 1, + "style": "integration", + "description": "threads blocked by maxConnecting check out returned connections", + "runOn": [ + { + "minServerVersion": "4.4.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 750, + "appName": "PoolCheckoutReturnedConnectionMaxConnecting" + } + }, + "poolOptions": { + "maxConnecting": 2, + "maxPoolSize": 10, + "waitQueueTimeoutMS": 5000, + "appName": "PoolCheckoutReturnedConnectionMaxConnecting" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn0" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 4 + }, + { + "name": "wait", + "ms": 100 + }, + { + "name": "checkIn", + "connection": "conn0" + }, + { + "name": "wait", + "ms": 100 + } + ], + "events": [ + { + "type": "ConnectionCreated", + "address": 42, + "connectionId": 1 + }, + { + "type": "ConnectionCheckedOut", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionClosed", + "ConnectionReady", + "ConnectionPoolCreated", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.yml new file mode 100644 index 0000000000..253c34e609 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-checkout-returned-connection-maxConnecting.yml @@ -0,0 +1,86 @@ +version: 1 +style: integration +description: threads blocked by maxConnecting check out returned connections +runOn: + - + # required for blockConnection in fail point + minServerVersion: "4.4.0" +failPoint: + configureFailPoint: failCommand + # high amount to ensure not interfered with by monitor checks. + mode: { times: 50 } + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 750 + appName: "PoolCheckoutReturnedConnectionMaxConnecting" +poolOptions: + maxConnecting: 2 + maxPoolSize: 10 + waitQueueTimeoutMS: 5000 + appName: "PoolCheckoutReturnedConnectionMaxConnecting" +operations: + - name: ready + # check out a connection and hold on to it. + - name: checkOut + label: conn0 + # then start three threads that all attempt to check out. Two threads + # will fill maxConnecting, and the other should be waiting either for + # the other two to finish or for the main thread to check its connection + # back in. + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: start + target: thread2 + - name: checkOut + thread: thread2 + - name: start + target: thread3 + - name: checkOut + thread: thread3 + # wait for all three to start checking out and a little longer + # for the establishments to begin. + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 4 + - name: wait + ms: 100 + # Check original connection back in, so one of the waiting threads can check + # out the idle connection before the two new connections are ready. + - name: checkIn + connection: conn0 + # Wait for 100ms to let one of the blocked checkOut operations complete. + - name: wait + ms: 100 +events: + # main thread checking out a Connection and holding it + - type: ConnectionCreated + address: 42 + connectionId: 1 + - type: ConnectionCheckedOut + address: 42 + # two threads creating their Connections + - type: ConnectionCreated + address: 42 + - type: ConnectionCreated + address: 42 + # main thread checking its Connection back in + - type: ConnectionCheckedIn + connectionId: 1 + address: 42 + # Another thread checks out the returned Connection before the two new + # connections are checked out. + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + # Events after this can come in different orders but still be valid. + # See DRIVERS-2223 for details. +ignore: + - ConnectionPoolReady + - ConnectionClosed + - ConnectionReady + - ConnectionPoolCreated + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.json new file mode 100644 index 0000000000..e6077f12a5 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.json @@ -0,0 +1,105 @@ +{ + "version": 1, + "style": "unit", + "description": "clearing pool clears the WaitQueue", + "poolOptions": { + "maxPoolSize": 1, + "waitQueueTimeoutMS": 30000 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 4 + }, + { + "name": "clear" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 3, + "timeout": 1000 + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionPoolCleared", + "ConnectionPoolCreated", + "ConnectionCreated", + "ConnectionReady", + "ConnectionCheckedIn", + "ConnectionClosed" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.yml new file mode 100644 index 0000000000..388056f4fd --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-clears-waitqueue.yml @@ -0,0 +1,67 @@ +version: 1 +style: unit +description: clearing pool clears the WaitQueue +poolOptions: + maxPoolSize: 1 + waitQueueTimeoutMS: 30000 +operations: + - name: ready + # check out only connection in the pool + - name: checkOut + # start a few threads that all will enter + # the wait queue + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: start + target: thread2 + - name: checkOut + thread: thread2 + - name: start + target: thread3 + - name: checkOut + thread: thread3 + # wait for the 3 threads to start checking out a connection + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 4 + # clear the pool, ejecting the previous three threads + # from the WaitQueue + - name: clear + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 3 + timeout: 1000 +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedOut + address: 42 + duration: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutFailed + reason: connectionError + address: 42 + duration: 42 + - type: ConnectionCheckOutFailed + reason: connectionError + address: 42 + duration: 42 + - type: ConnectionCheckOutFailed + reason: connectionError + address: 42 + duration: 42 +ignore: + - ConnectionPoolReady + - ConnectionPoolCleared + - ConnectionPoolCreated + - ConnectionCreated + - ConnectionReady + - ConnectionCheckedIn + - ConnectionClosed diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.json new file mode 100644 index 0000000000..a8452d8f84 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.json @@ -0,0 +1,79 @@ +{ + "version": 1, + "style": "integration", + "description": "clear with interruptInUseConnections = true closes pending connections", + "runOn": [ + { + "minServerVersion": "4.9.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 10000, + "appName": "PoolClearInterruptingPendingConnections" + } + }, + "poolOptions": { + "minPoolSize": 0, + "appName": "PoolClearInterruptingPendingConnections" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 1 + }, + { + "name": "clear", + "interruptInUseConnections": true + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionPoolCleared", + "interruptInUseConnections": true + }, + { + "type": "ConnectionClosed" + }, + { + "type": "ConnectionCheckOutFailed" + } + ], + "ignore": [ + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.yml new file mode 100644 index 0000000000..43e104fe07 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-interrupting-pending-connections.yml @@ -0,0 +1,44 @@ +version: 1 +style: integration +description: clear with interruptInUseConnections = true closes pending connections +runOn: + - + minServerVersion: "4.9.0" +failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: ["isMaster","hello"] + closeConnection: false + blockConnection: true + blockTimeMS: 10000 + appName: "PoolClearInterruptingPendingConnections" +poolOptions: + minPoolSize: 0 + appName: "PoolClearInterruptingPendingConnections" +operations: + - name: ready + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCreated + count: 1 + - name: clear + interruptInUseConnections: true + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 +events: + - type: ConnectionCheckOutStarted + - type: ConnectionCreated + - type: ConnectionPoolCleared + interruptInUseConnections: true + - type: ConnectionClosed + - type: ConnectionCheckOutFailed +ignore: + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.json new file mode 100644 index 0000000000..239df871b8 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.json @@ -0,0 +1,68 @@ +{ + "version": 1, + "style": "unit", + "description": "pool clear halts background minPoolSize establishments", + "poolOptions": { + "minPoolSize": 1, + "backgroundThreadIntervalMS": 50 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "waitForEvent", + "event": "ConnectionReady", + "count": 1 + }, + { + "name": "clear" + }, + { + "name": "wait", + "ms": 200 + }, + { + "name": "ready" + }, + { + "name": "waitForEvent", + "event": "ConnectionReady", + "count": 2 + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionReady", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionReady", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionClosed" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.yml new file mode 100644 index 0000000000..959c6ccafe --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-min-size.yml @@ -0,0 +1,37 @@ +version: 1 +style: unit +description: pool clear halts background minPoolSize establishments +poolOptions: + minPoolSize: 1 + backgroundThreadIntervalMS: 50 +operations: + - name: ready + - name: waitForEvent + event: ConnectionReady + count: 1 + - name: clear + # ensure no connections created after clear + - name: wait + ms: 200 + - name: ready + - name: waitForEvent + event: ConnectionReady + count: 2 +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionReady + address: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionReady + address: 42 +ignore: + - ConnectionPoolCreated + - ConnectionClosed diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.json new file mode 100644 index 0000000000..847f08d849 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "style": "unit", + "description": "clearing a paused pool emits no events", + "operations": [ + { + "name": "clear" + }, + { + "name": "ready" + }, + { + "name": "clear" + }, + { + "name": "clear" + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.yml new file mode 100644 index 0000000000..5f311f6ff8 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-paused.yml @@ -0,0 +1,15 @@ +version: 1 +style: unit +description: clearing a paused pool emits no events +operations: + - name: clear + - name: ready + - name: clear + - name: clear +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionPoolCleared + address: 42 +ignore: + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.json new file mode 100644 index 0000000000..88c2988ac5 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.json @@ -0,0 +1,72 @@ +{ + "version": 1, + "style": "unit", + "description": "after clear, cannot check out connections until pool ready", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "clear" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + }, + { + "name": "ready" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42, + "connectionId": 42, + "duration": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "address": 42, + "duration": 42, + "reason": "connectionError" + }, + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionReady", + "ConnectionCheckOutStarted", + "ConnectionCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.yml new file mode 100644 index 0000000000..93c85bfbef --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-ready.yml @@ -0,0 +1,39 @@ +version: 1 +style: unit +description: after clear, cannot check out connections until pool ready +operations: + - name: ready + - name: checkOut + - name: clear + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 + - name: ready + - name: checkOut +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCheckedOut + address: 42 + connectionId: 42 + duration: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionCheckOutFailed + address: 42 + duration: 42 + reason: connectionError + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCheckedOut + address: 42 + duration: 42 +ignore: + - ConnectionPoolCreated + - ConnectionReady + - ConnectionCheckOutStarted + - ConnectionCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.json new file mode 100644 index 0000000000..3d7536951d --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.json @@ -0,0 +1,81 @@ +{ + "version": 1, + "style": "unit", + "description": "Pool clear SHOULD schedule the next background thread run immediately (interruptInUseConnections = false)", + "poolOptions": { + "backgroundThreadIntervalMS": 10000 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkIn", + "connection": "conn" + }, + { + "name": "clear", + "interruptInUseConnections": false + }, + { + "name": "waitForEvent", + "event": "ConnectionPoolCleared", + "count": 1, + "timeout": 1000 + }, + { + "name": "waitForEvent", + "event": "ConnectionClosed", + "count": 1, + "timeout": 1000 + }, + { + "name": "close" + } + ], + "events": [ + { + "type": "ConnectionCheckedOut", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 2, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 2, + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "interruptInUseConnections": false + }, + { + "type": "ConnectionClosed", + "connectionId": 2, + "reason": "stale", + "address": 42 + }, + { + "type": "ConnectionPoolClosed", + "address": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionPoolReady", + "ConnectionReady", + "ConnectionCheckOutStarted", + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.yml new file mode 100644 index 0000000000..dcaafec8b5 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.yml @@ -0,0 +1,48 @@ +version: 1 +style: unit +description: Pool clear SHOULD schedule the next background thread run immediately (interruptInUseConnections = false) +poolOptions: + # ensure it's not involved by default + backgroundThreadIntervalMS: 10000 +operations: + - name: ready + - name: checkOut + - name: checkOut + label: conn + - name: checkIn + connection: conn + - name: clear + interruptInUseConnections: false + - name: waitForEvent + event: ConnectionPoolCleared + count: 1 + timeout: 1000 + - name: waitForEvent + event: ConnectionClosed + count: 1 + timeout: 1000 + - name: close +events: + - type: ConnectionCheckedOut + connectionId: 1 + address: 42 + - type: ConnectionCheckedOut + connectionId: 2 + address: 42 + - type: ConnectionCheckedIn + connectionId: 2 + address: 42 + - type: ConnectionPoolCleared + interruptInUseConnections: false + - type: ConnectionClosed + connectionId: 2 + reason: stale + address: 42 + - type: ConnectionPoolClosed + address: 42 +ignore: + - ConnectionCreated + - ConnectionPoolReady + - ConnectionReady + - ConnectionCheckOutStarted + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.json new file mode 100644 index 0000000000..a3d58a2136 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "style": "unit", + "description": "When a pool is closed, it MUST first destroy all available connections in that pool", + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "checkOut" + }, + { + "name": "checkIn", + "connection": "conn" + }, + { + "name": "close" + } + ], + "events": [ + { + "type": "ConnectionCheckedIn", + "connectionId": 2, + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 2, + "reason": "poolClosed", + "address": 42 + }, + { + "type": "ConnectionPoolClosed", + "address": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionPoolReady", + "ConnectionReady", + "ConnectionPoolCreated", + "ConnectionCheckOutStarted", + "ConnectionCheckedOut" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.yml new file mode 100644 index 0000000000..0140cdd198 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close-destroy-conns.yml @@ -0,0 +1,30 @@ +version: 1 +style: unit +description: When a pool is closed, it MUST first destroy all available connections in that pool +operations: + - name: ready + - name: checkOut + - name: checkOut + label: conn + - name: checkOut + - name: checkIn + connection: conn + - name: close +events: + - type: ConnectionCheckedIn + connectionId: 2 + address: 42 + - type: ConnectionClosed + connectionId: 2 + reason: poolClosed + address: 42 + - type: ConnectionPoolClosed + address: 42 +ignore: + - ConnectionCreated + - ConnectionPoolReady + - ConnectionReady + - ConnectionPoolCreated + - ConnectionCheckOutStarted + - ConnectionCheckedOut + diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.json new file mode 100644 index 0000000000..fe083d73e6 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to manually close a pool", + "operations": [ + { + "name": "close" + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionPoolClosed", + "address": 42 + } + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.yml new file mode 100644 index 0000000000..2562224b43 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-close.yml @@ -0,0 +1,11 @@ +version: 1 +style: unit +description: must be able to manually close a pool +operations: + - name: close +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionPoolClosed + address: 42 diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.json new file mode 100644 index 0000000000..e3a1fa8eda --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.json @@ -0,0 +1,133 @@ +{ + "version": 1, + "style": "unit", + "description": "must never exceed maxPoolSize total connections", + "poolOptions": { + "maxPoolSize": 3 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn1" + }, + { + "name": "checkOut" + }, + { + "name": "checkOut", + "label": "conn2" + }, + { + "name": "checkIn", + "connection": "conn2" + }, + { + "name": "checkOut" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 5 + }, + { + "name": "checkIn", + "connection": "conn1" + }, + { + "name": "waitForThread", + "target": "thread1" + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionReady", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.yml new file mode 100644 index 0000000000..fa85d9eb48 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-max-size.yml @@ -0,0 +1,73 @@ +version: 1 +style: unit +description: must never exceed maxPoolSize total connections +poolOptions: + maxPoolSize: 3 +operations: + - name: ready + - name: checkOut + label: conn1 + - name: checkOut + - name: checkOut + label: conn2 + - name: checkIn + connection: conn2 + - name: checkOut + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 5 + - name: checkIn + connection: conn1 + - name: waitForThread + target: thread1 +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 +ignore: + - ConnectionReady + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json new file mode 100644 index 0000000000..fe7489f401 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "style": "integration", + "description": "error during minPoolSize population clears pool", + "runOn": [ + { + "minServerVersion": "4.9.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "errorCode": 91, + "appName": "poolCreateMinSizeErrorTest" + } + }, + "poolOptions": { + "minPoolSize": 1, + "backgroundThreadIntervalMS": 50, + "appName": "poolCreateMinSizeErrorTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "waitForEvent", + "event": "ConnectionPoolCleared", + "count": 1 + }, + { + "name": "wait", + "ms": 200 + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionClosed", + "address": 42, + "connectionId": 42, + "reason": "error" + } + ], + "ignore": [ + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml new file mode 100644 index 0000000000..42cf6e32a3 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml @@ -0,0 +1,40 @@ +version: 1 +style: integration +description: error during minPoolSize population clears pool +runOn: + - + # required for appName in fail point + minServerVersion: "4.9.0" +failPoint: + configureFailPoint: failCommand + # high amount to ensure not interfered with by monitor checks. + mode: { times: 50 } + data: + failCommands: ["isMaster","hello"] + errorCode: 91 + appName: "poolCreateMinSizeErrorTest" +poolOptions: + minPoolSize: 1 + backgroundThreadIntervalMS: 50 + appName: "poolCreateMinSizeErrorTest" +operations: + - name: ready + - name: waitForEvent + event: ConnectionPoolCleared + count: 1 + # ensure pool doesn't start making new connections + - name: wait + ms: 200 +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionClosed + address: 42 + connectionId: 42 + reason: error +ignore: + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.json new file mode 100644 index 0000000000..43118f7841 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to start a pool with minPoolSize connections", + "poolOptions": { + "minPoolSize": 3 + }, + "operations": [ + { + "name": "wait", + "ms": 200 + }, + { + "name": "ready" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 3 + }, + { + "name": "waitForEvent", + "event": "ConnectionReady", + "count": 3 + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + }, + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionReady", + "ConnectionClosed", + "ConnectionCheckOutStarted" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.yml new file mode 100644 index 0000000000..a91e33f7a9 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size.yml @@ -0,0 +1,40 @@ +version: 1 +style: unit +description: must be able to start a pool with minPoolSize connections +poolOptions: + minPoolSize: 3 +operations: + # ensure no connections are created until this pool is ready + - name: wait + ms: 200 + - name: ready + - name: waitForEvent + event: ConnectionCreated + count: 3 + - name: waitForEvent + event: ConnectionReady + count: 3 + - name: checkOut +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + - type: ConnectionCreated + connectionId: 42 + address: 42 + # Ensures that by the time pool is closed, there are at least 3 connections + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 +ignore: + - ConnectionReady + - ConnectionClosed + - ConnectionCheckOutStarted diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.json new file mode 100644 index 0000000000..4e8223f91e --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to start a pool with various options set", + "poolOptions": { + "maxPoolSize": 50, + "minPoolSize": 5, + "maxIdleTimeMS": 100 + }, + "operations": [ + { + "name": "waitForEvent", + "event": "ConnectionPoolCreated", + "count": 1 + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": { + "maxPoolSize": 50, + "minPoolSize": 5, + "maxIdleTimeMS": 100 + } + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.yml new file mode 100644 index 0000000000..32c8d0e54c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-with-options.yml @@ -0,0 +1,21 @@ +version: 1 +style: unit +description: must be able to start a pool with various options set +poolOptions: + maxPoolSize: 50 + minPoolSize: 5 + maxIdleTimeMS: 100 +operations: + - name: waitForEvent + event: ConnectionPoolCreated + count: 1 +events: + - type: ConnectionPoolCreated + address: 42 + options: + maxPoolSize: 50 + minPoolSize: 5 + maxIdleTimeMS: 100 +ignore: + - ConnectionCreated + - ConnectionReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.json new file mode 100644 index 0000000000..8c1f85537f --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "style": "unit", + "description": "must be able to create a pool", + "operations": [ + { + "name": "waitForEvent", + "event": "ConnectionPoolCreated", + "count": 1 + } + ], + "events": [ + { + "type": "ConnectionPoolCreated", + "address": 42, + "options": 42 + } + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.yml new file mode 100644 index 0000000000..f4989e8d4b --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create.yml @@ -0,0 +1,12 @@ +version: 1 +style: unit +description: must be able to create a pool +operations: + - name: waitForEvent + event: ConnectionPoolCreated + count: 1 +events: + - type: ConnectionPoolCreated + address: 42 + options: 42 + diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.json new file mode 100644 index 0000000000..25dfa9c97c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "style": "unit", + "description": "readying a ready pool emits no events", + "operations": [ + { + "name": "ready" + }, + { + "name": "ready" + }, + { + "name": "ready" + }, + { + "name": "clear" + }, + { + "name": "ready" + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionPoolReady", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.yml new file mode 100644 index 0000000000..b3d3ae7ad1 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready-ready.yml @@ -0,0 +1,19 @@ +version: 1 +style: unit +description: readying a ready pool emits no events +operations: + - name: ready + - name: ready + - name: ready + # the first ready after this clear should emit an event + - name: clear + - name: ready +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionPoolCleared + address: 42 + - type: ConnectionPoolReady + address: 42 +ignore: + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.json new file mode 100644 index 0000000000..a90aed04d0 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.json @@ -0,0 +1,59 @@ +{ + "version": 1, + "style": "unit", + "description": "pool starts as cleared and becomes ready", + "operations": [ + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + }, + { + "name": "ready" + }, + { + "name": "checkOut" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "address": 42, + "duration": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.yml new file mode 100644 index 0000000000..2332099393 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-ready.yml @@ -0,0 +1,32 @@ +version: 1 +style: unit +description: pool starts as cleared and becomes ready +operations: + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 + - name: ready + - name: checkOut +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutFailed + reason: connectionError + address: 42 + duration: 42 + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionCheckedOut + address: 42 + duration: 42 +ignore: + - ConnectionPoolCreated + - ConnectionReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.json b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.json new file mode 100644 index 0000000000..88a67c60ec --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.json @@ -0,0 +1,196 @@ +{ + "version": 1, + "style": "unit", + "description": "must issue Connections to threads in the order that the threads entered the queue", + "poolOptions": { + "maxPoolSize": 1, + "waitQueueTimeoutMS": 5000 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn0" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1", + "label": "conn1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 2 + }, + { + "name": "wait", + "ms": 100 + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread2", + "label": "conn2" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 3 + }, + { + "name": "wait", + "ms": 100 + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread3", + "label": "conn3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 4 + }, + { + "name": "wait", + "ms": 100 + }, + { + "name": "start", + "target": "thread4" + }, + { + "name": "checkOut", + "thread": "thread4", + "label": "conn4" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 5 + }, + { + "name": "wait", + "ms": 100 + }, + { + "name": "checkIn", + "connection": "conn0" + }, + { + "name": "waitForThread", + "target": "thread1" + }, + { + "name": "checkIn", + "connection": "conn1" + }, + { + "name": "waitForThread", + "target": "thread2" + }, + { + "name": "checkIn", + "connection": "conn2" + }, + { + "name": "waitForThread", + "target": "thread3" + }, + { + "name": "checkIn", + "connection": "conn3" + }, + { + "name": "waitForThread", + "target": "thread4" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionReady", + "ConnectionClosed", + "ConnectionPoolReady", + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.yml new file mode 100644 index 0000000000..e7027c3304 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-fairness.yml @@ -0,0 +1,126 @@ +version: 1 +style: unit +description: must issue Connections to threads in the order that the threads entered the queue +poolOptions: + maxPoolSize: 1 + waitQueueTimeoutMS: 5000 +operations: + - name: ready + # Check out sole connection in pool + - name: checkOut + label: conn0 + # Create 4 threads, have them all queue up for connections + # Note: this might become non-deterministic depending on how you + # implement your test runner. The goal is for each thread to + # have started and begun checkOut before the next thread starts. + # The sleep operations should make this more consistent. + - name: start + target: thread1 + - name: checkOut + thread: thread1 + label: conn1 + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 2 + # Give thread1 some time to actually enter the wait queue since the + # ConnectionCheckOutStarted event is publish beforehand. + - name: wait + ms: 100 + - name: start + target: thread2 + - name: checkOut + thread: thread2 + label: conn2 + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 3 + # Give thread2 some time to actually enter the wait queue since the + # ConnectionCheckOutStarted event is publish beforehand. + - name: wait + ms: 100 + - name: start + target: thread3 + - name: checkOut + thread: thread3 + label: conn3 + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 4 + # Give thread3 some time to actually enter the wait queue since the + # ConnectionCheckOutStarted event is publish beforehand. + - name: wait + ms: 100 + - name: start + target: thread4 + - name: checkOut + thread: thread4 + label: conn4 + - name: waitForEvent + event: ConnectionCheckOutStarted + count: 5 + # Give thread4 some time to actually enter the wait queue since the + # ConnectionCheckOutStarted event is publish beforehand. + - name: wait + ms: 100 + # From main thread, keep checking in connection and then wait for appropriate thread + # Test will timeout if threads are not enqueued in proper order + - name: checkIn + connection: conn0 + - name: waitForThread + target: thread1 + - name: checkIn + connection: conn1 + - name: waitForThread + target: thread2 + - name: checkIn + connection: conn2 + - name: waitForThread + target: thread3 + - name: checkIn + connection: conn3 + - name: waitForThread + target: thread4 +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 +ignore: + - ConnectionCreated + - ConnectionReady + - ConnectionClosed + - ConnectionPoolReady + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.json b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.json new file mode 100644 index 0000000000..8bd7c49499 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.json @@ -0,0 +1,77 @@ +{ + "version": 1, + "style": "unit", + "description": "must aggressively timeout threads enqueued longer than waitQueueTimeoutMS", + "poolOptions": { + "maxPoolSize": 1, + "waitQueueTimeoutMS": 50 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn0" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + }, + { + "name": "checkIn", + "connection": "conn0" + }, + { + "name": "waitForThread", + "target": "thread1" + } + ], + "error": { + "type": "WaitQueueTimeoutError", + "message": "Timed out while checking out a connection from connection pool" + }, + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckedOut", + "connectionId": 42, + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "timeout", + "address": 42, + "duration": 42 + }, + { + "type": "ConnectionCheckedIn", + "connectionId": 42, + "address": 42 + } + ], + "ignore": [ + "ConnectionCreated", + "ConnectionReady", + "ConnectionClosed", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.yml new file mode 100644 index 0000000000..fdb3b58629 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/wait-queue-timeout.yml @@ -0,0 +1,50 @@ +version: 1 +style: unit +description: must aggressively timeout threads enqueued longer than waitQueueTimeoutMS +poolOptions: + maxPoolSize: 1 + waitQueueTimeoutMS: 50 +operations: + - name: ready + # Check out only possible connection + - name: checkOut + label: conn0 + # Start a thread, have it enter the wait queue + - name: start + target: thread1 + - name: checkOut + thread: thread1 + # Wait for other thread to time out, then check in connection + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 + - name: checkIn + connection: conn0 + # Rejoin thread1, should experience error + - name: waitForThread + target: thread1 +error: + type: WaitQueueTimeoutError + message: Timed out while checking out a connection from connection pool +events: + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckedOut + connectionId: 42 + address: 42 + duration: 42 + - type: ConnectionCheckOutStarted + address: 42 + - type: ConnectionCheckOutFailed + reason: timeout + address: 42 + duration: 42 + - type: ConnectionCheckedIn + connectionId: 42 + address: 42 +ignore: + - ConnectionCreated + - ConnectionReady + - ConnectionClosed + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-logging.json b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.json new file mode 100644 index 0000000000..72103b3cab --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.json @@ -0,0 +1,523 @@ +{ + "description": "connection-logging", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient" + } + } + ], + "tests": [ + { + "description": "Create a client, run a command, and close the client", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "connection": "debug" + } + } + } + ] + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection ready", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked out", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked in", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked out", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked in", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection closed", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "Connection pool was closed" + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool closed", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Connection checkout fails due to error establishing connection", + "runOnRequirements": [ + { + "auth": true, + "minServerVersion": "4.0" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryReads": false, + "appname": "clientAppName", + "heartbeatFrequencyMS": 10000 + }, + "observeLogMessages": { + "connection": "debug" + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "clientAppName" + } + } + } + }, + { + "name": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool cleared", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection closed", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "An error occurred while using the connection", + "error": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout failed", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "An error occurred while trying to establish a new connection", + "error": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml new file mode 100644 index 0000000000..49868a062e --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml @@ -0,0 +1,224 @@ +description: "connection-logging" + +schemaVersion: "1.13" + +runOnRequirements: + - topologies: + - single # The number of log messages is different for each topology since there is a connection pool per host. + +createEntities: + - client: + id: &failPointClient failPointClient + +tests: + - description: "Create a client, run a command, and close the client" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + connection: debug + - name: listDatabases + object: *client + arguments: + filter: {} + - name: close + object: *client + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checkout started" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection ready" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - level: debug + component: connection + data: + message: "Connection checked out" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - level: debug + component: connection + data: + message: "Connection checked in" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # The next three expected logs are for ending a session. + - level: debug + component: connection + data: + message: "Connection checkout started" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checked out" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + - level: debug + component: connection + data: + message: "Connection checked in" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection closed" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "Connection pool was closed" + + - level: debug + component: connection + data: + message: "Connection pool closed" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # This test exists to provide coverage of checkout failed and pool cleared events. + - description: "Connection checkout fails due to error establishing connection" + runOnRequirements: + - auth: true + minServerVersion: "4.0" # failCommand was added to mongod in 4.0 + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + retryReads: false + appname: &clientAppName clientAppName + # use a high heartbeatFrequencyMS to avoid a successful monitor check marking the pool as + # ready (and emitting another event) during the course of test execution. + heartbeatFrequencyMS: 10000 + observeLogMessages: + connection: debug + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["saslContinue"] + closeConnection: true + appName: *clientAppName + - name: listDatabases + object: *client + arguments: + filter: {} + expectError: + isClientError: true + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection checkout started" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection pool cleared" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection closed" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "An error occurred while using the connection" + error: { $$exists: true } + + - level: debug + component: connection + data: + message: "Connection checkout failed" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + reason: "An error occurred while trying to establish a new connection" + error: { $$exists: true } + durationMS: { $$type: [double, int, long] } diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json b/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json new file mode 100644 index 0000000000..7055a54869 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.json @@ -0,0 +1,458 @@ +{ + "description": "connection-pool-options", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "tests": [ + { + "description": "Options should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "connectionReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "minPoolSize": 1, + "maxPoolSize": 5, + "maxIdleTimeMS": 10000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "minPoolSize": 1, + "maxPoolSize": 5, + "maxIdleTimeMS": 10000 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection ready", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "maxConnecting should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "maxConnecting": 5 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "maxConnecting": 5 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueTimeoutMS should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueTimeoutMS": 10000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueTimeoutMS": 10000 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueSize should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueSize": 100 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueSize": 100 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "waitQueueMultiple should be included in connection pool created message when specified", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent" + ], + "observeLogMessages": { + "connection": "debug" + }, + "uriOptions": { + "waitQueueSize": 5 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "waitQueueMultiple": 5 + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml b/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml new file mode 100644 index 0000000000..790dab6fed --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-pool-options.yml @@ -0,0 +1,254 @@ +description: "connection-pool-options" + +schemaVersion: "1.13" + +runOnRequirements: + - topologies: + - single # The number of log messages is different for each topology since there is a connection pool per host. + +tests: + - description: "Options should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait on a connection ready event for the connection created in the background. + # This is to avoid raciness around whether the background thread has created the connection + # (and whether corresponding log messages have been generated) by the time log message assertions + # are made. + observeEvents: + - connectionReadyEvent + observeLogMessages: + connection: debug + uriOptions: + minPoolSize: 1 + maxPoolSize: 5 + maxIdleTimeMS: 10000 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + minPoolSize: 1 + maxPoolSize: 5 + maxIdleTimeMS: 10000 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection created" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - level: debug + component: connection + data: + message: "Connection ready" + driverConnectionId: { $$type: [int, long] } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + durationMS: { $$type: [double, int, long] } + + # Drivers who have not done DRIVERS-1943 will need to skip this test. + - description: "maxConnecting should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + maxConnecting: 5 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + maxConnecting: 5 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueTimeoutMS will need to skip this test. + - description: "waitQueueTimeoutMS should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueTimeoutMS: 10000 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueTimeoutMS: 10000 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueSize will need to skip this test. + - description: "waitQueueSize should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueSize: 100 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueSize: 100 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + # Drivers that do not support waitQueueMultiple will need to skip this test. + - description: "waitQueueMultiple should be included in connection pool created message when specified" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + # Observe and wait for a poolReadyEvent so we can ensure the pool has been created and is + # ready by the time we assert on log messages, in order to avoid raciness around which messages + # are emitted. + observeEvents: + - poolReadyEvent + observeLogMessages: + connection: debug + uriOptions: + waitQueueSize: 5 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + expectLogMessages: + - client: *client + messages: + - level: debug + component: connection + data: + message: "Connection pool created" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + waitQueueMultiple: 5 + + - level: debug + component: connection + data: + message: "Connection pool ready" + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } diff --git a/source/connection-string/connection-string-spec.md b/source/connection-string/connection-string-spec.md new file mode 100644 index 0000000000..e30bb6b1ff --- /dev/null +++ b/source/connection-string/connection-string-spec.md @@ -0,0 +1,498 @@ +# Connection String Spec + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +The purpose of the Connection String is to provide a machine readable way of configuring a MongoClient, allowing users +to configure and change the connection to their MongoDB system without requiring any application code changes. + +This specification defines how the connection string is constructed and parsed. The aim is not to list all of connection +string options and their semantics. Rather it defines the syntax of the connection string, including rules for parsing, +naming conventions for options, and standard data types. + +It should be noted that while the connection string specification is inspired by the URI specification as described in +[RFC 3986](http://tools.ietf.org/html/rfc3986) and uses similar terminology, it does not conform to that specification. + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### General Syntax + +In general we follow URI style conventions, however unlike a URI the connection string supports multiple hosts. + +```text +mongodb://username:password@example.com:27017,example2.com:27017,...,example.comN:27017/database?key=value&keyN=valueN +\_____/ \_______________/ \_________/ \__/ \_______________________________________/ \______/ \_/ \___/ + | | | | | | | | +Scheme | Host Port Alternative host identifiers | Key Value + Userinfo \_____________/ | \_______/ + | Auth database | + Host Identifier Key Value Pair + \_______________________________________________________/ \___________________/ + | | + Host Information Connection Options +``` + +### Scheme + +The scheme `mongodb` represents that this is a connection string for a MongoClient. + +Other schemes are also possible and are introduced through additional specifications. These additional schemes build on +top of the connection string as documented in this specification. + +For example the `mongodb+srv` specification, introduced with +[Initial DNS Seedlist Discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md), obtains +information from DNS in addition to just the connection string. + +### Userinfo (optional) + +The user information if present, is followed by a commercial at-sign ("@") that delimits it from the host. + +A password may be supplied as part of the user information and is anything after the first colon (":") up until the end +of the user information. + +RFC 3986 has guidance for encoding user information in +[Section 2.1 ("Percent-Encoding")](https://tools.ietf.org/html/rfc3986#section-2.1), +[Section 2.2 ("Reserved Characters")](https://tools.ietf.org/html/rfc3986#section-2.2), and +[Section 3.2.1 ("User Information")](https://tools.ietf.org/html/rfc3986#section-3.2.1). + +Specifically, Section 3.2.1 provides for the following allowed characters: + +```text +userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) +``` + +If the user information contains an at-sign ("@"), more than one colon (":"), or a percent-sign ("%") that does not +match the rules for "pct-encoded", then an exception MUST be thrown informing the user that the username and password +must be URL encoded. + +Above and beyond that restriction, drivers SHOULD require connection string user information to follow the "userinfo" +encoding rules of RFC 3986 and SHOULD throw an exception if disallowed characters are detected. However, for +backwards-compatibility reasons, drivers MAY allow reserved characters other than "@" and ":" to be present in user +information without percent-encoding. + +### Host Information + +Unlike a standard URI, the connection string allows for identifying multiple hosts. The host information section of the +connection string MAY be delimited by the trailing slash ("/") or end of string. + +The host information must contain at least one host identifier but may contain more (see the alternative hosts / ports +in the general syntax diagram above). Multiple host identifiers are delimited by a comma (","). + +#### Host Identifier + +A host identifier consists of a host and an optional port. + +##### Host + +Identifies a server address to connect to. It can identify either a hostname, IP address, IP Literal, or UNIX domain +socket. For definitions of hostname, IP address and IP Literal formats see +[RFC 3986 Section 3.2.2](http://tools.ietf.org/html/rfc3986#section-3.2.2) . + +UNIX domain sockets MUST end in ".sock" and MUST be URL encoded, for example: + +```text +mongodb://user:pass@%2Ftmp%2Fmongodb-27017.sock/authDB?replicaSet=rs +``` + +The host information cannot contain an unescaped slash ("/"), if it does then an exception MUST be thrown informing +users that paths must be URL encoded. For example: + +```text +Unsupported host '/tmp/mongodb-27017.sock', UNIX socket domain paths must be URL encoded. +``` + +Support for UNIX domain sockets and IP Literals is OPTIONAL. + +Unsupported host types MUST throw an exception informing the user they are not supported. + +This specification does not define how host types should be differentiated (e.g. determining if a parsed host string is +a socket path or hostname). It is merely concerned with extracting the host identifiers from the URI. + +##### Port (optional) + +The port is an integer between 1 and 65535 (inclusive) that identifies the port to connect to. See +[RFC 3986 3.2.3](http://tools.ietf.org/html/rfc3986#section-3.2.3). + +### Auth Database (optional) + +The database to authenticate against. If provided it is everything after the Host Information (ending with "/") and up +to the first question mark ("?") or end of string. The auth database MUST be URL decoded by the parser. + +The following characters MUST NOT appear in the database name, once it has been decoded: slash ("/"), backslash (""), +space (" "), double-quote ("""), or dollar sign ("$"). The MongoDB Manual +[says that](https://www.mongodb.com/docs/manual/reference/limits/#Restrictions-on-Field-Names) period (".") is also +prohibited, but drivers MAY allow periods in order to express a namespace (database and collection name, perhaps +containing multiple periods) in this part of the URL. + +The presence of the auth database component without other credential data such as Userinfo or authentication parameters +in connection options MUST NOT be interpreted as a request for authentication. + +### Connection Options (optional) + +Any extra options to configure the MongoClient connection can be specified in the connection options part of the +connection string. If provided, it is everything after the Host Information (ending with "/"), optional auth database, +and first question mark ("?") to the end of the string. Connection Options consist of an ordered list of Key Value Pairs +that are delimited by an ampersand ("&"). A delimiter of a semi colon (";") MAY also be supported for connection options +for legacy reasons. + +#### Key Value Pair + +A key value pair represents the option key and its associated value. The key is everything up to the first equals sign +("=") and the value is everything afterwards. Key values contain the following information: + +- Key: + +The connection option's key string. Keys should be normalised and character case should be ignored. + +- Value: (optional) + +The value if provided otherwise it defaults to an empty string. + +### Defining connection options + +Connection option key values MUST be defined in the relevant specification that describes the usage of the key and +value. The value data type MUST also be defined there. The value's default value SHOULD also be defined if it is +relevant. + +#### Keys + +Keys are strings and the character case must be normalized by lower casing the uppercase ASCII characters A through Z; +other characters are left as-is. + +When defining and documenting keys, specifications should follow the camelCase naming convention with the first letter +in lowercase, snake_case MUST not be used. Keys that aren't supported by a driver MUST be ignored. + +Keys that aren't supported by a driver MUST be ignored. A WARN level logging message MUST be issued for unsupported +keys. For example: + +```text +Unsupported option 'connectMS'. +``` + +Keys should be descriptive and follow existing conventions: + +##### Time based keys + +If a key represents a unit of time it MUST end with that unit of time. + +Key authors SHOULD follow the existing convention of defaulting to using milliseconds as the unit of time (e.g. +`connectionTimeoutMS`). + +#### Values + +The values in connection options MUST be URL decoded by the parser. The values can represent the following data types: + +- Strings: The value + +- Integer: The value parsed as a integer. If the value is the empty string, the key MUST be ignored. + +- Boolean: "true" and "false" strings MUST be supported. If the value is the empty string, the key MUST be ignored. + + - For legacy reasons it is RECOMMENDED that alternative values for true and false be supported: + - true: "1", "yes", "y" and "t" + - false: "0", "-1", "no", "n" and "f". + + Alternative values are deprecated and MUST be removed from documentation and examples. + + If any of these alternative values are used, drivers MUST log a deprecation notice or issue a logging message at the + WARNING level (as appropriate for your language). For example: + + ```text + Deprecated boolean value for "journal" : "1", please update to "journal=true" + ``` + +- Lists: Repeated keys represent a list in the Connection String consisting of the corresponding values in the same + order as they appear in the Connection String. For example: + + ```text + ?readPreferenceTags=dc:ny,rack:1&readPreferenceTags=dc:ny&readPreferenceTags= + ``` + +- Key value pairs: A value that represents one or more key and value pairs. Multiple key value pairs are delimited by a + comma (","). The key is everything up to the first colon sign (":") and the value is everything afterwards. + + For example: + + ```text + ?readPreferenceTags=dc:ny,rack:1 + ``` + + Drivers MUST handle unencoded colon signs (":") within the value. For example, given the connection string option: + + ```text + authMechanismProperties=TOKEN_RESOURCE:mongodb://foo + ``` + + the driver MUST interpret the key as `TOKEN_RESOURCE` and the value as `mongodb://foo`. + + For any option key-value pair that may contain a comma (such as `TOKEN_RESOURCE`), drivers MUST document that: a value + containing a comma (",") MUST NOT be provided as part of the connection string. This prevents use of values that + would interfere with parsing. + +Any invalid Values for a given key MUST be ignored and MUST log a WARN level message. For example: + +```text +Unsupported value for "fsync" : "ifPossible" +``` + +### Repeated Keys + +If a key is repeated and the corresponding data type is not a List then the precedence of which key value pair will be +used is undefined except where defined otherwise by the [URI options spec](../uri-options/uri-options.md). + +Where possible, a warning SHOULD be raised to inform the user that multiple options were found for the same value. + +### Deprecated Key Value Pairs + +If a key name was deprecated due to renaming it MUST still be supported. Users aren't expected to be vigilant on changes +to key names. + +If the renamed key is also defined in the connection string the deprecated key MUST NOT be applied and a WARN level +message MUST be logged. For example: + +```text +Deprecated key "wtimeout" present and ignored as found replacement "wtimeoutms" value. +``` + +Deprecated keys MUST log a WARN level message informing the user that the option is deprecated and supply the +alternative key name. For example: + +```text +Deprecated key "wtimeout" has been replaced with "wtimeoutms" +``` + +### Legacy support + +Semi colon (";") query parameter delimiters and alternative string representations of Boolean values MAY be supported +only for legacy reasons. + +As these options are not standard they might not be supported across all drivers. As such, these alternatives MUST NOT +be used as general examples or documentation. + +### Language specific connection options + +Connection strings are a mechanism to configure a MongoClient outside the user's application. As each driver may have +language specific configuration options, those options SHOULD also be supported via the connection string. Where +suitable, specifications MUST be updated to reflect new options. + +Keys MUST follow existing connection option naming conventions as defined above. Values MUST also follow the existing, +specific data types. + +Any options that are not supported MUST raise a WARN log level as described in the keys section. + +### Connection options precedence + +If a driver allows URI options to be specified outside of the connection string (e.g. dictionary parameter to the +MongoClient constructor) it MUST document the precedence rules between all such mechanisms. For instance, a driver MAY +allow a value for option `foo` in a dictionary parameter to override the value of `foo` in the connection string (or +vice versa) so long as that behavior is documented. + +### Test Plan + +See the [README](tests/README.md) for tests. + +### Motivation for Change + +The motivation for this specification is to publish how connection strings are formed and how they should be parsed. +This is important because although the connection string follows the terminology of a standard URI format (as described +in [RFC 3986](http://tools.ietf.org/html/rfc3986)) it is not a standard URI and cannot be parsed by standard URI +parsers. + +The specification also formalizes the standard practice for the definition of new connection options and where the +responsibility for their definition should be. + +### Design Rationale + +The rationale for the Connection String is to provide a consistent, driver independent way to define the connection to a +MongoDB system outside of the application. The connection string is an existing standard and is already widely used. + +### Backwards Compatibility + +Connection Strings are already generally supported across languages and driver implementations. As the responsibility +for the definitions of connections options relies on the specifications defining them, there should be no backwards +compatibility breaks caused by this specification with regards to options. + +Connection options precedence may cause some backwards incompatibilities as existing driver behaviour differs here. As +such, it is currently only a recommendation. + +### Reference Implementation + +The Java driver implements a `ConnectionString` class for the parsing of the connection string; however, it does not +support UNIX domain sockets. The Python driver's `uri_parser` module implements connection string parsing for both hosts +and UNIX domain sockets. + +The following example parses a connection string into its components and can be used as a guide. + +Given the string `mongodb://foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock/admin?w=1`: + +1. Validate and remove the scheme prefix `mongodb://`, leaving: + `foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock/admin?w=1` +2. Split the string by the first, unescaped `/` (if any), yielding: + 1. User information and host identifiers: `foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock`. + 2. Auth database and connection options: `admin?w=1`. +3. Split the user information and host identifiers string by the last, unescaped `@`, yielding: + 1. User information: `foo:bar%3A`. + 2. Host identifiers: `mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock`. +4. Validate, split (if applicable), and URL decode the user information. In this example, the username and password + would be `foo` and `bar:`, respectively. +5. Validate, split, and URL decode the host identifiers. In this example, the hosts would be + `["mongodb.example.com", "/tmp/mongodb-27018.sock"]`. +6. Split the auth database and connection options string by the first, unescaped `?`, yielding: + 1. Auth database: `admin`. + 2. Connection options: `w=1`. +7. URL decode the auth database. In this example, the auth database is `admin`. +8. Validate the database contains no prohibited characters. +9. Validate, split, and URL decode the connection options. In this example, the connection options are `{w: 1}`. + +### Q&A + +Q: What about existing Connection Options that aren't currently defined in a specification + +Ideally all MongoClient options would already belong in their relevant specifications. As we iterate and produce more +specifications these options should be covered. + +Q: Why is it recommended that Connection Options take precedence over application set options + +This is only a recommendation but the reasoning is application code is much harder to change across deployments. By +making the Connection String take precedence from outside the application it would be easier for the application to be +portable across environments. The order of precedence of MongoClient hosts and options is recommended to be from low to +high: + +1. Default values +2. MongoClient hosts and options +3. Connection String hosts and options + +Q: Why WARN level warning on unknown options rather than throwing an exception + +It is responsible to inform users of possible misconfigurations and both methods achieve that. However, there are +conflicting requirements of a Connection String. One goal is that any given driver should be configurable by a +connection string but different drivers and languages have different feature sets. Another goal is that Connection +Strings should be portable and as such some options supported by language X might not be relevant to language Y. Any +given driver does not know is an option is specific to a different driver or is misspelled or just not supported. So the +only way to stay portable and support configuration of all options is to not throw an exception but rather log a +warning. + +Q: How long should deprecation options be supported + +This is not declared in this specification. It's not deemed responsible to give a single timeline for how long +deprecated options should be supported. As such any specifications that deprecate options that do have the context of +the decision should provide the timeline. + +Q: Why can I not use a standard URI parser + +The connection string format does not follow the standard URI format (as described in +[RFC 3986](http://tools.ietf.org/html/rfc3986)) we differ in two key areas: + +1. Hosts + + The connection string allows for multiple hosts for high availability reasons but standard URI's only ever define a + single host. + +2. Query Parameters / Connection Options + + The connection string provides a concreted definition on how the Connection Options are parsed, including definitions + of different data types. The [RFC 3986](http://tools.ietf.org/html/rfc3986) only defines that they are `key=value` + pairs and gives no instruction on parsing. In fact different languages handle the parsing of query parameters in + different ways and as such there is no such thing as a standard URI parser. + +Q: Can the connection string contain non-ASCII characters + +The connection string can contain non-ASCII characters. The connection string is text, which can be encoded in any way +appropriate for the application (e.g. the C Driver requires you to pass it a UTF-8 encoded connection string). + +Q: Why does reference implementation check for a `.sock` suffix when parsing a socket path and possible auth database + +To simplify parsing of a socket path followed by an auth database, we rely on MongoDB's +[naming restrictions](https://www.mongodb.com/docs/manual/reference/limits/#naming-restrictions)), which do not allow +database names to contain a dot character, and the fact that socket paths must end with `.sock`. This allows us to +differentiate the last part of a socket path from a database name. While we could immediately rule out an auth database +on the basis of the dot alone, this specification is primarily concerned with breaking down the components of a URI +(e.g. hosts, auth database, options) in a deterministic manner, rather than applying strict validation to those parts +(e.g. host types, database names, allowed values for an option). Additionally, some drivers might allow a namespace +(e.g. `"db.collection"`) for the auth database part, so we do not want to be more strict than is necessary for parsing. + +Q: Why throw an exception if the userinfo contains a percent sign ("%"), at-sign ("@"), or more than one colon (":") + +This is done to help users format the connection string correctly. Although at-signs ("@") or colons (":") in the +username must be URL encoded, users may not be aware of that requirement. Take the following example: + +```text +mongodb://anne:bob:pass@localhost:27017 +``` + +Is the username `anne` and the password `bob:pass` or is the username `anne:bob` and the password `pass`? Accepting this +as the userinfo could cause authentication to fail, causing confusion for the user as to why. Allowing unescaped at-sign +and percent symbols would invite further ambiguity. By throwing an exception users are made aware and then update the +connection string so to be explicit about what forms the username and password. + +Q: Why must UNIX domain sockets be URL encoded + +This has been done to reduce ambiguity between the socket name and the database name. Take the following example: + +```text +mongodb:///tmp/mongodb.sock/mongodb.sock +``` + +Is the host `/tmp/mongodb.sock` and the auth database `mongodb.sock` or does the connection string just contain the host +`/tmp/mongodb.sock/mongodb.sock` and no auth database? By enforcing URL encoding on UNIX domain sockets it makes users +be explicit about the host and the auth database. By requiring an exception to be thrown when the host contains a slash +("/") users can be informed on how to migrate their connection strings. + +Q: Why must the auth database be URL decoded by the parser + +On Linux systems database names can contain a question mark ("?"), in these rare cases the auth database must be URL +encoded. This disambiguates between the auth database and the connection options. Take the following example: + +```text +mongodb://localhost/admin%3F?w=1 +``` + +In this case the auth database would be `admin?` and the connection options `w=1`. + +Q: How should the space character be encoded in a connection string + +Space characters SHOULD be encoded as `%20` rather than `+`, this will be portable across all implementations. +Implementations MAY support decoding `+` into a space, as many languages treat strings as `x-www-form-urlencoded` data +by default. + +## Changelog + +- 2024-05-29: Clarify handling of key-value pairs and add specification test. + +- 2024-02-15: Migrated from reStructuredText to Markdown. + +- 2016-07-22: In Port section, clarify that zero is not an acceptable port. + +- 2017-01-09: In Userinfo section, clarify that percent signs must be encoded. + +- 2017-06-10: In Userinfo section, require username and password to be fully URI encoded, not just "%", "@", and ":". In + Auth Database, list the prohibited characters. In Reference Implementation, split at the first "/", not the last. + +- 2018-01-09: Clarified that space characters should be encoded to `%20`. + +- 2018-06-04: Revised Userinfo section to provide an explicit list of allowed characters and clarify rules for + exceptions. + +- 2019-02-04: In Repeated Keys section, clarified that the URI options spec may override the repeated key behavior + described here for certain options. + +- 2019-03-04: Require drivers to document option precedence rules + +- 2019-04-26: Database name in URI alone does not trigger authentication + +- 2020-01-21: Clarified how empty values in a connection string are parsed. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-12-27: Note that host information ends with a "/" character in connection options description. + +- 2023-08-02: Make delimiting slash between host information and connection options optional and update tests diff --git a/source/connection-string/connection-string-spec.rst b/source/connection-string/connection-string-spec.rst index 8a71026bac..474eac5bf2 100644 --- a/source/connection-string/connection-string-spec.rst +++ b/source/connection-string/connection-string-spec.rst @@ -1,386 +1,6 @@ .. role:: javascript(code) :language: javascript -====================== -Connection String Spec -====================== - -:Spec: 104 -:Title: Connection String Spec -:Authors: Ross Lawley -:Advisors: \A. Jesse Jiryu Davis, Jeremy Mikola, Anna Herlihy -:Status: Approved -:Type: Standards -:Last Modified: June 04, 2018 -:Version: 1.3 - -.. contents:: - --------- - -Abstract -======== - -The purpose of the Connection String is to provide a machine readable way of configuring a MongoClient, allowing users to configure and change the connection to their MongoDB system without requiring any application code changes. - -This specification defines how the connection string is constructed and parsed. The aim is not to list all of connection string options and their semantics. Rather it defines the syntax of the connection string, including rules for parsing, naming conventions for options, and standard data types. - -It should be noted that while the connection string specification is inspired by the URI specification as described in `RFC 3986 `_ and uses similar terminology, it does not conform to that specification. - ------------ -Definitions ------------ - -META ----- - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in `RFC 2119 `_. - --------------- -General Syntax --------------- - -In general we follow URI style conventions, however unlike a URI the -connection string supports multiple hosts. - -.. code:: prettyprint - - mongodb://username:password@example.com:27017,example2.com:27017,...,example.comN:27017/database?key=value&keyN=valueN - \_____/ \_______________/ \_________/ \__/ \_______________________________________/ \______/ \_/ \___/ - | | | | | | | | - Scheme | Host Port Alternative host identifiers | Key Value - Userinfo \_____________/ | \_______/ - | Auth database | - Host Identifier Key Value Pair - \_______________________________________________________/ \___________________/ - | | - Host Information Connection Options - - ------- -Scheme ------- -The scheme ``mongodb`` represents that this is a connection string for a MongoClient. - -Other schemes are also possible and are introduced through additional -specifications. These additional schemes build on top of the connection string -as documented in this specification. - -For example the ``mongodb+srv`` specification, introduced with `Initial DNS -Seedlist Discovery`_, obtains information from DNS in addition to just the -connection string. - -.. _`Initial DNS Seedlist Discovery`: ../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst - -------------------- -Userinfo (optional) -------------------- -The user information if present, is followed by a commercial at-sign ("@") that delimits it from the host. - -A password may be supplied as part of the user information and is anything after the first colon (":") up until the end of the user information. - -RFC 3986 has guidance for encoding user information in `Section 2.1 ("Percent-Encoding") `_, `Section 2.2 ("Reserved Characters") `_, and `Section 3.2.1 ("User Information") `_. - -Specifically, Section 3.2.1 provides for the following allowed characters:: - - userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) - -If the user information contains an at-sign ("@"), more than one colon (":"), or a percent-sign ("%") that does not match the rules for "pct-encoded", then an exception MUST be thrown informing the user that the username and password must be URL encoded. - -Above and beyond that restriction, drivers SHOULD require connection string user information to follow the "userinfo" encoding rules of RFC 3986 and SHOULD throw an exception if disallowed characters are detected. However, for backwards-compatibility reasons, drivers MAY allow reserved characters other than "@" and ":" to be present in user information without percent-encoding. - ----------------- -Host Information ----------------- -Unlike a standard URI, the connection string allows for identifying multiple hosts. The host information section of the connection string is delimited by the trailing slash ("/") or end of string. - -The host information must contain at least one host identifier but may contain more (see the alternative hosts / ports in the general syntax diagram above). Multiple host identifiers are delimited by a comma (","). - -Host Identifier ---------------- -A host identifier consists of a host and an optional port. - -Host -~~~~ -Identifies a server address to connect to. It can identify either a hostname, IP address, IP Literal, or UNIX domain socket. For definitions of hostname, IP address and IP Literal formats see `RFC 3986 `_ . - -UNIX domain sockets MUST end in ".sock" and MUST be URL encoded, for example:: - - mongodb://user:pass@%2Ftmp%2Fmongodb-27017.sock/authDB?replicaSet=rs - -The host information cannot contain an unescaped slash ("/"), if it does then an exception MUST be thrown informing users that paths must be URL encoded. For example:: - - Unsupported host '/tmp/mongodb-27017.sock', UNIX socket domain paths must be URL encoded. - -Support for UNIX domain sockets and IP Literals is OPTIONAL. - -Unsupported host types MUST throw an exception informing the user they are not supported. - -This specification does not define how host types should be differentiated (e.g. determining if a parsed host string is a socket path or hostname). It is merely concerned with extracting the host identifiers from the URI. - -Port (optional) -~~~~~~~~~~~~~~~ -The port is an integer between 1 and 65535 (inclusive) that identifies the port to connect to. See `RFC 3986 `_ . - -.. _database contains no prohibited characters: - ------------------------- -Auth Database (optional) ------------------------- -The database to authenticate against. If provided it is everything after the Host Information (ending with "/") and up to the first question mark ("?") or end of string. The auth database MUST be URL decoded by the parser. - -The following characters MUST NOT appear in the database name, once it has been decoded: slash ("/"), backslash ("\\"), space (" "), double-quote ("""), or dollar sign ("$"). The MongoDB Manual `says that `_ period (".") is also prohibited, but drivers MAY allow periods in order to express a namespace (database and collection name, perhaps containing multiple periods) in this part of the URL. - ------------------------------ -Connection Options (optional) ------------------------------ - -Any extra options to configure the MongoClient connection can be specified in the connection options part of the connection string. If provided, it is everything after the Host Information, optional auth database, and first question mark ("?") to the end of the -string. Connection Options consist of an ordered list of Key Value Pairs that are delimited by an ampersand ("&"). A delimiter of a semi colon (";") MAY also be supported for connection options for legacy reasons. - -Key Value Pair --------------- -A key value pair represents the option key and its associated value. The key is everything up to the first equals sign ("=") and the value is everything afterwards. Key values contain the following information: - -- Key: - The connection option's key string. Keys should be normalised and - character case should be ignored. -- Value: (optional) - The value if provided otherwise it defaults to an empty string. - ---------------------------- -Defining connection options ---------------------------- -Connection option key values MUST be defined in the relevant specification that describes the usage of the key and value. The value data type MUST also be defined there. The value's default value SHOULD also be defined if it is relevant. - -Keys ----- -Keys are strings and the character case must be normalized by lower casing the uppercase ASCII characters A through Z; other characters are left as-is. - -When defining and documenting keys, specifications should follow the camelCase naming convention with the first letter in lowercase, snake\_case MUST not be used. Keys that aren't supported by a driver MUST be ignored. - -A WARN level logging message MUST be issued. For example:: - - Unsupported option 'connectMS'. Keys should be descriptive and follow existing conventions: - - -Time based keys -~~~~~~~~~~~~~~~ -If a key represents a unit of time it MUST end with that unit of time. - -Key authors SHOULD follow the existing convention of defaulting to using milliseconds as the unit of time (e.g. `connectionTimeoutMS`). - -Values ------- -The values in connection options MUST be URL decoded by the parser. The values can represent the following data types: - -- Strings: - The value -- Integer: - The value parsed as a integer -- Boolean: - "true" and "false" strings MUST be supported. - - - For legacy reasons it is RECOMMENDED that alternative values for true and false be supported: - - - true: "1", "yes", "y" and "t" - - false: "0", "-1", "no", "n" and "f". - - Alternative values are deprecated and MUST be removed from documentation and examples. - - If any of these alternative values are used, drivers MUST log a deprecation notice or issue a logging message at the WARNING level (as appropriate for your language). For example:: - - Deprecated boolean value for "journal" : "1", please update to "journal=true" - -- Lists: - Repeated keys represent a list in the Connection String consisting of the corresponding values in the same order as they appear in the Connection String. For example:: - - ?readPreferenceTags=dc:ny,rack:1&readPreferenceTags=dc:ny&readPreferenceTags= -- Key value pairs: - A value that represents one or more key and value pairs. Multiple key value pairs are delimited by a comma (","). The key is everything up to the first colon sign (":") and the value is everything afterwards. If any keys or values containing a comma (",") or a colon (":") they must be URL encoded. For example:: - - ?readPreferenceTags=dc:ny,rack:1 - -Any invalid Values for a given key MUST be ignored and MUST log a WARN level message. For example:: - - Unsupported value for "fsync" : "ifPossible" - -------------- -Repeated Keys -------------- -If a key is repeated and the corresponding data type is not a List then the precedence of which key value pair will be used is undefined. - -Where possible, a warning SHOULD be raised to inform the user that multiple options were found for the same value. - --------------------------- -Deprecated Key Value Pairs --------------------------- -If a key name was deprecated due to renaming it MUST still be supported. Users aren't expected to be vigilant on changes to key names. - -If the renamed key is also defined in the connection string the deprecated key MUST NOT be applied and a WARN level message MUST be logged. For example:: - - Deprecated key "wtimeout" present and ignored as found replacement "wtimeoutms" value. - -Deprecated keys MUST log a WARN level message informing the user that the option is deprecated and supply the alternative key name. For example:: - - Deprecated key "wtimeout" has been replaced with "wtimeoutms" - --------------- -Legacy support --------------- - -Semi colon (";") query parameter delimiters and alternative string representations of Boolean values MAY be supported only for legacy reasons. - -As these options are not standard they might not be supported across all drivers. As such, these alternatives MUST NOT be used as general examples or documentation. - ------------------------------------- -Language specific connection options ------------------------------------- - -Connection strings are a mechanism to configure a MongoClient outside the user's application. As each driver may have language specific configuration options, those options SHOULD also be supported via the connection string. Where suitable, specifications MUST be updated to reflect new options. - -Keys MUST follow existing connection option naming conventions as defined above. Values MUST also follow the existing, specific data types. - -Any options that are not supported MUST raise a WARN log level as described in the keys section. - ------------------------------ -Connection options precedence ------------------------------ - -As the connection string is designed as a mechanism outside of an application to define and change MongoClient configuration, it is RECOMMENDED that the connection string and its defined options take precedence over any MongoClient Options defined in the application, which take precedence over default values. - ---------- -Test Plan ---------- - -See the `README `_ for tests. - ---------------------- -Motivation for Change ---------------------- -The motivation for this specification is to publish how connection strings are formed and how they should be parsed. This is important because although the connection string follows the terminology of a standard URI format (as described in `RFC 3986 `_) it is not a standard URI and cannot be parsed by standard URI parsers. - -The specification also formalizes the standard practice for the definition of new connection options and where the responsibility for their definition should be. - ----------------- -Design Rationale ----------------- -The rationale for the Connection String is to provide a consistent, driver independent way to define the connection to a MongoDB system outside of the application. The connection string is an existing standard and is already widely used. - ------------------------ -Backwards Compatibility ------------------------ -Connection Strings are already generally supported across languages and driver implementations. As the responsibility for the definitions of connections options relies on the specifications defining them, there should be no backwards compatibility breaks caused by this specification with regards to options. - -Connection options precedence may cause some backwards incompatibilities as existing driver behaviour differs here. As such, it is currently only a recommendation. - ------------------------- -Reference Implementation ------------------------- -The Java driver implements a ``ConnectionString`` class for the parsing of the connection string; however, it does not support UNIX domain sockets. The Python driver's ``uri_parser`` module implements connection string parsing for both hosts and UNIX domain sockets. - -The following example parses a connection string into its components and can be used as a guide. - -Given the string ``mongodb://foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock/admin?w=1``: - -1. Validate and remove the scheme prefix ``mongodb://``, leaving: ``foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock/admin?w=1`` - -2. Split the string by the first, unescaped ``/`` (if any), yielding: - - 1. User information and host identifers: ``foo:bar%3A@mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock``. - - 2. Auth database and connection options: ``admin?w=1``. - -3. Split the user information and host identifiers string by the last, unescaped ``@``, yielding: - - 1. User information: ``foo:bar%3A``. - - 2. Host identifiers: ``mongodb.example.com,%2Ftmp%2Fmongodb-27018.sock``. - -4. Validate, split (if applicable), and URL decode the user information. In this example, the username and password would be ``foo`` and ``bar:``, respectively. - -5. Validate, split, and URL decode the host identifiers. In this example, the hosts would be ``["mongodb.example.com", "/tmp/mongodb-27018.sock"]``. - -6. Split the auth database and connection options string by the first, unescaped ``?``, yielding: - - 1. Auth database: ``admin``. - - 2. Connection options: ``w=1``. - -7. URL decode the auth database. In this example, the auth database is ``admin``. - -8. Validate the `database contains no prohibited characters`_. - -9. Validate, split, and URL decode the connection options. In this example, the connection options are ``{w: 1}``. - ---- -Q&A ---- - -Q: What about existing Connection Options that aren't currently defined in a specification? - Ideally all MongoClient options would already belong in their relevant specifications. As we iterate and produce more specifications these options should be covered. - -Q: Why is it recommended that Connection Options take precedence over application set options? - This is only a recommendation but the reasoning is application code is much harder to change across deployments. By making the Connection String take precedence from outside the application it would be easier for the application to be portable across environments. The order of precedence of MongoClient hosts and options is recommended to be from low to high: - - 1. Default values - 2. MongoClient hosts and options - 3. Connection String hosts and options - -Q: Why WARN level warning on unknown options rather than throwing an exception? - It is responsible to inform users of possible misconfigurations and both methods achieve that. However, there are conflicting requirements of a Connection String. One goal is that any given driver should be configurable by a connection string but different drivers and languages have different feature sets. Another goal is that Connection Strings should be portable and as such some options supported by language X might not be relevant to language Y. Any given driver does not know is an option is specific to a different driver or is misspelled or just not supported. So the only way to stay portable and support configuration of all options is to not throw an exception but rather log a warning. - -Q: How long should deprecation options be supported? - This is not declared in this specification. It's not deemed responsible to give a single timeline for how long deprecated options should be supported. As such any specifications that deprecate options that do have the context of the decision should provide the timeline. - -Q: Why can I not use a standard URI parser? - The connection string format does not follow the standard URI format (as described in `RFC 3986 `_) we differ in two key areas: - - 1. Hosts - The connection string allows for multiple hosts for high availability reasons but standard URI's only ever define a single host. - - 2. Query Parameters / Connection Options - The connection string provides a concreted definition on how the Connection Options are parsed, including definitions of different data types. The `RFC 3986 `_ only defines that they are `key=value` pairs and gives no instruction on parsing. In fact different languages handle the parsing of query parameters in different ways and as such there is no such thing as a standard URI parser. - -Q: Can the connection string contain non-ASCII characters? - The connection string can contain non-ASCII characters. The connection string is text, which can be encoded in any way appropriate for the application (e.g. the C Driver requires you to pass it a UTF-8 encoded connection string). - -Q: Why does reference implementation check for a ``.sock`` suffix when parsing a socket path and possible auth database? - To simplify parsing of a socket path followed by an auth database, we rely on MongoDB's `naming restrictions `_), which do not allow database names to contain a dot character, and the fact that socket paths must end with ``.sock``. This allows us to differentiate the last part of a socket path from a database name. While we could immediately rule out an auth database on the basis of the dot alone, this specification is primarily concerned with breaking down the components of a URI (e.g. hosts, auth database, options) in a deterministic manner, rather than applying strict validation to those parts (e.g. host types, database names, allowed values for an option). Additionally, some drivers might allow a namespace (e.g. ``"db.collection"``) for the auth database part, so we do not want to be more strict than is necessary for parsing. - -Q: Why throw an exception if the userinfo contains a percent sign ("%"), at-sign ("@"), or more than one colon (":")? - This is done to help users format the connection string correctly. Although at-signs ("@") or colons (":") in the username must be URL encoded, users may not be aware of that requirement. Take the following example:: - - mongodb://anne:bob:pass@localhost:27017 - - Is the username ``anne`` and the password ``bob:pass`` or is the username ``anne:bob`` and the password ``pass``? Accepting this as the userinfo could cause authentication to fail, causing confusion for the user as to why. Allowing unescaped at-sign and percent symbols would invite further ambiguity. By throwing an exception users are made aware and then update the connection string so to be explicit about what forms the username and password. - -Q: Why must UNIX domain sockets be URL encoded? - This has been done to reduce ambiguity between the socket name and the database name. Take the following example:: - - mongodb:///tmp/mongodb.sock/mongodb.sock - - Is the host ``/tmp/mongodb.sock`` and the auth database ``mongodb.sock`` or does the connection string just contain the host ``/tmp/mongodb.sock/mongodb.sock`` and no auth database? By enforcing URL encoding on UNIX domain sockets it makes users be explicit about the host and the auth database. By requiring an exception to be thrown when the host contains a slash ("/") users can be informed on how to migrate their connection strings. - -Q: Why must the auth database be URL decoded by the parser? - On Linux systems database names can contain a question mark ("?"), in these rare cases the auth database must be URL encoded. This disambiguates between the auth database and the connection options. Take the following example:: - - mongodb://localhost/admin%3F?w=1 - - In this case the auth database would be ``admin?`` and the connection options ``w=1``. - -Q: How should the space character be encoded in a connection string? - Space characters SHOULD be encoded as ``%20`` rather than ``+``, this will be portable across all implementations. Implementations MAY support decoding ``+`` into a space, as many languages treat strings as ``x-www-form-urlencoded`` data by default. - -------- -Changes -------- - -- 2016-07-22: In Port section, clarify that zero is not an acceptable port. -- 2017-01-09: In Userinfo section, clarify that percent signs must be encoded. -- 2017-06-10: In Userinfo section, require username and password to be fully URI - encoded, not just "%", "@", and ":". In Auth Database, list the prohibited - characters. In Reference Implementation, split at the first "/", not the last. -- 2018-01-09: Clarified that space characters should be encoded to ``%20``. -- 2018-06-04: Revised Userinfo section to provide an explicit list of allowed - characters and clarify rules for exceptions. +.. note:: + This specification has been converted to Markdown and renamed to + `connection-string-spec.md `_. diff --git a/source/connection-string/tests/README.md b/source/connection-string/tests/README.md new file mode 100644 index 0000000000..c40d23aef6 --- /dev/null +++ b/source/connection-string/tests/README.md @@ -0,0 +1,55 @@ +# Connection String Tests + +The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their +conformance to the Connection String Spec. + +As the spec is primarily concerned with parsing the parts of a URI, these tests do not focus on host and option +validation. Where necessary, the tests use options known to be (un)supported by drivers to assert behavior such as +issuing a warning on repeated option keys. As such these YAML tests are in no way a replacement for more thorough +testing. However, they can provide an initial verification of your implementation. + +## Version + +Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +## Format + +Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which +have the following keys: + +- `description`: A string describing the test. +- `uri`: A string containing the URI to be parsed. +- `valid:` A boolean indicating if the URI should be considered valid. +- `warning:` A boolean indicating whether URI parsing should emit a warning (independent of whether or not the URI is + valid). +- `hosts`: An array of host objects, each of which have the following keys: + - `type`: A string denoting the type of host. Possible values are "ipv4", "ip_literal", "hostname", and "unix". + Asserting the type is *optional*. + - `host`: A string containing the parsed host. + - `port`: An integer containing the parsed port number. +- `auth`: An object containing the following keys: + - `username`: A string containing the parsed username. For auth mechanisms that do not utilize a password, this may be + the entire `userinfo` token (as discussed in [RFC 2396](https://www.ietf.org/rfc/rfc2396.txt)). + - `password`: A string containing the parsed password. + - `db`: A string containing the parsed authentication database. For legacy implementations that support namespaces + (databases and collections) this may be the full namespace eg: `.` +- `options`: An object containing key/value pairs for each parsed query string option. + +If a test case includes a null value for one of these keys (e.g. `auth: ~`, `port: ~`), no assertion is necessary. This +both simplifies parsing of the test files (keys should always exist) and allows flexibility for drivers that might +substitute default values *during* parsing (e.g. omitted `port` could be parsed as 27017). + +The `valid` and `warning` fields are boolean in order to keep the tests flexible. We are not concerned with asserting +the format of specific error or warnings messages strings. + +### Use as unit tests + +Testing whether a URI is valid or not should simply be a matter of checking whether URI parsing (or MongoClient +construction) raises an error or exception. Testing for emitted warnings may require more legwork (e.g. configuring a +log handler and watching for output). + +Not all drivers may be able to directly assert the hosts, auth credentials, and options. Doing so may require exposing +the driver's URI parsing component. + +The file `valid-db-with-dotted-name.yml` is a special case for testing drivers that allow dotted namespaces, instead of +only database names, in the Auth Database portion of the URI. diff --git a/source/connection-string/tests/README.rst b/source/connection-string/tests/README.rst deleted file mode 100644 index f221600b2d..0000000000 --- a/source/connection-string/tests/README.rst +++ /dev/null @@ -1,73 +0,0 @@ -======================= -Connection String Tests -======================= - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the Connection String Spec. - -As the spec is primarily concerned with parsing the parts of a URI, these tests -do not focus on host and option validation. Where necessary, the tests use -options known to be (un)supported by drivers to assert behavior such as issuing -a warning on repeated option keys. As such these YAML tests are in no way a -replacement for more thorough testing. However, they can provide an initial -verification of your implementation. - -Version -------- - -Files in the "specifications" repository have no version scheme. They are not -tied to a MongoDB server version. - -Format ------- - -Each YAML file contains an object with a single ``tests`` key. This key is an -array of test case objects, each of which have the following keys: - -- ``description``: A string describing the test. -- ``uri``: A string containing the URI to be parsed. -- ``valid:`` A boolean indicating if the URI should be considered valid. -- ``warning:`` A boolean indicating whether URI parsing should emit a warning - (independent of whether or not the URI is valid). -- ``hosts``: An array of host objects, each of which have the following keys: - - - ``type``: A string denoting the type of host. Possible values are "ipv4", - "ip_literal", "hostname", and "unix". Asserting the type is *optional*. - - ``host``: A string containing the parsed host. - - ``port``: An integer containing the parsed port number. -- ``auth``: An object containing the following keys: - - - ``username``: A string containing the parsed username. For auth mechanisms - that do not utilize a password, this may be the entire ``userinfo`` token - (as discussed in `RFC 2396 `_). - - ``password``: A string containing the parsed password. - - ``db``: A string containing the parsed authentication database. For legacy - implementations that support namespaces (databases and collections) this may - be the full namespace eg: ``.`` -- ``options``: An object containing key/value pairs for each parsed query string - option. - -If a test case includes a null value for one of these keys (e.g. ``auth: ~``, -``port: ~``), no assertion is necessary. This both simplifies parsing of the -test files (keys should always exist) and allows flexibility for drivers that -might substitute default values *during* parsing (e.g. omitted ``port`` could be -parsed as 27017). - -The ``valid`` and ``warning`` fields are boolean in order to keep the tests -flexible. We are not concerned with asserting the format of specific error or -warnings messages strings. - -Use as unit tests -================= - -Testing whether a URI is valid or not should simply be a matter of checking -whether URI parsing (or MongoClient construction) raises an error or exception. -Testing for emitted warnings may require more legwork (e.g. configuring a log -handler and watching for output). - -Not all drivers may be able to directly assert the hosts, auth credentials, and -options. Doing so may require exposing the driver's URI parsing component. - -The file valid-db-with-dotted-name.yml is a special case for testing drivers -that allow dotted namespaces, instead of only database names, in the Auth -Database portion of the URI. diff --git a/source/connection-string/tests/invalid-uris.json b/source/connection-string/tests/invalid-uris.json index 2a182fac7e..a7accbd27d 100644 --- a/source/connection-string/tests/invalid-uris.json +++ b/source/connection-string/tests/invalid-uris.json @@ -162,15 +162,6 @@ "auth": null, "options": null }, - { - "description": "Missing delimiting slash between hosts and options", - "uri": "mongodb://example.com?w=1", - "valid": false, - "warning": null, - "hosts": null, - "auth": null, - "options": null - }, { "description": "Incomplete key value pair for option", "uri": "mongodb://example.com/?w", @@ -269,6 +260,24 @@ "hosts": null, "auth": null, "options": null + }, + { + "description": "Username with password containing an unescaped percent sign and an escaped one", + "uri": "mongodb://user%20%:password@localhost", + "valid": false, + "warning": null, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Username with password containing an unescaped percent sign (non hex digit)", + "uri": "mongodb://user%w:password@localhost", + "valid": false, + "warning": null, + "hosts": null, + "auth": null, + "options": null } ] } diff --git a/source/connection-string/tests/invalid-uris.yml b/source/connection-string/tests/invalid-uris.yml index 766be5282c..dd4d4ce31c 100644 --- a/source/connection-string/tests/invalid-uris.yml +++ b/source/connection-string/tests/invalid-uris.yml @@ -143,14 +143,6 @@ tests: hosts: ~ auth: ~ options: ~ - - - description: "Missing delimiting slash between hosts and options" - uri: "mongodb://example.com?w=1" - valid: false - warning: ~ - hosts: ~ - auth: ~ - options: ~ - description: "Incomplete key value pair for option" uri: "mongodb://example.com/?w" @@ -239,3 +231,21 @@ tests: hosts: ~ auth: ~ options: ~ + + - + description: "Username with password containing an unescaped percent sign and an escaped one" + uri: "mongodb://user%20%:password@localhost" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + + - + description: "Username with password containing an unescaped percent sign (non hex digit)" + uri: "mongodb://user%w:password@localhost" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ diff --git a/source/connection-string/tests/valid-auth.json b/source/connection-string/tests/valid-auth.json index 4f684ff185..60f63f4e3f 100644 --- a/source/connection-string/tests/valid-auth.json +++ b/source/connection-string/tests/valid-auth.json @@ -220,29 +220,8 @@ "options": null }, { - "description": "Escaped user info and database (MONGODB-CR)", - "uri": "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR", - "valid": true, - "warning": false, - "hosts": [ - { - "type": "ipv4", - "host": "127.0.0.1", - "port": null - } - ], - "auth": { - "username": "$am", - "password": "f:zzb@z/z=", - "db": "admin?" - }, - "options": { - "authmechanism": "MONGODB-CR" - } - }, - { - "description": "Subdelimiters in user/pass don't need escaping (MONGODB-CR)", - "uri": "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=MONGODB-CR", + "description": "Subdelimiters in user/pass don't need escaping (PLAIN)", + "uri": "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=PLAIN", "valid": true, "warning": false, "hosts": [ @@ -258,7 +237,7 @@ "db": "admin" }, "options": { - "authmechanism": "MONGODB-CR" + "authmechanism": "PLAIN" } }, { @@ -284,7 +263,7 @@ }, { "description": "Escaped username (GSSAPI)", - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authMechanism=GSSAPI", "valid": true, "warning": false, "hosts": [ @@ -303,7 +282,8 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true + "SERVICE_HOST": "example.com", + "CANONICALIZE_HOST_NAME": "forward" } } }, diff --git a/source/connection-string/tests/valid-auth.yml b/source/connection-string/tests/valid-auth.yml index 01c866ee9e..02ed287428 100644 --- a/source/connection-string/tests/valid-auth.yml +++ b/source/connection-string/tests/valid-auth.yml @@ -173,24 +173,8 @@ tests: db: "my=db" options: ~ - - description: "Escaped user info and database (MONGODB-CR)" - uri: "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR" - valid: true - warning: false - hosts: - - - type: "ipv4" - host: "127.0.0.1" - port: ~ - auth: - username: "$am" - password: "f:zzb@z/z=" - db: "admin?" - options: - authmechanism: "MONGODB-CR" - - - description: "Subdelimiters in user/pass don't need escaping (MONGODB-CR)" - uri: "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=MONGODB-CR" + description: "Subdelimiters in user/pass don't need escaping (PLAIN)" + uri: "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=PLAIN" valid: true warning: false hosts: @@ -203,7 +187,7 @@ tests: password: "!$&'()*+,;=" db: "admin" options: - authmechanism: "MONGODB-CR" + authmechanism: "PLAIN" - description: "Escaped username (MONGODB-X509)" uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509" @@ -222,7 +206,7 @@ tests: authmechanism: "MONGODB-X509" - description: "Escaped username (GSSAPI)" - uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI" + uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authMechanism=GSSAPI" valid: true warning: false hosts: @@ -238,7 +222,8 @@ tests: authmechanism: "GSSAPI" authmechanismproperties: SERVICE_NAME: "other" - CANONICALIZE_HOST_NAME: true + SERVICE_HOST: "example.com" + CANONICALIZE_HOST_NAME: "forward" - description: "At-signs in options aren't part of the userinfo" uri: "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset" diff --git a/source/connection-string/tests/valid-options.json b/source/connection-string/tests/valid-options.json index 4c2bded9e7..fce53873a6 100644 --- a/source/connection-string/tests/valid-options.json +++ b/source/connection-string/tests/valid-options.json @@ -2,7 +2,7 @@ "tests": [ { "description": "Option names are normalized to lowercase", - "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR", + "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=PLAIN", "valid": true, "warning": false, "hosts": [ @@ -18,7 +18,44 @@ "db": "admin" }, "options": { - "authmechanism": "MONGODB-CR" + "authmechanism": "PLAIN" + } + }, + { + "description": "Missing delimiting slash between hosts and options", + "uri": "mongodb://example.com?tls=true", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "tls": true + } + }, + { + "description": "Colon in a key value pair", + "uri": "mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster,ENVIRONMENT:azure", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "authmechanismProperties": { + "TOKEN_RESOURCE": "mongodb://test-cluster", + "ENVIRONMENT": "azure" + } } } ] diff --git a/source/connection-string/tests/valid-options.yml b/source/connection-string/tests/valid-options.yml index e1b94039c8..c5d61f974c 100644 --- a/source/connection-string/tests/valid-options.yml +++ b/source/connection-string/tests/valid-options.yml @@ -1,7 +1,7 @@ tests: - description: "Option names are normalized to lowercase" - uri: "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR" + uri: "mongodb://alice:secret@example.com/admin?AUTHMechanism=PLAIN" valid: true warning: false hosts: @@ -14,4 +14,32 @@ tests: password: "secret" db: "admin" options: - authmechanism: "MONGODB-CR" + authmechanism: "PLAIN" + - + description: "Missing delimiting slash between hosts and options" + uri: "mongodb://example.com?tls=true" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: + tls: true + - + description: Colon in a key value pair + uri: mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster,ENVIRONMENT:azure + valid: true + warning: false + hosts: + - + type: hostname + host: example.com + port: ~ + auth: ~ + options: + authmechanismProperties: + TOKEN_RESOURCE: 'mongodb://test-cluster' + ENVIRONMENT: azure diff --git a/source/connection-string/tests/valid-unix_socket-absolute.json b/source/connection-string/tests/valid-unix_socket-absolute.json index 5bb02476eb..66491db13b 100644 --- a/source/connection-string/tests/valid-unix_socket-absolute.json +++ b/source/connection-string/tests/valid-unix_socket-absolute.json @@ -30,6 +30,21 @@ "auth": null, "options": null }, + { + "description": "Unix domain socket (mixed case)", + "uri": "mongodb://%2Ftmp%2FMongoDB-27017.sock", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "unix", + "host": "/tmp/MongoDB-27017.sock", + "port": null + } + ], + "auth": null, + "options": null + }, { "description": "Unix domain socket (absolute path with spaces in path)", "uri": "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock", diff --git a/source/connection-string/tests/valid-unix_socket-absolute.yml b/source/connection-string/tests/valid-unix_socket-absolute.yml index 651e06fe39..cdfd00d332 100644 --- a/source/connection-string/tests/valid-unix_socket-absolute.yml +++ b/source/connection-string/tests/valid-unix_socket-absolute.yml @@ -23,6 +23,18 @@ tests: port: ~ auth: ~ options: ~ + - + description: "Unix domain socket (mixed case)" + uri: "mongodb://%2Ftmp%2FMongoDB-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/MongoDB-27017.sock" + port: ~ + auth: ~ + options: ~ - description: "Unix domain socket (absolute path with spaces in path)" uri: "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock" diff --git a/source/connection-string/tests/valid-unix_socket-relative.json b/source/connection-string/tests/valid-unix_socket-relative.json index 2ce649ffc2..788720920b 100644 --- a/source/connection-string/tests/valid-unix_socket-relative.json +++ b/source/connection-string/tests/valid-unix_socket-relative.json @@ -30,6 +30,21 @@ "auth": null, "options": null }, + { + "description": "Unix domain socket (mixed case)", + "uri": "mongodb://rel%2FMongoDB-27017.sock", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "unix", + "host": "rel/MongoDB-27017.sock", + "port": null + } + ], + "auth": null, + "options": null + }, { "description": "Unix domain socket (relative path with spaces)", "uri": "mongodb://rel%2F %2Fmongodb-27017.sock", diff --git a/source/connection-string/tests/valid-unix_socket-relative.yml b/source/connection-string/tests/valid-unix_socket-relative.yml index 7164188b3b..6d0d108205 100644 --- a/source/connection-string/tests/valid-unix_socket-relative.yml +++ b/source/connection-string/tests/valid-unix_socket-relative.yml @@ -23,6 +23,18 @@ tests: port: ~ auth: ~ options: ~ + - + description: "Unix domain socket (mixed case)" + uri: "mongodb://rel%2FMongoDB-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/MongoDB-27017.sock" + port: ~ + auth: ~ + options: ~ - description: "Unix domain socket (relative path with spaces)" uri: "mongodb://rel%2F %2Fmongodb-27017.sock" diff --git a/source/connection-string/tests/valid-warnings.json b/source/connection-string/tests/valid-warnings.json index 87f7248f21..e11757eb0e 100644 --- a/source/connection-string/tests/valid-warnings.json +++ b/source/connection-string/tests/valid-warnings.json @@ -63,6 +63,53 @@ "options": { "wtimeoutms": 10 } + }, + { + "description": "Empty integer option values are ignored", + "uri": "mongodb://localhost/?maxIdleTimeMS=", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "localhost", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Empty boolean option value are ignored", + "uri": "mongodb://localhost/?journal=", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "localhost", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Comma in a key value pair causes a warning", + "uri": "mongodb://localhost?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://host1%2Chost2,ENVIRONMENT:azure", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "localhost", + "port": null + } + ], + "auth": null, + "options": { + "authMechanism": "MONGODB-OIDC" + } } ] } diff --git a/source/connection-string/tests/valid-warnings.yml b/source/connection-string/tests/valid-warnings.yml index 4515c9a3bf..3495b50778 100644 --- a/source/connection-string/tests/valid-warnings.yml +++ b/source/connection-string/tests/valid-warnings.yml @@ -49,3 +49,40 @@ tests: auth: ~ options: wtimeoutms: 10 + - + description: "Empty integer option values are ignored" + uri: "mongodb://localhost/?maxIdleTimeMS=" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "localhost" + port: ~ + auth: ~ + options: ~ + - + description: "Empty boolean option value are ignored" + uri: "mongodb://localhost/?journal=" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "localhost" + port: ~ + auth: ~ + options: ~ + - + description: Comma in a key value pair causes a warning + uri: mongodb://localhost?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://host1%2Chost2,ENVIRONMENT:azure + valid: true + warning: true + hosts: + - + type: "hostname" + host: "localhost" + port: ~ + auth: ~ + options: + authMechanism: MONGODB-OIDC diff --git a/source/connections-survive-step-down/tests/README.md b/source/connections-survive-step-down/tests/README.md new file mode 100644 index 0000000000..9f82780a8b --- /dev/null +++ b/source/connections-survive-step-down/tests/README.md @@ -0,0 +1,156 @@ +# Connections Survive Primary Step Down Tests + +These tests can be used to verify a driver's compliance with server discovery and monitoring requirements with respect +to handling "not primary" and "node is shutting down" error responses from the server. + +These tests apply only to replica set topologies. + +## Server Fail Point + +See: [Server Fail Point](../../transactions/tests/legacy-test-format.md#server-fail-point) in the Transactions spec test +suite. + +### Disabling Fail Point after Test Execution + +After each test that configures a fail point, drivers should disable the `failCommand` fail point to avoid spurious +failures in subsequent tests. The fail point may be disabled like so: + +```javascript +db.runCommand({ + configureFailPoint: "failCommand", + mode: "off" +}); +``` + +### Consideration when using serverStatus + +Drivers executing [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) for connection +assertions MUST take its own connection into account when making their calculations. Those drivers SHOULD execute +[serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) using a separate client not under +test. + +## Tests + +### Test setup + +For each test, make sure the following steps have been completed before running the actual test: + +- Create a `MongoClient` with `retryWrites=false` +- Create a collection object from the `MongoClient`, using `step-down` for the database and collection name. +- Drop the test collection, using `writeConcern` "majority". +- Execute the "create" command to recreate the collection, using `writeConcern` "majority". + +The driver should implement the following tests: + +### getMore Iteration + +This test requires a replica set with server version 4.2 or higher. + +Perform the following operations: + +- Insert 5 documents into a collection with a majority write concern. +- Start a find operation on the collection with a batch size of 2, and retrieve the first batch of results. +- Send a `{replSetFreeze: 0}` command to any secondary and verify that the command succeeded. This command will unfreeze + the secondary and ensure that it will be eligible to be elected immediately. +- Send a `{replSetStepDown: 30, force: true}` command to the current primary and verify that the command succeeded. +- Retrieve the next batch of results from the cursor obtained in the find operation, and verify that this operation + succeeded. +- If the driver implements the [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification, verify that no new + [PoolClearedEvent](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) has been + published. Otherwise verify that + [connections.totalCreated](https://www.mongodb.com/docs/manual/reference/command/serverStatus/#serverstatus.connections.totalCreated) + in [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) has not changed. + +### Not Primary - Keep Connection Pool + +This test requires a replica set with server version 4.2 or higher. + +- Set the following fail point: + `{configureFailPoint: "failCommand", mode: {times: 1}, data: {failCommands: ["insert"], errorCode: 10107}}` +- Execute an insert into the test collection of a `{test: 1}` document. +- Verify that the insert failed with an operation failure with 10107 code. +- Execute an insert into the test collection of a `{test: 1}` document and verify that it succeeds. +- If the driver implements the [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification, verify that no new + [PoolClearedEvent](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) has been + published. Otherwise verify that + [connections.totalCreated](https://www.mongodb.com/docs/manual/reference/command/serverStatus/#serverstatus.connections.totalCreated) + in [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) has not changed. + +### Not Primary - Reset Connection Pool + +This test requires a replica set with server version 4.0. + +- Set the following fail point: + `{configureFailPoint: "failCommand", mode: {times: 1}, data: {failCommands: ["insert"], errorCode: 10107}}` +- Execute an insert into the test collection of a `{test: 1}` document. +- Verify that the insert failed with an operation failure with 10107 code. +- If the driver implements the [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification, verify that a + [PoolClearedEvent](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) has been + published +- Execute an insert into the test collection of a `{test: 1}` document and verify that it succeeds. +- If the driver does NOT implement the + [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) specification, use the + [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) command to verify + [connections.totalCreated](https://www.mongodb.com/docs/manual/reference/command/serverStatus/#serverstatus.connections.totalCreated) + has increased by 1. + +### Shutdown in progress - Reset Connection Pool + +This test should be run on all server versions >= 4.0. + +Perform the following operations on a client configured to NOT retry writes: + +- Set the following fail point: + `{configureFailPoint: "failCommand", mode: {times: 1}, data: {failCommands: ["insert"], errorCode: 91}}` +- Execute an insert into the test collection of a `{test: 1}` document. +- Verify that the insert failed with an operation failure with 91 code. +- If the driver implements the [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification, verify that a + [PoolClearedEvent](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) has been + published +- Execute an insert into the test collection of a `{test: 1}` document and verify that it succeeds. +- If the driver does NOT implement the + [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) specification, use the + [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) command to verify + [connections.totalCreated](https://www.mongodb.com/docs/manual/reference/command/serverStatus/#serverstatus.connections.totalCreated) + has increased by 1. + +### Interrupted at shutdown - Reset Connection Pool + +This test should be run on all server versions >= 4.0. + +Perform the following operations on a client configured to NOT retry writes: + +- Set the following fail point: + `{configureFailPoint: "failCommand", mode: {times: 1}, data: {failCommands: ["insert"], errorCode: 11600}}` +- Execute an insert into the test collection of a `{test: 1}` document. +- Verify that the insert failed with an operation failure with 11600 code. +- If the driver implements the [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification, verify that a + [PoolClearedEvent](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) has been + published +- Execute an insert into the test collection of a `{test: 1}` document and verify that it succeeds. +- If the driver does NOT implement the + [CMAP](../../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) specification, use the + [serverStatus](https://www.mongodb.com/docs/manual/reference/command/serverStatus) command to verify + [connections.totalCreated](https://www.mongodb.com/docs/manual/reference/command/serverStatus/#serverstatus.connections.totalCreated) + has increased by 1. + +## Questions and Answers + +### Do we need to wait for re-election after the first test? + +Since test setup requires creation of a collection, a primary must exist, so subsequent tests will block in server +selection until a primary is available again. + +### Why do tests check for a successful insert operation in addition to checking that the pool was updated appropriately? + +Ensuring that we can run a successful insert after the primary steps down and without needing to recreate the +`MongoClient` serves to test the resiliency of drivers in the event of a failover/election. Even though checking for a +successful insert operation does not directly test functionality introduced in this specification, it is a +straightforward way to test driver resiliency against a live replica set undergoing an election. This testing +methodology is in contrast to the one adopted by the SDAM spec tests that rely entirely on mocking with no actual server +communication. diff --git a/source/crud/bulk-write.md b/source/crud/bulk-write.md new file mode 100644 index 0000000000..73deb2459e --- /dev/null +++ b/source/crud/bulk-write.md @@ -0,0 +1,980 @@ +# Bulk Write + +- Status: Accepted +- Minimum Server Version: 8.0 + +## Abstract + +This specification defines the driver API for the `bulkWrite` server command introduced in MongoDB 8.0. The API defined +in this specification allows users to perform insert, update, and delete operations against mixed namespaces in a +minimized number of round trips, and to receive detailed results for each operation performed. This API is distinct from +the [collection-level bulkWrite method](../crud/crud.md#insert-update-replace-delete-and-bulk-writes) defined in the +CRUD specification. + +## Specification + +> [!NOTE] +> The `BulkWriteOptions`, `BulkWriteResult`, and `BulkWriteException` types defined in this specification are similar to +> those used for the `MongoCollection.bulkWrite` method. Statically typed drivers MUST NOT reuse their existing +> definitions for these types for the `MongoClient.bulkWrite` API and MUST introduce new types. If naming conflicts +> arise, drivers SHOULD prepend "Client" to the new type names (e.g. `ClientBulkWriteOptions`). + +### `MongoClient.bulkWrite` Interface + +```typescript +interface MongoClient { + /** + * Executes a list of mixed write operations. + * + * @throws BulkWriteException + */ + bulkWrite(models: NamespaceWriteModelPair[], options: Optional): BulkWriteResult; +} +``` + +### Write Models + +A `WriteModel` defines a single write operation to be performed as part of a bulk write. + +```typescript +/** + * Unifying interface for the various write model types. Drivers may also use an enum with + * variants for each write model for this type. + */ +interface WriteModel {} + +class InsertOneModel implements WriteModel { + /** + * The document to insert. + */ + document: Document; +} + +class UpdateOneModel implements WriteModel { + /** + * The filter to apply. + */ + filter: Document; + + /** + * The update document or pipeline to apply to the selected document. + */ + update: (Document | Document[]); + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. + */ + arrayFilters: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. If + * specified, then the query system will only consider plans using the hinted index. + * + * This option is only sent if the caller explicitly provides a value. + */ + hint: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is only sent if the caller explicitly provides a value. The server's default + * value is false. + */ + upsert: Optional; + + /** + * Specify which document the operation updates if the query matches multiple + * documents. The first document matched by the sort order will be updated. + * + * This option is only sent if the caller explicitly provides a value. + */ + sort: Optional; +} + +class UpdateManyModel implements WriteModel { + /** + * The filter to apply. + */ + filter: Document; + + /** + * The update document or pipeline to apply to the selected documents. + */ + update: (Document | Document[]); + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. + */ + arrayFilters: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. If + * specified, then the query system will only consider plans using the hinted index. + * + * This option is only sent if the caller explicitly provides a value. + */ + hint: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is only sent if the caller explicitly provides a value. The server's default + * value is false. + */ + upsert: Optional; +} + +class ReplaceOneModel implements WriteModel { + /** + * The filter to apply. + */ + filter: Document; + + /** + * The replacement document. + */ + replacement: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. If + * specified, then the query system will only consider plans using the hinted index. + * + * This option is only sent if the caller explicitly provides a value. + */ + hint: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is only sent if the caller explicitly provides a value. The server's default + * value is false. + */ + upsert: Optional; + + /** + * Specify which document the operation replaces if the query matches multiple + * documents. The first document matched by the sort order will be replaced. + * + * This option is only sent if the caller explicitly provides a value. + */ + sort: Optional; +} + +class DeleteOneModel implements WriteModel { + /** + * The filter to apply. + */ + filter: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. If + * specified, then the query system will only consider plans using the hinted index. + * + * This option is only sent if the caller explicitly provides a value. + */ + hint: Optional; +} + +class DeleteManyModel implements WriteModel { + /** + * The filter to apply. + */ + filter: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. If + * specified, then the query system will only consider plans using the hinted index. + * + * This option is only sent if the caller explicitly provides a value. + */ + hint: Optional; +} +``` + +Each write model provided to `MongoClient.bulkWrite` in the `models` parameter MUST have a corresponding namespace that +defines the collection on which the operation should be performed. Drivers SHOULD design this pairing in whichever way +is most idiomatic for its language. For example, drivers may: + +- Include a required `namespace` field on each `WriteModel` variant and accept a list of `WriteModel` objects for the + `models` parameter. +- Accept a list of `(Namespace, WriteModel)` tuples for `models`. +- Define the following pair class: + +```typescript +class NamespaceWriteModelPair { + /** + * The namespace on which to perform the write. + */ + namespace: Namespace; + + /** + * The write to perform. + */ + model: WriteModel; +} +``` + +Drivers MUST throw an exception if the list provided for `models` is empty. + +#### Update vs. replace document validation + +Update documents provided in `UpdateOne` and `UpdateMany` write models are required only to contain atomic modifiers +(i.e. keys that start with `$`). Drivers MUST throw an error if an update document is empty or if the document's first +key does not start with `$`. Drivers MUST rely on the server to return an error if any other entries in the update +document are not atomic modifiers. Drivers are not required to perform validation on update pipelines. + +Replacement documents provided in `ReplaceOne` write models are required not to contain atomic modifiers. Drivers MUST +throw an error if a replacement document is nonempty and its first key starts with `$`. Drivers MUST rely on the server +to return an error if any other entries in the replacement document are atomic modifiers. + +### Options + +```typescript +class BulkWriteOptions { + /** + * Whether the operations in this bulk write should be executed in the order in which they were + * specified. If false, writes will continue to be executed if an individual write fails. If + * true, writes will stop executing if an individual write fails. + * + * Defaults to true. + */ + ordered: Optional; + + /** + * If true, allows the writes to opt out of document-level validation. + * + * This option is only sent if the caller explicitly provides a value. The server's default + * value is false. + */ + bypassDocumentValidation: Optional; + + /** + * A map of parameter names and values to apply to all operations within the bulk write. Value + * 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 option is only sent if the caller explicitly provides a value. + */ + let: Optional; + + /** + * The write concern to use for this bulk write. + */ + writeConcern: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. + * + * This option is only sent if the caller explicitly provides a value. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; + + /** + * Whether detailed results for each successful operation should be included in the returned + * BulkWriteResult. + * + * Defaults to false. This value corresponds inversely to the errorsOnly field in the bulkWrite + * command. + */ + verboseResults: Optional; +} +``` + +### Result + +```typescript +class BulkWriteResult { + /** + * Indicates whether this write result was acknowledged. + * + * NOT REQUIRED TO IMPLEMENT. See below for guidance on modeling unacknowledged results. + */ + acknowledged: Boolean; + + /** + * Indicates whether this result contains verbose results. + * + * NOT REQUIRED TO IMPLEMENT. See below for guidance on modeling verbose results. + */ + hasVerboseResults: Boolean; + + /** + * The total number of documents inserted across all insert operations. + */ + insertedCount: Int64; + + /** + * The total number of documents upserted across all update operations. + */ + upsertedCount: Int64; + + /** + * The total number of documents matched across all update operations. + */ + matchedCount: Int64; + + /** + * The total number of documents modified across all update operations. + */ + modifiedCount: Int64; + + /** + * The total number of documents deleted across all delete operations. + */ + deletedCount: Int64; + + /** + * The results of each individual insert operation that was successfully performed. + * + * NOT REQUIRED TO IMPLEMENT. See below for guidance on modeling verbose results. + */ + insertResults: Map; + + /** + * The results of each individual update operation that was successfully performed. + * + * NOT REQUIRED TO IMPLEMENT. See below for guidance on modeling verbose results. + */ + updateResults: Map; + + /** + * The results of each individual delete operation that was successfully performed. + * + * NOT REQUIRED TO IMPLEMENT. See below for guidance on modeling verbose results. + */ + deleteResults: Map; +} + +class InsertOneResult { + /** + * The _id of the inserted document. + */ + insertedId: Any; +} + +class UpdateResult { + /** + * The number of documents that matched the filter. + */ + matchedCount: Int64; + + /** + * The number of documents that were modified. + */ + modifiedCount: Int64; + + /** + * The _id field of the upserted document if an upsert occurred. + * + * It MUST be possible to discern between a BSON Null upserted ID value and this field being + * unset. If necessary, drivers MAY add a didUpsert boolean field to differentiate between + * these two cases. + */ + upsertedId: Optional; +} + +class DeleteResult { + /** + * The number of documents that were deleted. + */ + deletedCount: Int64; +} +``` + +#### Unacknowledged results + +Users MUST be able to discern whether a `BulkWriteResult` contains acknowledged results without inspecting the +configured write concern. Drivers should follow the guidance in the CRUD specification +[here](../crud/crud.md#write-results) to determine how to model unacknowledged results. + +If drivers expose the `acknowledged` field, they MUST document what will happen if a user attempts to access a result +value when `acknowledged` is `false` (e.g. an undefined value is returned or an error is thrown). + +#### Summary vs. verbose results + +When a user does not set the `verboseResults` option to `true`, drivers MUST NOT populate the `insertResults`, +`updateResults`, and `deleteResults` fields. Users MUST be able to discern whether a `BulkWriteResult` contains these +verbose results without inspecting the value provided for `verboseResults` in `BulkWriteOptions`. Drivers can implement +this in a number of ways, including: + +- Expose the `hasVerboseResults` field in `BulkWriteResult` as defined above. Document what will happen if a user + attempts to access the `insertResults`, `updateResults`, or `deleteResults` values when `hasVerboseResults` is + false. Drivers MAY raise an error if a user attempts to access one of these values when `hasVerboseResults` is + false. +- Embed the verbose results in an optional type: + +```typescript +class BulkWriteResult { + /** + * The results of each individual write operation that was successfully performed. + * + * This value will only be populated if the verboseResults option was set to true. + */ + verboseResults: Optional; + + /* rest of fields */ +} + +class VerboseResults { + /** + * The results of each individual insert operation that was successfully performed. + */ + insertResults: Map; + + /** + * The results of each individual update operation that was successfully performed. + */ + updateResults: Map; + + /** + * The results of each individual delete operation that was successfully performed. + */ + deleteResults: Map; +} +``` + +- Define separate `SummaryBulkWriteResult` and `VerboseBulkWriteResult` types. `SummaryBulkWriteResult` MUST only + contain the summary result fields, and `VerboseBulkWriteResult` MUST contain both the summary and verbose result + fields. Return `VerboseBulkWriteResult` when `verboseResults` was set to true and `SummaryBulkWriteResult` + otherwise. + +#### Individual results + +The `InsertOneResult`, `UpdateResult`, and `DeleteResult` classes are the same as or similar to types of the same name +defined in the [CRUD specification](crud.md). Drivers MUST redefine these classes if their existing result classes +deviate from the definitions in this specification (e.g. if they contain acknowledgement information, which is not +applicable for individual bulk write operations). Drivers MAY reuse their existing types for these classes if they match +the ones defined here exactly. + +### Exception + +```typescript +class BulkWriteException { + /** + * A top-level error that occurred when attempting to communicate with the server or execute + * the bulk write. This value may not be populated if the exception was thrown due to errors + * occurring on individual writes. + */ + error: Optional; + + /** + * Write concern errors that occurred while executing the bulk write. This list may have + * multiple items if more than one server command was required to execute the bulk write. + */ + writeConcernErrors: WriteConcernError[]; + + /** + * Errors that occurred during the execution of individual write operations. This map will + * contain at most one entry if the bulk write was ordered. + */ + writeErrors: Map; + + /** + * The results of any successful operations that were performed before the error was + * encountered. + */ + partialResult: Optional; +} +``` + +### Index Types + +The `insertResults`, `updateResults`, and `deleteResults` maps in `BulkWriteResult` and the `writeErrors` map in +`BulkWriteException` specify `Index` as their key type. This value corresponds to the index of the operation in the +`writeModels` list that was provided to `MongoClient.bulkWrite`. Drivers SHOULD use their language's standard numeric +type for indexes for this type (e.g. `usize` in Rust). If no standard index type exists, drivers MUST use `Int64`. + +## Building a `bulkWrite` Command + +The `bulkWrite` server command has the following format: + +```javascript +{ + "bulkWrite": 1, + "ops": , + "nsInfo": , + "errorsOnly": Optional, + "ordered": Optional, + "bypassDocumentValidation": Optional, + "comment": Optional, + "let": Optional, + "rawData": Optional, + ...additional operation-agnostic fields +} +``` + +If auto-encryption is not enabled, drivers MUST use document sequences ([`OP_MSG`](../message/OP_MSG.md) payload type 1) +for the `ops` and `nsInfo` fields. If auto-encryption is enabled, drivers MUST NOT use document sequences and MUST +append the `ops` and `nsInfo` fields to the `bulkWrite` command document. + +The `bulkWrite` command is executed on the "admin" database. + +### Operations + +The `ops` field is a list of write operation documents. The first entry in each document has the name of the operation +(i.e. "insert", "update", or "delete") as its key and the index in the `nsInfo` array of the namespace on which the +operation should be performed as its value. The documents have the following format: + +#### Insert + +```javascript +{ + "insert": , + "document": +} +``` + +If the document to be inserted does not contain an `_id` field, drivers MUST generate a new +[`ObjectId`](../bson-objectid/objectid.md) and add it as the `_id` field at the beginning of the document. + +#### Update + +```javascript +{ + "update": , + "filter": , + "updateMods": , + "multi": Optional, + "upsert": Optional, + "arrayFilters": Optional, + "hint": Optional, + "collation": Optional +} +``` + +The `update` command document is used for update and replace operations. For update operations, the `updateMods` field +corresponds to the `update` field in `UpdateOneModel` and `UpdateManyModel`. For replace operations, the `updateMods` +field corresponds to the `replacement` field in `ReplaceOneModel`. + +#### Delete + +```javascript +{ + "delete": , + "filter": , + "multi": Optional, + "hint": Optional, + "collation": Optional +} +``` + +### Namespace Information + +The `nsInfo` field is an array containing the namespaces on which the write operations should be performed. Drivers MUST +NOT include duplicate namespaces in this list. The documents in the `nsInfo` array have the following format: + +```javascript +{ + "ns": +} +``` + +### `errorsOnly` and `verboseResults` + +The `errorsOnly` field indicates whether the results cursor returned in the `bulkWrite` response should contain only +errors and omit individual results. If false, both individual results for successful operations and errors will be +returned. This field is optional and defaults to false on the server. + +`errorsOnly` corresponds inversely to the `verboseResults` option defined on `BulkWriteOptions`. If the user specified a +value for `verboseResults`, drivers MUST define `errorsOnly` as the opposite of `verboseResults`. If the user did not +specify a value for `verboseResults`, drivers MUST define `errorsOnly` as `true`. + +Drivers MUST return a client-side error if `verboseResults` is true with an unacknowledged write concern containing the +following message: + +> Cannot request unacknowledged write concern and verbose results + +### `ordered` + +The `ordered` field defines whether writes should be executed in the order in which they were specified, and, if an +error occurs, whether the server should halt execution of further writes. It is optional and defaults to true on the +server. Drivers MUST explicitly define `ordered` as `true` in the `bulkWrite` command if a value is not specified in +`BulkWriteOptions`. This is required to avoid inconsistencies between server and driver behavior if the server default +changes in the future. + +Drivers MUST return a client-side error if `ordered` is true (including when default is applied) with an unacknowledged +write concern containing the following message: + +> Cannot request unacknowledged write concern and ordered writes + +## Command Batching + +Drivers MUST accept an arbitrary number of operations as input to the `MongoClient.bulkWrite` method. Because the server +imposes restrictions on the size of write operations, this means that a single call to `MongoClient.bulkWrite` may +require multiple `bulkWrite` commands to be sent to the server. Drivers MUST split bulk writes into separate commands +when the user's list of operations exceeds one or more of these maximums: `maxWriteBatchSize`, `maxBsonObjectSize` (for +`OP_MSG` payload type 0), and `maxMessageSizeBytes` (for `OP_MSG` payload type 1). Each of these values can be retrieved +from the selected server's `hello` command response. Drivers MUST merge results from multiple batches into a single +`BulkWriteResult` or `BulkWriteException` to return from `MongoClient.bulkWrite`. + +When constructing the `nsInfo` array for a `bulkWrite` batch, drivers MUST only include the namespaces that are +referenced in the `ops` array for that batch. + +### Number of Writes + +`maxWriteBatchSize` defines the total number of writes allowed in one command. Drivers MUST split a bulk write into +multiple commands if the user provides more than `maxWriteBatchSize` operations in the argument for `models`. + +### Total Message Size + +#### Unencrypted bulk writes + +When auto-encryption is not enabled, drivers MUST ensure that the total size of the `OP_MSG` built for each `bulkWrite` +command does not exceed `maxMessageSizeBytes`. + +The upper bound for the size of an `OP_MSG` includes opcode-related bytes (e.g. the `OP_MSG` header) and +operation-agnostic command field bytes (e.g. `txnNumber`, `lsid`). Drivers MUST limit the combined size of the +`bulkWrite` command document (excluding command-agnostic fields), `ops` document sequence, and `nsInfo` document +sequence to `maxMessageSizeBytes - 1000` to account for this overhead. The following pseudocode demonstrates how to +apply this limit in batch-splitting logic: + +```javascript +MESSAGE_OVERHEAD_BYTES = 1000 + +bulkWriteCommand = Document { "bulkWrite": 1 } +bulkWriteCommand.appendOptions(bulkWriteOptions) + +maxOpsNsInfoBytes = maxMessageSizeBytes - (MESSAGE_OVERHEAD_BYTES + bulkWriteCommand.numBytes()) + +while (writeModels.hasNext()) { + ops = DocumentSequence {} + nsInfo = DocumentSequence {} + while (true) { + if (!writeModels.hasNext()) { + break + } + model = writeModels.next() + + modelDoc = writeModel.toOpsDoc() + bytesAdded = modelDoc.numBytes() + + nsInfoDoc = null + if (!nsInfo.contains(model.namespace)) { + nsInfoDoc = model.namespace.toNsInfoDoc() + bytesAdded += nsInfoDoc.numBytes() + } + + newSize = ops.numBytes() + nsInfo.numBytes() + bytesAdded + if (newSize > maxOpsNsInfoBytes) { + break + } else { + ops.push(modelDoc) + if (nsInfoDoc != null) { + nsInfo.push(nsInfoDoc) + } + } + } + + // construct and send OP_MSG +} +``` + +See [this Q&A entry](#how-was-the-op_msg-overhead-allowance-determined) for more details on how the overhead allowance +was determined. + +Drivers MUST return an error if there is not room to add at least one operation to `ops`. + +#### Auto-encrypted bulk writes + +Drivers MUST use the reduced size limit defined in +[Size limits for Write Commands](../client-side-encryption/client-side-encryption.md#size-limits-for-write-commands) for +the size of the `bulkWrite` command document when auto-encryption is enabled. + +## Handling the `bulkWrite` Server Response + +The server's response to `bulkWrite` has the following format: + +```javascript +{ + "ok": <0 | 1>, + "cursor": { + "id": , + "firstBatch": , + "ns": + }, + "nErrors": , + "nInserted": , + "nUpserted": , + "nMatched": , + "nModified": , + "nDeleted": , + ...additional command-agnostic fields +} +``` + +Drivers MUST record the summary count fields in a `BulkWriteResult` to be returned to the user or embedded in a +`BulkWriteException` if the response indicates that at least one write was successful: + +- For ordered bulk writes, at least one write was successful if `nErrors` is 0 or if the `idx` value for the write error + returned in the results cursor is greater than 0. +- For unordered bulk writes, at least one write was successful if `nErrors` is less than the number of operations that + were included in the `bulkWrite` command. + +Drivers MUST NOT populate the `partialResult` field in `BulkWriteException` if it cannot be determined that at least one +write was successfully performed. + +Drivers MUST attempt to consume the contents of the cursor returned in the server's `bulkWrite` response before +returning to the user. This is required regardless of whether the user requested verbose or summary results, as the +results cursor always contains any write errors that occurred. If the cursor contains a nonzero cursor ID, drivers MUST +perform `getMore` until the cursor has been exhausted. Drivers MUST use the same session used for the `bulkWrite` +command for each `getMore` call. When connected to a load balancer, drivers MUST use the connection used for the +`bulkWrite` command to create the cursor to ensure the same server is targeted. + +The documents in the results cursor have the following format: + +```javascript +{ + "ok": <0 | 1>, + "idx": Int32, + "code": Optional, + "errmsg": Optional, + "errInfo": Optional, + "n": , + "nModified": Optional, + "upserted": Optional +} +``` + +If an error occurred (i.e. the value for `ok` is 0), the `code`, `errmsg`, and optionally `errInfo` fields will be +populated with details about the failure. + +If the write succeeded, (i.e. the value for `ok` is 1), `n`, `nModified`, and `upsertedId` will be populated with the +following values based on the type of write: + +| Response Field | Insert | Update | Delete | +| -------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------ | +| `n` | The number of documents that were inserted. | The number of documents that matched the filter. | The number of documents that were deleted. | +| `nModified` | Not present. | The number of documents that were modified. | Not present. | +| `upserted` | Not present. | A document containing the `_id` value for the upserted document. Only present if an upsert took place. | Not present. | + +Note that the responses do not contain information about the type of operation that was performed. Drivers may need to +maintain the user's list of write models to infer which type of result should be recorded based on the value of `idx`. + +### Handling Insert Results + +Unlike the other result types, `InsertOneResult` contains an `insertedId` field that is generated driver-side, either by +recording the `_id` field present in the user's insert document or creating and adding one. Drivers MUST only record +these `insertedId` values in a `BulkWriteResult` when a successful response for the insert operation (i.e. +`{ "ok": 1, "n": 1 }`) is received in the results cursor. This ensures that drivers only report an `insertedId` when it +is confirmed that the insert succeeded. + +## Handling Errors + +### Top-Level Errors + +A top-level error is any error that occurs that is not the result of a single write operation failing or a write concern +error. Examples include network errors that occur when communicating with the server, command errors (`{ "ok": 0 }`) +returned from the server, client-side errors, and errors that occur when attempting to perform a `getMore` to retrieve +results from the server. + +When a top-level error is caused by a command error (i.e. an `{ "ok": 0 }` server response), drivers MUST provide access +to the raw server reply in the error returned to the user. + +When a top-level error is encountered and individual results and/or errors have already been observed, drivers MUST +embed the top-level error within a `BulkWriteException` as the `error` field to retain this information. Otherwise, +drivers MAY throw an exception containing only the top-level error. + +Encountering a top-level error MUST halt execution of a bulk write for both ordered and unordered bulk writes. This +means that drivers MUST NOT attempt to retrieve more responses from the cursor or execute any further `bulkWrite` +batches and MUST immediately throw an exception. If the results cursor has not been exhausted on the server when a +top-level error occurs, drivers MUST send the `killCursors` command to attempt to close it. The result returned from the +`killCursors` command MUST NOT be ignored. + +### Write Concern Errors + +Write concern errors are recorded in the `writeConcernErrors` field on `BulkWriteException`. When a write concern error +is encountered, it should not terminate execution of the bulk write for either ordered or unordered bulk writes. +However, drivers MUST throw an exception at the end of execution if any write concern errors were observed. + +### Individual Write Errors + +Individual write errors retrieved from the cursor are recorded in the `writeErrors` field on `BulkWriteException`. If an +individual write error is encountered during an ordered bulk write, drivers MUST record the error in `writeErrors` and +immediately throw the exception. Otherwise, drivers MUST continue to iterate the results cursor and execute any further +`bulkWrite` batches. + +## Test Plan + +The majority of tests for `MongoClient.bulkWrite` are written in the +[Unified Test Format](../unified-test-format/unified-test-format.md) and reside in the CRUD unified tests directory. + +Additional prose tests are specified [here](../crud/tests/README.md). These tests require constructing very large +documents to test batch splitting, which is not feasible in the unified test format at the time of writing this +specification. + +## Future Work + +### Retrying `getMore`s + +When a `getMore` fails with a retryable error when attempting to iterate the results cursor, drivers could retry the +entire `bulkWrite` command to receive a fresh cursor and retry iteration. This work was omitted to minimize the scope of +the initial implementation and testing of the new bulk write API, but may be revisited in the future. + +Note that there is one exception to this behavior: when a command fails with an error that is eligible for retry under +the conditions defined in the [Client Backpressure](../client-backpressure/client-backpressure.md) specification, +drivers SHOULD retry the `getMore` following the rules outlined in the client backpressure specification. + +### Use document sequences for auto-encrypted bulk writes + +Auto-encryption does not currently support document sequences. This specification should be updated when +[DRIVERS-2859](https://jira.mongodb.org/browse/DRIVERS-2859) is completed to require use of document sequences for `ops` +and `nsInfo` when auto-encryption is enabled. + +Drivers requiring significant changes to pass a bulkWrite command to libmongocrypt are recommended to wait until +[DRIVERS-2859](https://jira.mongodb.org/browse/DRIVERS-2859) is implemented before supporting automatic encryption. + +## Q&A + +### Is `bulkWrite` supported on Atlas Serverless? + +No. See [CLOUDP-256344](https://jira.mongodb.org/browse/CLOUDP-256344) + +### Why are we adding a new bulk write API rather than updating the `MongoCollection.bulkWrite` implementation? + +The new `bulkWrite` command is only available in MongoDB 8.0+, so it cannot function as a drop-in replacement for the +existing bulk write implementation that uses the `insert`, `update`, and `delete` commands. Additionally, because the +new `bulkWrite` command allows operations against multiple collections and databases, `MongoClient` is a more +appropriate place to expose its functionality. + +### Why can't drivers reuse existing bulk write types? + +This specification introduces several types that are similar to existing types used in the `MongoCollection.bulkWrite` +API. Although these types are similar now, they may diverge in the future with the introduction of new options and +features to the `bulkWrite` command. Introducing new types also provides more clarity to users on the existing +differences between the collection-level and client-level bulk write APIs. For example, the `verboseResults` option is +only available for `MongoClient.bulkWrite`. + +### Why are bulk write operation results returned in a cursor? + +Returning results via a cursor rather than an array in the `bulkWrite` response allows full individual results and +errors to be returned without the risk of the response exceeding the maximum BSON object size. Using a cursor also +leaves open the opportunity to add `findAndModify` to the list of supported write operations in the future. + +### Why was the `verboseResults` option introduced, and why is its default `false`? + +The `bulkWrite` command returns top-level summary result counts and, optionally, individual results for each operation. +Compiling the individual results server-side and consuming these results driver-side is less performant than only +recording the summary counts. We expect that most users are not interested in the individual results of their operations +and that most users will rely on defaults, so `verboseResults` defaults to `false` to improve performance in the common +case. + +### Why is providing access to the raw server response when a command error occurs required? + +This allows users to access new error fields that the server may add in the future without needing to upgrade their +driver version. See [DRIVERS-2385](https://jira.mongodb.org/browse/DRIVERS-2385) for more details. + +### Why are drivers required to send `nsInfo` as a document sequence? + +`nsInfo` could exceed `maxBsonObjectSize` if a user is doing `maxWriteBatchSize` operations, each operation is on a +unique namespace, and each namespace is near the +[maximum length](https://www.mongodb.com/docs/manual/reference/limits/#mongodb-limit-Restriction-on-Collection-Names) +allowed for namespaces given the values for these limits at the time of writing this specification. Providing `nsInfo` +as a document sequence reduces the likelihood that a driver would need to batch split a user's bulk write in this +scenario. + +### How was the `OP_MSG` overhead allowance determined? + +The Command Batching [Total Message Size](#total-message-size) section uses a 1000 byte overhead allowance to +approximate the number of non-`bulkWrite`-specific bytes contained in an `OP_MSG` sent for a `bulkWrite` batch. This +number was determined by constructing `OP_MSG` messages with various fields attached to the command, including +`startTransaction`, `autocommit`, and `apiVersion`. Additional room was allocated to allow for future additions to the +`OP_MSG` structure or the introduction of new command-agnostic fields. + +Drivers are required to use this value even if they are capable of determining the exact size of the message prior to +batch-splitting to standardize implementations across drivers and simplify batch-splitting testing. + +### Why is there no requirement to validate the size of a BSON document? + +Following +["_Where possible, depend on server to return errors_"](https://github.com/mongodb/specifications/blob/f8dbd2469f18d093f917efa1f758024bca5d3aaa/source/driver-mantras.md#where-possible-depend-on-server-to-return-errors), +drivers should rely on the server to return errors about exceeded size limits. Such reliance is not possible for +unacknowledged writes. This specification previously required drivers to check size limits for unacknowledged writes. +The requirement has since been removed. Checking size limits complicates some driver implementations. Returning a driver +error in this specific situation does not seem helpful enough to require size checks. + +## **Changelog** + +- 2026-01-05: Specify that `killCursors`'s response cannot be ignored. + +- 2025-09-09: Clarify that `rawData` is for internal use only. + +- 2025-08-13: Removed the requirement to error when QE is enabled. + +- 2025-06-27: Added `rawData` option. + +- 2024-11-05: Updated the requirements regarding the size validation. + +- 2024-10-07: Error if `w:0` is used with `ordered=true` or `verboseResults=true`. + +- 2024-10-01: Add sort option to `replaceOne` and `updateOne`. + +- 2024-09-30: Define more options for modeling summary vs. verbose results. + +- 2024-09-25: Add `collation` field to `update` document and clarify usage of `updateMods`. + +- 2024-09-25: Update the `partialResult` population logic to account for ordered bulk writes. + +- 2024-09-18: Relax numeric type requirements for indexes. + +- 2024-05-17: Update specification status to "Accepted". + +- 2024-05-10: Improve rendered format for JSON-like code blocks. + +- 2024-05-08: Bulk write specification created. diff --git a/source/crud/crud.md b/source/crud/crud.md new file mode 100644 index 0000000000..3f2b2ca8fa --- /dev/null +++ b/source/crud/crud.md @@ -0,0 +1,2896 @@ +# CRUD API + +- Status: Accepted +- Minimum Server Version: 2.6 + +______________________________________________________________________ + +## Abstract + +The CRUD API defines a set of related methods and structures defining a driver's API. As not all languages/frameworks +have the same abilities, parts of the spec may or may not apply. These sections have been called out. + +## Specification + +This specification is about [Guidance](#guidance) in 3 areas related to the CRUD API as well as a specification for the +API itself. It does not define implementation details and provides room and flexibility for the idioms and differences +in languages and frameworks. + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +#### Terms + +**Collection:** + +The term `interface Collection` will be seen in most of the sections. Each driver will likely have a class or interface +defined for the concept of a collection. Operations appearing inside the `interface Collection` are required operations +to be present on a driver's concept of a collection. + +**Iterable:** + +The term `Iterable` will be seen as a return type from some of the [Read](#read) methods. Its use is as that of a +sequence of items. For instance, `collection.find({})` returns a sequence of documents that can be iterated over. + +### Guidance + +#### Documentation + +The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation +for the driver. + +#### Operations + +All drivers MUST offer the operations defined in the following sections. This does not preclude a driver from offering +more. + +#### Operation Parameters + +All drivers MUST offer the same options for each operation as defined in the following sections. This does not preclude +a driver from offering more. A driver SHOULD NOT require a user to specify optional parameters, denoted by the +`Optional<>` signature. Unless otherwise specified, optional values should not be sent to the server. + +##### Deviations + +A non-exhaustive list of acceptable deviations are as follows: + +- Using named parameters instead of an options hash. For instance, `collection.find({x:1}, sort: {a: -1})`. + +- When using an `Options` class, if multiple `Options` classes are structurally equatable, it is permissible to + consolidate them into one with a clear name. For instance, it would be permissible to use the name `UpdateOptions` + as the options for `UpdateOne` and `UpdateMany`. + +- Using a fluent style builder for find or aggregate: + + ```typescript + collection.find({x: 1}).sort({a: -1}).skip(10); + ``` + + When using a fluent-style builder, all options should be named rather than inventing a new word to include in the + pipeline (like options). Required parameters are still required to be on the initiating method. + + In addition, it is imperative that documentation indicate when the order of operations is important. For instance, + skip and limit in find is order irrelevant where skip and limit in aggregate is not. + +#### Naming + +All drivers MUST name operations, objects, and parameters as defined in the following sections. + +Deviations are permitted as outlined below. + +##### Deviations + +When deviating from a defined name, an author should consider if the altered name is recognizable and discoverable to +the user of another driver. + +A non-exhaustive list of acceptable naming deviations are as follows: + +- Using "batchSize" as an example, Java would use "batchSize" while Python would use "batch_size". However, calling it + "batchCount" would not be acceptable. +- Using "maxTimeMS" as an example, .NET would use "MaxTime" where it's type is a TimeSpan structure that includes units. + However, calling it "MaximumTime" would not be acceptable. +- Using "FindOptions" as an example, Javascript wouldn't need to name it while other drivers might prefer to call it + "FindArgs" or "FindParams". However, calling it "QueryOptions" would not be acceptable. +- Using "isOrdered" rather than "ordered". Some languages idioms prefer the use of "is", "has", or "was" and this is + acceptable. + +#### Timeouts + +Drivers MUST enforce timeouts for all operations per the +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. All +operations that return cursors MUST support the timeout options documented in the +[Cursors](../client-side-operations-timeout/client-side-operations-timeout.md#cursors) section of that specification. +All explain helpers MUST support the timeout options documented in the +[Explain Helpers](../client-side-operations-timeout/client-side-operations-timeout.md#explain) section of that +specification. + +### API + +#### Read + +> [!NOTE] +> The term `Iterable` is used below to indicate many of T. This spec is flexible on what that means as different +> drivers will have different requirements, types, and idioms. + +```typescript +interface Collection { + + /** + * Runs an aggregation framework pipeline. + * + * Note: $out and $merge are special pipeline stages that cause no results + * to be returned from the server. As such, the iterable here would never + * contain documents. Drivers MAY setup a cursor to be executed upon + * iteration against the output collection such that if a user were to + * iterate the return value, results would be returned. + * + * Note: result iteration should be backed by a cursor. Depending on the implementation, + * the cursor may back the returned Iterable instance or an iterator that it produces. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + aggregate(pipeline: Document[], options: Optional): Iterable; + + /** + * Gets the number of documents matching the filter. + * + * **This method is DEPRECATED and should not be implemented in new drivers.** + * + * @see https://www.mongodb.com/docs/manual/reference/command/count/ + @deprecated 4.0 + */ + count(filter: Document, options: Optional): Int64; + + /** + * Count the number of documents in a collection that match the given + * filter. Note that an empty filter will force a scan of the entire + * collection. For a fast count of the total documents in a collection + * see estimatedDocumentCount. + * + * See "Count API Details" section below. + */ + countDocuments(filter: Document, options: Optional): Int64; + + /** + * Gets an estimate of the count of documents in a collection using collection metadata. + * + * See "Count API Details" section below for implementation and documentation + * requirements. + */ + estimatedDocumentCount(options: Optional): Int64; + + /** + * Finds the distinct values for a specified field across a single collection. + * + * Note: the results are backed by the "values" array in the distinct command's result + * document. This differs from aggregate and find, where results are backed by a cursor. + * + * @see https://www.mongodb.com/docs/manual/reference/command/distinct/ + */ + distinct(fieldName: string, filter: Document, options: Optional): Iterable; + + /** + * Finds the documents matching the model. + * + * Note: result iteration should be backed by a cursor. Depending on the implementation, + * the cursor may back the returned Iterable instance or an iterator that it produces. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + find(filter: Document, options: Optional): Iterable; + + /** + * Find a document matching the model. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + findOne(filter: Document, options: Optional): Optional; + +} + +interface Database { + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages + * that do not require an underlying collection, such as $currentOp and $listLocalSessions. + * + * Note: result iteration should be backed by a cursor. Depending on the implementation, + * the cursor may back the returned Iterable instance or an iterator that it produces. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate + */ + aggregate(pipeline: Document[], options: Optional): Iterable; + +} + +class AggregateOptions { + + /** + * Enables writing to temporary files. When set to true, aggregation stages + * can write data to the _tmp subdirectory in the dbPath directory. + * + * This option is sent only if the caller explicitly provides a value. The default + * is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + allowDiskUse: Optional; + + /** + * The number of documents to return per batch. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * If specified, drivers SHOULD apply this option to both the original aggregate command and subsequent + * getMore operations on the cursor. + * + * Drivers MUST NOT specify a batchSize of zero in an aggregate command that includes an $out or $merge stage, + * as that will prevent the pipeline from executing. Drivers SHOULD leave the cursor.batchSize command option + * unset in an aggregate command that includes an $out or $merge stage. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + batchSize: Optional; + + /** + * If true, allows the write to opt-out of document level validation. This only applies + * when the $out or $merge stage is specified. + * + * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + bypassDocumentValidation: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + collation: Optional; + + /** + * The maximum amount of time to allow the query to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + maxTimeMS: Optional; + + /** + * The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor + * query. + * + * This options only applies to aggregations which return a TAILABLE_AWAIT cursor. Drivers + * SHOULD always send this value, if the cursor is not a TAILABLE_AWAIT cursor the server will + * ignore it. + * + * @note this option is an alias for maxTimeMS, used on getMore commands + * @note this option is not set on the aggregate command + */ + maxAwaitTimeMS: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions between 3.6 and 4.2 only support string as comment, + * and providing a non-string type will result in a server-side error. + * Older server versions do not support comment for aggregate command at all, + * and providing one will result in a server-side error. + * + * If a comment is provided, drivers MUST attach this comment to all + * subsequent getMore commands run on the same cursor for server + * versions 4.4 and above. For server versions below 4.4 drivers MUST NOT + * attach a comment to getMore commands. + */ + comment: Optional; + + /** + * The index to use for the aggregation. The hint does not apply to $lookup and $graphLookup stages. + * Specify either the index name as a string or the index key pattern. If specified, + * then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + hint: Optional<(String | Document)>; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + let: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class CountOptions { + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + */ + hint: Optional<(String | Document)>; + + /** + * The maximum number of documents to count. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + */ + limit: Optional; + + /** + * The maximum amount of time to allow the operation to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + */ + maxTimeMS: Optional; + + /** + * The number of documents to skip before counting. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + */ + skip: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for count command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class EstimatedDocumentCountOptions { + + /** + * The maximum amount of time to allow the operation to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + */ + maxTimeMS: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4.14 and above. + * For server versions between 4.4.0 and 4.4.14 string comment is supported. + * Servers versions below 4.4.0 do not support comment for count command, + * which is used to implement estimatedDocumentCount. Therefore, providing a + * comment may result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class DistinctOptions { + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/distinct/ + */ + collation: Optional; + + /** + * The maximum amount of time to allow the query to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/distinct/ + */ + maxTimeMS: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for distinct command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + hint: Optional<(String | Document)>; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +enum CursorType { + /** + * The default value. A vast majority of cursors will be of this type. + */ + NON_TAILABLE, + /** + * Tailable means the cursor is not closed when the last data is retrieved. + * Rather, the cursor marks the final object’s position. You can resume + * using the cursor later, from where it was located, if more data were + * received. Like any "latent cursor", the cursor may become invalid at + * some point (CursorNotFound) – for example if the final object it + * references were deleted. + * + * @see https://www.mongodb.com/docs/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query + */ + TAILABLE, + /** + * Combines the tailable option with awaitData, as defined below. + * + * Use with TailableCursor. If we are at the end of the data, block for a + * while rather than returning no data. After a timeout period, we do return + * as normal. The default is true. + * + * @see https://www.mongodb.com/docs/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query + */ + TAILABLE_AWAIT +} + +class FindOptions { + + /** + * Enables writing to temporary files on the server. When set to true, the server + * can write temporary data to disk while executing the find operation. + * + * This option is sent only if the caller explicitly provides a value. The default + * is to not send a value. + * + * This option is only supported by servers >= 4.4. Older servers >= 3.2 will report an error for using this option. + * For servers < 3.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + allowDiskUse: Optional; + + /** + * Get partial results from a mongos if some shards are down (instead of throwing an error). + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, the Partial wire protocol flag is used and defaults to false. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + allowPartialResults: Optional; + + /** + * The number of documents to return per batch. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this is combined with limit to create the wire protocol numberToReturn value. + * If specified, drivers SHOULD apply this option to both the original query operation and subsequent + * getMore operations on the cursor. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + batchSize: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + collation: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 only support string as comment, + * and providing a non-string type will result in a server-side error. + * + * If a comment is provided, drivers MUST attach this comment to all + * subsequent getMore commands run on the same cursor for server + * versions 4.4 and above. For server versions below 4.4 drivers MUST NOT + * attach a comment to getMore commands. + */ + comment: Optional; + + /** + * Indicates the type of cursor to use. This value includes both + * the tailable and awaitData options. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, the AwaitData and Tailable wire protocol flags are used and default to false. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + cursorType: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + hint: Optional<(String | Document)>; + + /** + * The maximum number of documents to return. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this is combined with batchSize to create the wire protocol numberToReturn value. + * + * A negative limit implies that the caller has requested a single batch of results. For servers >= 3.2, singleBatch + * should be set to true and limit should be converted to a positive value. For servers < 3.2, the wire protocol + * numberToReturn value may be negative. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + limit: Optional; + + /** + * The exclusive upper bound for a specific index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + max: Optional; + + /** + * The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor + * query. This only applies to a TAILABLE_AWAIT cursor. When the cursor is not a TAILABLE_AWAIT cursor, + * this option is ignored. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as maxTimeMS does not exist in the OP_GET_MORE wire protocol. + * + * Note: This option is specified as "maxTimeMS" in the getMore command and not provided as part of the + * initial find command. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + maxAwaitTimeMS: Optional; + + /** + * Maximum number of documents or index keys to scan when executing the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + * @deprecated 4.0 + */ + maxScan: Optional; + + /** + * The maximum amount of time to allow the query to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + maxTimeMS: Optional; + + /** + * The inclusive lower bound for a specific index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + min: Optional; + + /** + * The server normally times out idle cursors after an inactivity period (10 minutes) + * to prevent excess memory use. Set this option to prevent that. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, the NoCursorTimeout wire protocol flag is used and defaults to false. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + noCursorTimeout: Optional; + + /** + * Enables optimization when querying the oplog for a range of ts values + * + * Note: this option is intended for internal replication use only. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, the OplogReplay wire protocol flag is used and defaults to false. + * For servers >= 4.4, the server will ignore this option if set (see: SERVER-36186). + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + * @deprecated 4.4 + */ + oplogReplay: Optional; + + /** + * Limits the fields to return for all matching documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + projection: Optional; + + /** + * If true, returns only the index keys in the resulting documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + returnKey: Optional; + + /** + * Determines whether to return the record identifier for each document. If true, adds a field $recordId to the returned documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + showRecordId: Optional; + + /** + * The number of documents to skip before returning. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this is a wire protocol parameter that defaults to 0. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + skip: Optional; + + /** + * Prevents the cursor from returning a document more than once because of an intervening write operation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + * @deprecated 4.0 + */ + snapshot: Optional; + + /** + * The order in which to return matching documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + sort: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + let: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +type FindOneOptions = Omit; +``` + +##### Count API Details + +MongoDB drivers provide two helpers for counting the number of documents in a collection, estimatedDocumentCount and +countDocuments. The names were chosen to make it clear how they behave and exactly what they do. The +estimatedDocumentCount helper returns an estimate of the count of documents in the collection using collection metadata, +rather than counting the documents or consulting an index. The countDocuments helper counts the documents that match the +provided query filter using an aggregation pipeline. + +The count() helper is deprecated. It has always been implemented using the `count` command. The behavior of the count +command differs depending on the options passed to it and may or may not provide an accurate count. When no query filter +is provided the count command provides an estimate using collection 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](https://www.mongodb.com/docs/manual/reference/command/count/#behavior). +The countDocuments helper avoids these sharded cluster problems entirely when used with MongoDB 3.6+, and when using +`Primary` read preference with older sharded clusters. + +##### estimatedDocumentCount + +The estimatedDocumentCount function is implemented using the `count` command with no query filter, skip, limit, or other +options that would alter the results. The only supported options are listed in the `EstimatedDocumentCountOptions` type +defined above. + +Drivers MUST document that, due to an oversight in versions 5.0.0-5.0.8 of MongoDB, the `count` command, which +estimatedDocumentCount uses in its implementation, was not included in v1 of the Stable API, and so users of the Stable +API with estimatedDocumentCount are recommended to upgrade their server version to 5.0.9+ or set `apiStrict: false` to +avoid encountering errors. + +Drivers MUST document that the `count` server command is used to implement estimatedDocumentCount and that users can +find more information via [Count: Behavior](https://www.mongodb.com/docs/manual/reference/command/count/#behavior). + +The 5.0-compat versions of many drivers updated their estimatedDocumentCount implementations to use the `$collStats` +aggregation stage instead of the `count` command. This had the unintended consequence of breaking estimatedDocumentCount +on views, and so the change was seen as a backwards-incompatible regression and reverted. The release notes for the +driver versions that include the reversion from `$collStats` back to `count` MUST document the following: + +- The 5.0-compat release accidentally broke estimatedDocumentCount on views by changing its implementation to use + `aggregate` and a `$collStats` stage instead of the `count` command. +- The new release is fixing estimatedDocumentCount on views by reverting back to using `count` in its implementation. +- Due to an oversight, the `count` command was omitted from the Stable API in server versions 5.0.0 - 5.0.8 and 5.1.0 - + 5.3.1, so users of the Stable API with estimatedDocumentCount are recommended to upgrade their MongoDB clusters to + 5.0.9 or 5.3.2 (if on Atlas) or set `apiStrict: false` when constructing their MongoClients. + +##### countDocuments + +The countDocuments function is implemented using the `$group` aggregate pipeline stage with `$sum`. Applications must be +required to pass a value for filter, but an empty document is supported: + +```typescript +pipeline = [{'$match': filter}] +if (skip) { + pipeline.push({'$skip': skip}) +} +if (limit) { + pipeline.push({'$limit': limit}) +} +pipeline.push({'$group': {'_id': 1, 'n': {'$sum': 1}}}) +``` + +Due to countDocuments using the `$match` aggregation pipeline stage, certain query operators cannot be used in +countDocuments. This includes the `$where` and `$near` query operators, among others. Drivers MUST document these +[restrictions](https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/#restrictions) in their +documentation. + +The count of documents is returned in the `n` field, similar to the `count` command. countDocuments options other than +filter, skip, and limit are added as options to the `aggregate` command. + +In the event this aggregation is run against an empty collection, an empty array will be returned with no `n` field. +Drivers MUST interpret this result as a `0` count. + +##### findOne API details + +The `findOne` operation is implemented using a `find` operation, but only returns the first document returned in the +cursor. Due to the special case, `findOne` does not support the following options supported in `find`: + +- `batchSize`: drivers MUST NOT set a `batchSize` in the `find` command created for a `findOne` operation +- `cursorType`: `findOne` only supports non-tailable cursors +- `limit`: drivers MUST set `limit` to 1 in the `find` command created for a `findOne` operation +- `noCursorTimeout`: with a `limit` of 1 and no `batchSize`, there will not be an open cursor on the server + +To ensure that the cursor is closed regardless of the default server `batchSize`, drivers MUST also set +`singleBatch: true` in the `find` command. + +##### Setting limit and batchSize options for find commands + +When users specify both `limit` and `batchSize` options with the same value, the server returns all results in the first +batch, but still leaves an open cursor that needs to be closed using the `killCursors` command. To avoid this, drivers +MUST send a value of `limit + 1` for `batchSize` in the resulting `find` command. This eliminates the open cursor issue. + +##### Combining Limit and Batch Size for OP_QUERY + +The OP_QUERY wire protocol only contains a numberToReturn value which drivers must calculate to get expected limit and +batch size behavior. Subsequent calls to OP_GET_MORE should use the user-specified batchSize or default to 0. If the +result is larger than the max Int32 value, an error MUST be raised as the computed value is impossible to send to the +server. Below is pseudo-code for calculating numberToReturn for OP_QUERY. + +```typescript +function calculateFirstNumberToReturn(options: FindOptions): Int32 { + Int32 numberToReturn; + Int32 limit = options.limit || 0; + Int32 batchSize = options.batchSize || 0; + + if (limit < 0) { + numberToReturn = limit; + } + else if (limit == 0) { + numberToReturn = batchSize; + } + else if (batchSize == 0) { + numberToReturn = limit; + } + else if (limit < batchSize) { + numberToReturn = limit; + } + else { + numberToReturn = batchSize; + } + + return numberToReturn; +} +``` + +Because of this anomaly in the wire protocol, it is up to the driver to enforce the user-specified limit. Each driver +MUST keep track of how many documents have been iterated and stop iterating once the limit has been reached. When the +limit has been reached, if the cursor is still open, a driver MUST kill the cursor. + +##### Database-level aggregation + +The server supports several collection-less aggregation source stages like `$currentOp` and `$listLocalSessions`. The +database aggregate command requires a collection name of 1 for collection-less source stages. Drivers support for +database-level aggregation will allow users to receive a cursor from these collection-less aggregation source stages. + +#### Write + +##### Insert, Update, Replace, Delete, and Bulk Writes + +###### Generated identifiers + +The insert and bulk insert operations described below MUST generate identifiers for all documents that do not already +have them. These identifiers SHOULD be prepended to the document so they are the first field, in order to prevent the +server from spending time re-ordering the document. If a document already has a user-provided identifier, the driver MAY +re-order the document so the identifier is the first field. + +```typescript +interface Collection { + + /** + * Executes multiple write operations. + * + * An error MUST be raised if the requests parameter is empty. + * + * For servers < 3.4, if a collation was explicitly set for any request, an error MUST be raised + * and no documents sent. + * + * NOTE: see the FAQ about the previous bulk API and how it relates to this. + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + * @see https://www.mongodb.com/docs/manual/reference/command/insert/ + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + * @throws InvalidArgumentException if requests is empty + * @throws BulkWriteException + */ + bulkWrite(requests: WriteModel[], options: Optional): Optional; + + /** + * Inserts the provided document. If the document is missing an identifier, + * the driver should generate one. + * + * @see https://www.mongodb.com/docs/manual/reference/command/insert/ + * @throws WriteException + */ + insertOne(document: Document, options: Optional): Optional; + + /** + * Inserts the provided documents. If any documents are missing an identifier, + * the driver should generate them. + * + * An error MUST be raised if the documents parameter is empty. + * + * Note that this uses the bulk insert command underneath and should not + * use OP_INSERT. + * + * @see https://www.mongodb.com/docs/manual/reference/command/insert/ + * @throws InvalidArgumentException if documents is empty + * @throws BulkWriteException + */ + insertMany(documents: Iterable, options: Optional): Optional; + + /** + * Deletes one document. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + * @throws WriteException + */ + deleteOne(filter: Document, options: Optional): Optional; + + /** + * Deletes multiple documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + * @throws WriteException + */ + deleteMany(filter: Document, options: Optional): Optional; + + /** + * Replaces a single document. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + * @throws WriteException + */ + replaceOne(filter: Document, replacement: Document, options: Optional): Optional; + + /** + * Updates one document. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + * @throws WriteException + */ + updateOne(filter: Document, update: (Document | Document[]), options: Optional): Optional; + + /** + * Updates multiple documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + * @throws WriteException + */ + updateMany(filter: Document, update: (Document | Document[]), options: Optional): Optional; +} + +class BulkWriteOptions { + + /** + * If true, when a write fails, return without performing the remaining + * writes. If false, when a write fails, continue with the remaining writes, if any. + * Defaults to true. + */ + ordered: Boolean; + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * For unacknowledged writes using OP_INSERT, OP_UPDATE, or OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + */ + bypassDocumentValidation: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for write operations, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * 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"). + * + * The let parameter must be a valid Document type for server versions 5.0 and above. + * Server versions prior to 5.0 do not support the let parameter for bulk write operations, + * and providing it will result in a server-side error. + * + * The value of let will be passed to all update and delete, but not insert, commands. + */ + let: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class InsertOneOptions { + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * For unacknowledged writes using OP_INSERT, the driver MUST raise an error if the caller explicitly provides a value. + */ + bypassDocumentValidation: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for insert command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class InsertManyOptions { + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * For unacknowledged writes using OP_INSERT, the driver MUST raise an error if the caller explicitly provides a value. + */ + bypassDocumentValidation: Optional; + + /** + * If true, when an insert fails, return without performing the remaining + * writes. If false, when a write fails, continue with the remaining writes, if any. + * Defaults to true. + */ + ordered: Boolean; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for insert command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class UpdateOptions { + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + arrayFilters: Optional>; + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + */ + bypassDocumentValidation: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.2. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + hint: Optional<(String | Document)>; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + upsert: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for update command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * Specify which document the operation updates if the query matches multiple + * documents. The first document matched by the sort order will be updated. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * The server will report an error if the caller explicitly provides a value with updateMany(). + * This option is only supported by servers >= 8.0. Older servers will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + sort: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class ReplaceOptions { + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + */ + bypassDocumentValidation: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.2. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + hint: Optional<(String | Document)>; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + upsert: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for update command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * Specify which document the operation replaces if the query matches multiple + * documents. The first document matched by the sort order will be replaced. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 8.0. Older servers will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + sort: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class DeleteOptions { + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + hint: Optional<(String | Document)>; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for delete command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} +``` + +###### Bulk Write Models + +```typescript +interface WriteModel { + // marker interface for writes that can be batched together. +} + +class InsertOneModel implements WriteModel { + + /** + * The document to insert. + * + * @see https://www.mongodb.com/docs/manual/reference/command/insert/ + */ + document: Document; +} + +class DeleteOneModel implements WriteModel { + + /** + * The filter to limit the deleted documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + filter: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + hint: Optional<(String | Document)>; +} + +class DeleteManyModel implements WriteModel { + + /** + * The filter to limit the deleted documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + filter: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_DELETE or OP_MSG, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/delete/ + */ + hint: Optional<(String | Document)>; +} + +class ReplaceOneModel implements WriteModel { + + /** + * The filter to limit the replaced document. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + filter: Document; + + /** + * The document with which to replace the matched document. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + replacement: Document; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.2. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + hint: Optional<(String | Document)>; + + /** + * Specify which document the operation replaces if the query matches multiple + * documents. The first document matched by the sort order will be replaced. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 8.0. Older servers will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + sort: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + upsert: Optional; +} + +class UpdateOneModel implements WriteModel { + + /** + * The filter to limit the updated documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + filter: Document; + + /** + * A document or pipeline containing update operators. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + update: (Document | Document[]); + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + arrayFilters: Optional>; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.2. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + hint: Optional<(String | Document)>; + + /** + * Specify which document the operation updates if the query matches multiple + * documents. The first document matched by the sort order will be updated. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 8.0. Older servers will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + sort: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + upsert: Optional; +} + +class UpdateManyModel implements WriteModel { + + /** + * The filter to limit the updated documents. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + filter: Document; + + /** + * A document or pipeline containing update operators. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + update: (Document | Document[]); + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + arrayFilters: Optional>; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.2. Older servers >= 3.4 will report an error for using this option. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_UPDATE, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes using OP_MSG and servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + hint: Optional<(String | Document)>; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + upsert: Optional; +} +``` + +###### Write Results + +The acknowledged property is defined for languages/frameworks without a sufficient optional type. Hence, a driver may +choose to return an optional result (e.g. `Optional`) such that unacknowledged writes don't have a +value and acknowledged writes do have a value. + +> [!NOTE] +> If you have a choice, consider providing the acknowledged member and raising an error if the other fields are accessed +> in an unacknowledged write. Instead of users receiving a null reference exception, you have the opportunity to provide +> an informative error message indicating the correct way to handle the situation. For instance, "The insertedCount +> member is not available when the write was unacknowledged. Check the acknowledged member to avoid this error." + +Any result class with all parameters marked NOT REQUIRED is ultimately NOT REQUIRED as well. For instance, the +`InsertOneResult` has all NOT REQUIRED parameters and is therefore also NOT REQUIRED allowing a driver to use "void" as +the return value for the `insertOne` method. + +```typescript +class BulkWriteResult { + + /** + * Indicates whether this write result was acknowledged. If not, then all + * other members of this result will be undefined. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + acknowledged: Boolean; + + /** + * Number of documents inserted. + */ + insertedCount: Int64; + + /** + * Map of the index of the operation to the id of the inserted document. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + insertedIds: Map; + + /** + * Number of documents matched for update. + */ + matchedCount: Int64; + + /** + * Number of documents modified. + */ + modifiedCount: Int64; + + /** + * Number of documents deleted. + */ + deletedCount: Int64; + + /** + * Number of documents upserted. + */ + upsertedCount: Int64; + + /** + * Map of the index of the operation to the id of the upserted document. + */ + upsertedIds: Map; + +} + +/** + * Note: this class is "NOT REQUIRED" since all of its fields are marked + * "NOT REQUIRED". Drivers that do not implement this class SHOULD use void + * as the return type for insertOne. + */ +class InsertOneResult { + + /** + * Indicates whether this write result was acknowledged. If not, then all + * other members of this result will be undefined. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + acknowledged: Boolean; + + /** + * The identifier that was inserted. If the server generated the identifier, this value + * will be null as the driver does not have access to that data. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + insertedId: any; + +} + +/** + * Note: this class is "NOT REQUIRED" since all of its fields are marked + * "NOT REQUIRED". Drivers that do not implement this class SHOULD use void + * as the return type for insertMany. + */ +class InsertManyResult { + + /** + * Indicates whether this write result was acknowledged. If not, then all + * other members of this result will be undefined. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + acknowledged: Boolean; + + /** + * Map of the index of the inserted document to the id of the inserted document. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + insertedIds: Map; + +} + +class DeleteResult { + + /** + * Indicates whether this write result was acknowledged. If not, then all + * other members of this result will be undefined. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + acknowledged: Boolean; + + /** + * The number of documents that were deleted. + */ + deletedCount: Int64; + +} + +class UpdateResult { + + /** + * Indicates whether this write result was acknowledged. If not, then all + * other members of this result will be undefined. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + acknowledged: Boolean; + + /** + * The number of documents that matched the filter. + */ + matchedCount: Int64; + + /** + * The number of documents that were modified. + */ + modifiedCount: Int64; + + /** + * The number of documents that were upserted. + * + * NOT REQUIRED: Drivers may choose to not provide this property so long as + * it is always possible to infer whether an upsert has taken place. Since + * the "_id" of an upserted document could be null, a null "upsertedId" may + * be ambiguous in some drivers. If so, this field can be used to indicate + * whether an upsert has taken place. + */ + upsertedCount: Int64; + + /** + * The identifier of the inserted document if an upsert took place. + */ + upsertedId: any; + +} +``` + +##### Error Handling + +Defined below are error and exception types that should be reported from the various write methods. Since error types +across languages would be impossible to reconcile, the below definitions represent the fields and names for the +information that should be present. Structure isn't important as long as the information is available. + +Drivers SHOULD report errors however they report other server errors: by raising an exception, returning "false" and +populating an error struct, or another idiom that is consistent with other server errors. + +###### WriteConcernError + +```typescript +class WriteConcernError { + + /** + * An integer value identifying the write concern error. Corresponds to the + * "writeConcernError.code" field in the command response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + code: Int32; + + /** + * A document identifying the write concern setting related to the error. + * Corresponds to the "writeConcernError.errInfo" field in the command + * response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + details: Document; + + /** + * A description of the error. Corresponds to the + * "writeConcernError.errmsg" field in the command response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + message: String; + +} +``` + +Drivers MUST construct a `WriteConcernError` from a server reply as follows: + +- Set `code` to `writeConcernError.code`. +- Set `message` to `writeConcernError.errmsg` if available. +- Set `details` to `writeConcernError.errInfo` if available. Drivers MUST NOT parse inside `errInfo`. + +See [writeConcernError Examples](../read-write-concern/read-write-concern.md#writeconcernerror-examples) in the +Read/Write Concern spec for examples of how a server represents write concern errors in replies. + +###### WriteError + +Write errors for `insert`, `update`, and `delete` commands are reported as objects within a `writeErrors` array field in +the command response. Drivers MUST construct a `WriteError` from a server reply as follows (where `writeErrors[]` refers +to a particular element in the array): + +- Set `code` to `writeErrors[].code`. +- Set `message` to `writeErrors[].errmsg` if available. +- Set `details` to `writeErrors[].errInfo` if available. Drivers MUST NOT parse inside `errInfo`. + +For single-statement writes (i.e. `insertOne`, `updateOne`, `updateMany`, `replaceOne`, `deleteOne`, and `deleteMany`), +a single write error may be reported in the array and `writeErrors[0].index` will be zero. + +For multi-statement writes (i.e. `insertMany` and `bulkWrite`), potentially many write errors may be reported in the +array and the `index` property will be set accordingly. Since the reported `index` is specific to each command, drivers +MUST adjust the index accordingly for `BulkWriteError.index`. + +```typescript +class WriteError { + + /** + * An integer value identifying the write error. Corresponds to the + * "writeErrors[].code" field in the command response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + code: Int32; + + /** + * A document providing more information about the write error (e.g. details + * pertaining to document validation). Corresponds to the + * "writeErrors[].errInfo" field in the command response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + details: Document; + + /** + * A description of the error. Corresponds to the "writeErrors[].errmsg" + * field in the command response. + * + * @see https://www.mongodb.com/docs/manual/reference/method/WriteResult/ + */ + message: String; + +} + +class BulkWriteError : WriteError { + + /** + * The index of the request that errored. This is derived in part from the + * "writeErrors[].index" field in the command response; however, drivers + * MUST adjust the index accordingly for bulk writes that execute multiple + * writes commands. + */ + index: Int32; + + /** + * The request that errored. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + request: Optional; + +} + +/** + * NOTE: Only one of writeConcernError or writeError will be populated at a time. Your driver must present the offending + * error to the user. + */ +class WriteException { + + /** + * The error that occurred on account of write concern failure. + */ + writeConcernError: Optional; + + /** + * The error that occurred on account of a non-write concern failure. + */ + writeError: Optional; + +} + +class BulkWriteException { + + /** + * The requests that were sent to the server. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + processedRequests: Optional>; + + /** + * The requests that were not sent to the server. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + unprocessedRequests: Optional>; + + /** + * The intermediary write result for any operations that succeeded before + * the bulk write was interrupted. + * + * NOT REQUIRED: Drivers may choose to not provide this property. + */ + writeResult: Optional; + + /** + * The error that occurred on account of write concern failure. If the error was a Write Concern related, this field must be present. + */ + writeConcernError: Optional; + + /** + * The error that occurred on account of a non-write concern failure. This might be empty if the error was a Write Concern related error. + */ + writeErrors: Optional>; + +} +``` + + + +##### Find And Modify + +```typescript +interface Collection { + + /** + * Finds a single document and deletes it, returning the original. The document to return may be null. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @throws WriteException + */ + findOneAndDelete(filter: Document, options: Optional): Optional; + + /** + * Finds a single document and replaces it, returning either the original or the replaced + * document. The document to return may be null. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @throws WriteException + */ + findOneAndReplace(filter: Document, replacement: Document, options: Optional): Optional; + + /** + * Finds a single document and updates it, returning either the original or the updated + * document. The document to return may be null. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @throws WriteException + */ + findOneAndUpdate(filter: Document, update: (Document | Document[]), options: Optional): Optional; + +} + +enum ReturnDocument { + /** + * Indicates to return the document before the update, replacement, or insert occurred. + */ + BEFORE, + /** + * Indicates to return the document after the update, replacement, or insert occurred. + */ + AFTER +} + +class FindOneAndDeleteOptions { + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 4.2 will report an error for using this option. + * For servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes and servers < 4.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + hint: Optional<(String | Document)>; + + /** + * The maximum amount of time to allow the query to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + maxTimeMS: Optional; + + /** + * Limits the fields to return for all matching documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * Note: this option is mapped to the "fields" findAndModify command option. + * + * @see https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results + */ + projection: Optional; + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + sort: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for findAndModify command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class FindOneAndReplaceOptions { + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + */ + bypassDocumentValidation: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 4.2 will report an error for using this option. + * For servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes and servers < 4.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + hint: Optional<(String | Document)>; + + /** + * The maximum amount of time to allow the query to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + maxTimeMS: Optional; + + /** + * Limits the fields to return for all matching documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * Note: this option is mapped to the "fields" findAndModify command option. + * + * @see https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results + */ + projection: Optional; + + /** + * When ReturnDocument.After, returns the replaced or inserted document rather than the original. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * Note: this option is mapped to the "new" findAndModify boolean field. ReturnDocument.Before represents false, + * and ReturnDocument.After represents true. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + returnDocument: Optional; + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + sort: Optional; + + /** + * When true, findAndModify creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + upsert: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for findAndModify command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} + +class FindOneAndUpdateOptions { + + /** + * A set of filters specifying to which array elements an update should apply. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/update/ + */ + arrayFilters: Optional>; + + /** + * If true, allows the write to opt-out of document level validation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.2, this option is ignored and not sent as document validation is not available. + */ + bypassDocumentValidation: Optional; + + /** + * Specifies a collation. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + collation: Optional; + + /** + * The index to use. Specify either the index name as a string or the index key pattern. + * If specified, then the query system will only consider plans using the hinted index. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 4.4. Older servers >= 4.2 will report an error for using this option. + * For servers < 4.2, the driver MUST raise an error if the caller explicitly provides a value. + * For unacknowledged writes and servers < 4.4, the driver MUST raise an error if the caller explicitly provides a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + hint: Optional<(String | Document)>; + + /** + * The maximum amount of time to allow the query to run. + * + * NOTE: This option is deprecated in favor of timeoutMS. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + maxTimeMS: Optional; + + /** + * Limits the fields to return for all matching documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * Note: this option is mapped to the "fields" findAndModify command option. + * + * @see https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results + */ + projection: Optional; + + /** + * When ReturnDocument.After, returns the replaced or inserted document rather than the original. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * Note: this option is mapped to the "new" findAndModify boolean field. ReturnDocument.Before represents false, + * and ReturnDocument.After represents true. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + returnDocument: Optional; + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + sort: Optional; + + /** + * When true, creates a new document if no document matches the query. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + upsert: Optional; + + /** + * 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 option is sent only if the caller explicitly provides a value. The default is to not send a value. + * This option is only supported by servers >= 5.0. Older servers >= 2.6 (and possibly earlier) will report an error for using this option. + * + * @see https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + let: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * + * The comment can be any valid BSON type for server versions 4.4 and above. + * Server versions prior to 4.4 do not support comment for findAndModify command, + * and providing one will result in a server-side error. + */ + comment: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} +``` + +##### Update vs. Replace Validation + +The `update` family of operations require that the update document parameter MUST have only atomic modifiers. In +practice, this means that introspection needs to happen on that document to enforce this. However, it is enough to only +check the first element in the document. If it begins with a `$` sign and the rest of the document's elements do not, +the server will throw an error. Note that it is required that an update document have at least one atomic modifier. + +The `replace` family of operations require that the replacement document parameter MUST NOT begin with an atomic +modifier. In practice, this means that introspection needs to happen on that document to enforce this. However, it is +enough to only check the first element in the document. If it does not begin with a `$` sign but an element later on +does, the server will throw an error. + +##### Aggregation Pipelines with Write Stages + +This section discusses special considerations for aggregation pipelines that contain write stages (e.g. `$out`, +`$merge`). + +###### Returning a cursor on the output collection + +As noted in the documentation for the `aggregate` helper earlier in this document, `$out` and `$merge` are special +pipeline stages that cause no results to be returned from the server. As such, drivers MAY setup a cursor to be executed +upon iteration against the output collection and return that instead of an iterable that would otherwise have no +results. + +Drivers that do so for `$merge` MAY remind users that such a cursor may return more documents than were written by the +aggregation (e.g. documents that existed in the collection prior to `$merge` being executed). + +###### Read preferences and server selection + +This section is only applicable if an explicit (i.e. per-operation) or inherited (e.g. from a Collection) read +preference is available and it is *not* a primary read preference (i.e. `{ "mode": "primary" }`). + +Historically, only primaries could execute an aggregation pipeline with `$out` or `$merge` and drivers never considered +a read preference for the operation. As of `featureCompatibilityVersion` 4.4, secondaries can now execute pipelines with +`$out` or `$merge`. Since drivers do not track `featureCompatibilityVersion`, the decision to consider a read preference +for such a pipeline will depend on the wire version(s) of the server(s) to which the driver is connected. + +If there are one or more available servers and one or more of those servers is pre-5.0 (i.e. wire version < 13), drivers +MUST NOT use the available read preference and MUST instead select a server using a primary read preference. + +Otherwise, if there are either no available servers, all available servers are 5.0+ (i.e. wire version >= 13), or the +topology type is LoadBalanced (we can assume the backing mongos is 5.0+), drivers MUST use the available read +preference. + +Drivers SHOULD augment their +[server selection algorithm](../server-selection/server-selection.md#server-selection-algorithm) such that this logic +can be enforced within a single server selection attempt. + +Drivers MUST discern the read preference used to select a server for the operation, which SHALL be used for specifying +the [$readPreference global command argument](../message/OP_MSG.md#global-command-arguments) and +[passing read preference to mongos and load balancers](../server-selection/server-selection.md#passing-read-preference-to-mongos-and-load-balancers) +(if applicable). + +### Explain + +> [!NOTE] +> Explain helpers are optional. Drivers that do not provide explain helpers may ignore this section. + +```typescript +interface ExplainOptions { + /** + * The maximum amount of time to allow the explain to run. + * + * This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * NOTE: This option is deprecated in favor of timeoutMS. + */ + maxTimeMS: Optional; +} +``` + +Drivers MUST ensure that its helper permits users to specify a timeout (maxTimeMS or timeoutMS) for the explain command +specifically. An example, using Node, might look like: + +```typescript +collection.find({ name: 'john doe' }).explain({ maxTimeMS: 1000 }); + +// sends: +{ + explain: { find: , query: { name: 'john doe' } }, + maxTimeMS: 1000 +} + +collection.find({ name: 'john doe' }).explain({ timeoutMS: 1000 }); + +// sends: +{ + explain: { find: , query: { name: 'john doe' } }, + maxTimeMS: <1000 - min rtt> +} +``` + +Drivers MUST document how users can specify options on their explain helpers. + +## Test Plan + +See the [README](tests/README.md) for tests. + +## Motivation + +Current drivers have chosen slightly different names and semantics for the same operations and options. In addition, not +all drivers offer all the same operations and methods. As such, it is difficult to transition from driver to driver +making the jobs of polyglot developers, documentation authors, and support engineers more difficult. + +## Backwards Compatibility + +This spec should be mostly backwards compatible as it is very lenient. Drivers finding a backwards compatibility problem +should attempt to work around it using an acceptable deviation. In rare cases, a driver may need to break backwards +compatibility. This should be done in accordance with a versioning scheme indicating that a backwards compatible break +may have occurred in conjunction with release documentation and warnings. + +## Reference Implementation + +See Test Plan + +## Related Terminology + +If a driver needs to refer to items in the following list, the below are the accepted forms of those terms and +deviations from the Naming section are still permissible. + +- Read Preference: readPreference +- Read Concern: readConcern +- Write Concern: writeConcern + +## Q & A + +Q: Why do the names of the fields differ from those defined in the MongoDB manual? + +Documentation and commands often refer to same-purposed fields with different names making it difficult to have a +cohesive API. In addition, occasionally the name was correct at one point and its purpose has expanded to a point where +the initial name doesn't accurately describe its current function. + +In addition, responses from the servers are sometimes cryptic and used for the purposes of compactness. In these cases, +we felt the more verbose form was desirable for self-documentation purposes. + +Q: Where is read preference? + +Read preference is about selecting a server with which to perform a read operation, such as a query, a count, or an +aggregate. Since all operations defined in this specification are performed on a collection, it's uncommon that two +different read operations on the same collection would use a different read preference, potentially getting out-of-sync +results. As such, the most natural place to indicate read preference is on the client, the database, or the collection +itself and not the operations within it. + +However, it might be that a driver needs to expose this selection filter to a user per operation for various reasons. As +noted before, it is permitted to specify this, along with other driver-specific options, in some alternative way. + +Q: Where is read concern? + +Read concern is about indicating how reads are handled. Since all operations defined in this specification are performed +on a collection, it's uncommon that two different read operations on the same collection would use a different read +concern, potentially causing mismatched and out-of-sync data. As such, the most natural place to indicate read concern +is on the client, the database, or the collection itself and not the operations within it. + +However, it might be that a driver needs to expose read concern to a user per operation for various reasons. As noted +before, it is permitted to specify this, along with other driver-specific options, in some alternative way. + +Q: Where is write concern? + +Write concern is about indicating how writes are acknowledged. Since all operations defined in this specification are +performed on a collection, it's uncommon that two different write operations on the same collection would use a +different write concern, potentially causing mismatched and out-of-sync data. As such, the most natural place to +indicate write concern is on the client, the database, or the collection itself and not the operations within it. See +the [Read/Write Concern specification](../read-write-concern/read-write-concern.md) for the API of constructing a +read/write concern and associated API. + +However, it might be that a driver needs to expose write concern to a user per operation for various reasons. As noted +before, it is permitted to specify this, along with other driver-specific options, in some alternative way. + +Q: How do I throttle unacknowledged writes now that write concern is no longer defined on a per operation basis? + +Some users used to throttle unacknowledged writes by using an acknowledged write concern every X number of operations. +Going forward, the proper way to handle this is by using the bulk write API. + +Q: What is the logic for adding "One" or "Many" into the method and model names? + +If the maximum number of documents affected can only be one, we added "One" into the name. This makes it explicit that +the maximum number of documents that could be affected is one vs. infinite. + +In addition, the current API exposed by all our drivers has the default value for "one" or "many" set differently for +update and delete. This generally causes some issues for new developers and is a minor annoyance for existing +developers. The safest way to combat this without introducing discrepancies between drivers/driver versions or breaking +backwards compatibility was to use multiple methods, each signifying the number of documents that could be affected. + +Q: What considerations have been taken for the eventual merging of query and the aggregation framework? + +In the future, it is probable that a new query engine (QE) will look very much like the aggregation framework. Given +this assumption, we know that both `find` and `aggregate` will be renderable in QE, each maintaining their ordering +guarantees for full backwards compatibility. + +Hence, the only real concern is how to initiate a query using QE. While `find` is preferable, it would be a backwards +breaking change. It might be decided that `find` is what should be used, and all drivers will release major revisions +with this backwards breaking change. Alternatively, it might be decided that another initiator would be used. + +Q: Didn't we just build a bulk API? + +Yes, most drivers did just build out a bulk API (fluent-bulk-api). While unfortunate, we felt it better to have the bulk +api be consistent with the rest of the methods in the CRUD family of operations. However, the fluent-bulk-api is still +able to be used as this change is non-backwards breaking. Any driver which implemented the fluent bulk API should +deprecate it and drivers that have not built it should not do so. + +Q: Should drivers offer explain helpers?\ +Originally, it was determined that explain should not be exposed via specialized APIs in drivers because it it was +deemed to be an unusual use-case for a driver. We'd like users to use the shell for this purpose. However, explain is +still possible from a driver. Some drivers have historically provided explain helpers and continue to do so. Drivers +that do not offer explain helpers can run explain commands using the runCommand API. + +Q: What about explain? + +Explain has been determined to be not a normal use-case for a driver. We'd like users to use the shell for this purpose. +However, explain is still possible from a driver. For find, it can be passed as a modifier. Aggregate can be run using a +runCommand method passing the explain option. In addition, server 3.0 offers an explain command that can be run using a +runCommand method. + +Q: Where did modifiers go in FindOptions? + +MongoDB 3.2 introduced the find command. As opposed to using the general "modifiers" field any longer, each relevant +option is listed explicitly. Some options, such as "tailable" or "singleBatch" are not listed as they are derived from +other fields. Upgrading a driver should be a simple procedure of deprecating the "modifiers" field and introducing the +new fields. When a collision occurs, the explicitly specified field should override the value in "modifiers". + +Q: Where is `save`? + +Drivers have historically provided a `save` method, which was syntactic sugar for upserting or inserting a document +based on whether it contained an identifier, respectively. While the `save` method may be convenient for interactive +environments, such as the shell, it was intentionally excluded from the CRUD specification for language drivers for +several reasons. The `save` method promotes a design pattern of "fetch, modify, replace" and invites race conditions in +application logic. Additionally, the split nature of `save` makes it difficult to discern at a glance if application +code will perform an insert or potentially dangerous full-document replacement. Instead of relying on `save`, +application code should know whether document already has an identifier and explicitly call `insertOne` or `replaceOne` +with the `upsert` option. + +Q: Where is `useCursor` in AggregateOptions? + +Inline aggregation results are no longer supported in server 3.5.2+. The +[aggregate command](https://www.mongodb.com/docs/manual/reference/command/aggregate/) must be provided either the +`cursor` document or the `explain` boolean. AggregateOptions does not define an `explain` option. If a driver does +support an `explain` option, the `cursor` document should be omitted if `explain` is `true`. Otherwise a `cursor` +document must be added to the `aggregate` command. Regardless, `useCursor` is no longer needed. Removing `useCursor` is +a backwards breaking change, so drivers should first deprecate this option in a minor release, and remove it in a major +release. + +Q: Where is `singleBatch` in FindOptions? + +Drivers have historically allowed users to request a single batch of results (after which the cursor is closed) by +specifying a negative value for the `limit` option. For servers < 3.2, a single batch may be requested by specifying a +negative value in the `numberToReturn` wire protocol field. For servers >= 3.2, the `find` command defines `limit` as a +non-negative integer option but introduces a `singleBatch` boolean option. Rather than introduce a `singleBatch` option +to FindOptions, the spec preserves the existing API for `limit` and instructs drivers to convert negative values +accordingly for servers >= 3.2. + +Q: Why are client-side errors raised for some unsupported options? + +Server versions before 3.4 were inconsistent about reporting errors for unrecognized command options and may simply +ignore them, which means a client-side error is the only way to inform users that such options are unsupported. For +unacknowledged writes using OP_MSG, a client-side error is necessary because the server has no chance to return a +response (even though a 3.6+ server is otherwise capable of reporting errors for unrecognized options). For +unacknowledged writes using legacy opcodes (i.e. OP_INSERT, OP_UPDATE, and OP_DELETE), the message body has no field +with which to express these options so a client-side error is the only mechanism to inform the user that such options +are unsupported. The spec does not explicitly refer to unacknowledged writes using OP_QUERY primarily because a response +document is always returned and drivers generally would not consider using OP_QUERY precisely for that reason. + +Q: Why does reverting to using `count` instead of `aggregate` with `$collStats` for estimatedDocumentCount not require a +major version bump in the drivers, even though it might break users of the Stable API? + +SemVer +[allows](https://semver.org/#what-if-i-inadvertently-alter-the-public-api-in-a-way-that-is-not-compliant-with-the-version-number-change-ie-the-code-incorrectly-introduces-a-major-breaking-change-in-a-patch-release) +for a library to include a breaking change in a minor or patch version if the change is required to fix another +accidental breaking change introduced in a prior version and is not expected to further break a large number of users. +Given that the original switch to `$collStats` was a breaking change due to it not working on views, the number of users +using estimatedDocumentCount with `apiStrict: true` is small, and the server is back-porting the addition of `count` to +the Stable API, it was decided that this change was acceptable to make in minor version releases of the drivers per the +aforementioned allowance in the SemVer spec. + +## Changelog + +- 2025-09-09: Clarify that `rawData` is for internal use only. + +- 2025-06-27: Added `rawData` options. + +- 2024-11-13: Define `findOne` operation as optional, and add guidance on `limit` and `batchSize` for `find` operations. + +- 2024-11-04: Always send a value for `bypassDocumentValidation` if it was specified. + +- 2024-11-01: Add hint to DistinctOptions + +- 2024-10-30: Document query limitations in `countDocuments`. + +- 2024-10-28: Clarified that generated identifiers should be prepended to documents. + +- 2024-10-01: Add sort option to `replaceOne` and `updateOne`. + +- 2024-09-12: Specify that explain helpers support maxTimeMS. + +- 2024-02-20: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-04-21: Revert to using the `count` command for `estimatedDocumentCount` + +- 2022-02-18: Add let to BulkWriteOptions. + +- 2022-02-10: Specified that `getMore` command must explicitly send inherited comment. + +- 2022-02-01: Add comment attribute to all helpers. + +- 2022-01-27: Use optional return types for write commands and findAndModify + +- 2022-01-19: Deprecate the maxTimeMS option and require that timeouts be applied per the client-side operations timeout + spec. + +- 2022-01-14: Add let to ReplaceOptions + +- 2021-11-10: Revise rules for applying read preference for aggregations with $out and $merge. Add let to FindOptions, + UpdateOptions, DeleteOptions, FindOneAndDeleteOptions, FindOneAndReplaceOptions, FindOneAndUpdateOptions + +- 2021-09-28: Support aggregations with $out and $merge on 5.0+ secondaries + +- 2021-08-31: Allow unacknowledged hints on write operations if supported by server (reverts previous change). + +- 2021-06-02: Introduce WriteError.details and clarify WriteError construction + +- 2021-06-01: Add let to AggregateOptions + +- 2021-01-21: Update estimatedDocumentCount to use $collStats stage for servers >= 4.9 + +- 2020-04-17: Specify that the driver must raise an error for unacknowledged hints on any write operation, regardless of + server version. + +- 2020-03-19: Clarify that unacknowledged update, findAndModify, and delete operations with a hint option should raise + an error on older server versions. + +- 2020-03-06: Added hint option for DeleteOne, DeleteMany, and FindOneAndDelete operations. + +- 2020-01-24: Added hint option for findAndModify update/replace operations. + +- 2020-01-17: Add allowDiskUse to FindOptions. + +- 2020-01-14: Deprecate oplogReplay option for find command + +- 2020-01-10: Clarify client-side error reporting for unsupported options + +- 2020-01-10: Error if hint specified for unacknowledged update using OP_UPDATE or OP_MSG for servers < 4.2 + +- 2019-10-28: Removed link to old language examples. + +- 2019-09-26: Added hint option for update commands. + +- 2019-06-07: Consistent treatment for aggregate $merge and $out stages + +- 2019-05-01: Specify a document or pipeline for commands with updates in server 4.2+. + +- 2019-02-20: Mark the request field of BulkWriteError as NOT REQUIRED + +- 2018-11-30: Specify maxAwaitTimeMS in AggregateOptions + +- 2018-11-15: Aggregate commands with an $out stage should not specify batchSize + +- 2018-10-25: Note how results are backed for aggregate, distinct, and find operations + +- 2018-07-25: Added upsertedCount to UpdateResult. + +- 2018-06-07: Deprecated the count helper. Added the estimatedDocumentCount and countDocuments helpers. + +- 2018-03-05: Deprecate snapshot option + +- 2018-03-01: Deprecate maxScan query option. + +- 2018-02-06: Note that batchSize in FindOptions and AggregateOptions should also apply to getMore. + +- 2018-01-26: Only send bypassDocumentValidation option if it's true, don't send false. + +- 2017-10-23: Allow BulkWriteException to provide an intermediary write result. + +- 2017-10-17: Document negative limit for FindOptions. + +- 2017-10-09: Bumped minimum server version to 2.6 and removed references to older versions in spec and tests. + +- 2017-10-09: Prohibit empty insertMany() and bulkWrite() operations. + +- 2017-10-09: Split UpdateOptions and ReplaceOptions. Since replaceOne() previously used UpdateOptions, this may have BC + implications for drivers using option classes. + +- 2017-10-05: Removed useCursor option from AggregateOptions. + +- 2017-09-26: Added hint option to AggregateOptions. + +- 2017-09-25: Added comment option to AggregateOptions. + +- 2017-08-31: Added arrayFilters to bulk write update models. + +- 2017-06-29: Remove requirement of using OP_KILL_CURSOR to kill cursors. + +- 2017-06-27: Added arrayFilters to UpdateOptions and FindOneAndUpdateOptions. + +- 2017-06-26: Added FAQ entry for omission of save method. + +- 2017-05-12: Removed extra "collation" option added to several bulk write models. + +- 2017-01-09: Removed modifiers from FindOptions and added in all options. + +- 2017-01-09: Changed the value type of FindOptions.skip and FindOptions.limit to Int64 with a note related to + calculating batchSize for opcode writes. + +- 2017-01-09: Reworded description of how default values are handled and when to send certain options. + +- 2016-09-23: Included collation option in the bulk write models. + +- 2016-08-05: Added in collation option. + +- 2015-11-05: Typos in comments about bypassDocumentValidation + +- 2015-10-16: Added maxAwaitTimeMS to FindOptions. + +- 2015-10-01: Moved bypassDocumentValidation into BulkWriteOptions and removed it from the individual write models. + +- 2015-09-16: Added bypassDocumentValidation. + +- 2015-09-16: Added readConcern notes. + +- 2015-06-17: Added limit/batchSize calculation logic. diff --git a/source/crud/crud.rst b/source/crud/crud.rst index aa589e2d06..8f6853092a 100644 --- a/source/crud/crud.rst +++ b/source/crud/crud.rst @@ -1,1746 +1,7 @@ .. role:: javascript(code) :language: javascript -=============== -Driver CRUD API -=============== - -:Spec: 110 -:Title: Driver CRUD API -:Authors: Craig Wilson -:Advisors: Jeremy Mikola, Jeff Yemin -:Status: Approved -:Type: Standards -:Minimum Server Version: 2.6 -:Last Modified: July 25, 2018 - -.. contents:: - --------- - -Abstract -======== - -The CRUD API defines a set of related methods and structures defining a driver's API. As not all languages/frameworks have the same abilities, parts of the spec may or may not apply. These sections have been called out. - - -Specification -============= - -This specification is about `Guidance`_ in 3 areas related to the CRUD API as well as a specification for the API itself. It does not define implementation details and provides room and flexibility for the idioms and differences in languages and frameworks. - - ------------ -Definitions ------------ - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. - - -Terms ------ - -Collection - The term ``interface Collection`` will be seen in most of the sections. Each driver will likely have a class or interface defined for the concept of a collection. Operations appearing inside the ``interface Collection`` are required operations to be present on a driver's concept of a collection. - -Iterable - The term ``Iterable`` will be seen as a return type from some of the `Read`_ methods. Its use is as that of a sequence of items. For instance, ``collection.find({})`` returns a sequence of documents that can be iterated over. - - --------- -Guidance --------- - -Documentation -------------- - -The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation for the driver. - - -Operations ----------- - -All drivers MUST offer the operations defined in the following sections. This does not preclude a driver from offering more. - - -Operation Parameters --------------------- - -All drivers MUST offer the same options for each operation as defined in the following sections. This does not preclude a driver from offering more. A driver SHOULD NOT require a user to specify optional parameters, denoted by the Optional<> signature. Unless otherwise specified, optional values should not be sent to the server. - -~~~~~~~~~~ -Deviations -~~~~~~~~~~ - -A non-exhaustive list of acceptable deviations are as follows: - -* Using named parameters instead of an options hash. For instance, ``collection.find({x:1}, sort: {a: -1})``. - -* When using an ``Options`` class, if multiple ``Options`` classes are structurally equatable, it is permissible to consolidate them into one with a clear name. For instance, it would be permissible to use the name ``UpdateOptions`` as the options for ``UpdateOne`` and ``UpdateMany``. - -* Using a fluent style builder for find or aggregate: - - .. code:: typescript - - collection.find({x: 1}).sort({a: -1}).skip(10); - - When using a fluent-style builder, all options should be named rather than inventing a new word to include in the pipeline (like options). Required parameters are still required to be on the initiating method. - - In addition, it is imperative that documentation indicate when the order of operations is important. For instance, skip and limit in find is order irrelevant where skip and limit in aggregate is not. - - -Naming ------- - -All drivers MUST name operations, objects, and parameters as defined in the following sections. - -Deviations are permitted as outlined below. - - -~~~~~~~~~~ -Deviations -~~~~~~~~~~ - -When deviating from a defined name, an author should consider if the altered name is recognizable and discoverable to the user of another driver. - -A non-exhaustive list of acceptable naming deviations are as follows: - -* Using "batchSize" as an example, Java would use "batchSize" while Python would use "batch_size". However, calling it "batchCount" would not be acceptable. -* Using "maxTimeMS" as an example, .NET would use "MaxTime" where it's type is a TimeSpan structure that includes units. However, calling it "MaximumTime" would not be acceptable. -* Using "FindOptions" as an example, Javascript wouldn't need to name it while other drivers might prefer to call it "FindArgs" or "FindParams". However, calling it "QueryOptions" would not be acceptable. -* Using "isOrdered" rather than "ordered". Some languages idioms prefer the use of "is", "has", or "was" and this is acceptable. - - ---- -API ---- - -Read ----- .. note:: - - The term Iterable is used below to indicate many of T. This spec is flexible on what that means as different drivers will have different requirements, types, and idioms. - -.. code:: typescript - - interface Collection { - - /** - * Runs an aggregation framework pipeline. - * - * Note: $out is a special pipeline stage that causes no results to be returned - * from the server. As such, the iterable here would never contain documents. Drivers - * MAY setup a cursor to be executed upon iteration against the $out collection such - * that if a user were to iterate a pipeline including $out, results would be returned. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - aggregate(pipeline: Document[], options: Optional): Iterable; - - /** - * Gets the number of documents matching the filter. - * - * **This method is DEPRECATED and should not be implemented in new drivers.** - * - * @see https://docs.mongodb.com/manual/reference/command/count/ - @deprecated 4.0 - */ - count(filter: Document, options: Optional): Int64; - - /** - * Gets the number of documents matching the filter. - * - * See "Count API Details" section below. - */ - countDocuments(filter: Document, options: Optional): Int64; - - /** - * Gets an estimate of the count of documents in a collection using collection metadata. - * - * See "Count API Details" section below. - */ - estimatedDocumentCount(options: Optional): Int64; - - /** - * Finds the distinct values for a specified field across a single collection. - * - * @see https://docs.mongodb.com/manual/reference/command/distinct/ - */ - distinct(fieldName: string, filter: Document, options: Optional): Iterable; - - /** - * Finds the documents matching the model. - * - * Note: The filter parameter below equates to the $query meta operator. It cannot - * contain other meta operators like $maxScan. However, do not validate this document - * as it would be impossible to be forwards and backwards compatible. Let the server - * handle the validation. - * - * Note: If $explain is specified in the modifiers, the return value is a single - * document. This could cause problems for static languages using strongly typed entities. - * - * @see https://docs.mongodb.com/manual/core/read-operations-introduction/ - */ - find(filter: Document, options: Optional): Iterable; - - } - - class AggregateOptions { - - /** - * Enables writing to temporary files. When set to true, aggregation stages - * can write data to the _tmp subdirectory in the dbPath directory. - * - * This option is sent only if the caller explicitly provides a value. The default - * is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - allowDiskUse: Optional; - - /** - * The number of documents to return per batch. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * If specified, drivers SHOULD apply this option to both the original aggregate command and subsequent - * getMore operations on the cursor. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - batchSize: Optional; - - /** - * If true, allows the write to opt-out of document level validation. This only applies - * when the $out stage is specified. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - bypassDocumentValidation: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - collation: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/aggregate/ - */ - maxTimeMS: Optional; - - /** - * Enables users to specify an arbitrary string to help trace the operation through - * the database profiler, currentOp and logs. The default is to not send a value. - * - * @see http://docs.mongodb.com/manual/reference/command/aggregate/ - */ - comment: Optional; - - /** - * The index to use for the aggregation. The hint does not apply to $lookup and $graphLookup stages. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see http://docs.mongodb.com/manual/reference/command/aggregate/ - */ - hint: Optional<(String | Document)>; - } - - class CountOptions { - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - */ - collation: Optional; - - /** - * The index to use. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - */ - hint: Optional<(String | Document)>; - - /** - * The maximum number of documents to count. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - */ - limit: Optional; - - /** - * The maximum amount of time to allow the operation to run. - - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - */ - maxTimeMS: Optional; - - /** - * The number of documents to skip before counting. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - */ - skip: Optional; - } - - class EstimatedDocumentCountOptions { - - /** - * The maximum amount of time to allow the operation to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - */ - maxTimeMS: Optional; - } - - class DistinctOptions { - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/distinct/ - */ - collation: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/distinct/ - */ - maxTimeMS: Optional; - } - - enum CursorType { - /** - * The default value. A vast majority of cursors will be of this type. - */ - NON_TAILABLE, - /** - * Tailable means the cursor is not closed when the last data is retrieved. - * Rather, the cursor marks the final object’s position. You can resume - * using the cursor later, from where it was located, if more data were - * received. Like any “latent cursor”, the cursor may become invalid at - * some point (CursorNotFound) – for example if the final object it - * references were deleted. - * - * @see https://docs.mongodb.com/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query - */ - TAILABLE, - /** - * Combines the tailable option with awaitData, as defined below. - * - * Use with TailableCursor. If we are at the end of the data, block for a - * while rather than returning no data. After a timeout period, we do return - * as normal. The default is true. - * - * @see https://docs.mongodb.com/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query - */ - TAILABLE_AWAIT - } - - class FindOptions { - - /** - * Get partial results from a mongos if some shards are down (instead of throwing an error). - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, the Partial wire protocol flag is used and defaults to false. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - allowPartialResults: Optional; - - /** - * The number of documents to return per batch. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, this is combined with limit to create the wire protocol numberToReturn value. - * If specified, drivers SHOULD apply this option to both the original query operation and subsequent - * getMore operations on the cursor. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - batchSize: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - collation: Optional; - - /** - * Attaches a comment to the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - comment: Optional; - - /** - * Indicates the type of cursor to use. This value includes both - * the tailable and awaitData options. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, the AwaitData and Tailable wire protocol flags are used and default to false. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - cursorType: Optional; - - /** - * The index to use. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - hint: Optional<(String | Document)>; - - /** - * The maximum number of documents to return. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, this is combined with batchSize to create the wire protocol numberToReturn value. - * - * A negative limit implies that the caller has requested a single batch of results. For servers >= 3.2, singleBatch - * should be set to true and limit should be converted to a positive value. For servers < 3.2, the wire protocol - * numberToReturn value may be negative. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - limit: Optional; - - /** - * The exclusive upper bound for a specific index. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - max: Optional; - - /** - * The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor - * query. This only applies to a TAILABLE_AWAIT cursor. When the cursor is not a TAILABLE_AWAIT cursor, - * this option is ignored. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as maxTimeMS does not exist in the OP_GET_MORE wire protocol. - * - * Note: This option is specified as "maxTimeMS" in the getMore command and not provided as part of the - * initial find command. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - maxAwaitTimeMS: Optional; - - /** - * Maximum number of documents or index keys to scan when executing the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - * @deprecated 4.0 - */ - maxScan: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - maxTimeMS: Optional; - - /** - * The inclusive lower bound for a specific index. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - min: Optional; - - /** - * The server normally times out idle cursors after an inactivity period (10 minutes) - * to prevent excess memory use. Set this option to prevent that. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, the NoCursorTimeout wire protocol flag is used and defaults to false. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - noCursorTimeout: Optional; - - /** - * Internal replication use only - driver should not set - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, the OplogReplay wire protocol flag is used and defaults to false. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - oplogReplay: Optional; - - /** - * Limits the fields to return for all matching documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - projection: Optional; - - /** - * If true, returns only the index keys in the resulting documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - returnKey: Optional; - - /** - * Determines whether to return the record identifier for each document. If true, adds a field $recordId to the returned documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - showRecordId: Optional; - - /** - * The number of documents to skip before returning. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.2, this is a wire protocol parameter that defaults to 0. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - skip: Optional; - - /** - * Prevents the cursor from returning a document more than once because of an intervening write operation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - * @deprecated 4.0 - */ - snapshot: Optional; - - /** - * The order in which to return matching documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/find/ - */ - sort: Optional; - } - -~~~~~~~~~~~~~~~~~ -Count API Details -~~~~~~~~~~~~~~~~~ - -MongoDB drivers provide two helpers for counting the number of documents in a -collection, estimatedDocumentCount and countDocuments. The names were chosen -to make it clear how they behave and exactly what they do. The -estimatedDocumentCount helper returns an estimate of the count of documents -in the collection using collection metadata, rather than counting the -documents or consulting an index. The countDocuments helper counts the -documents that match the provided query filter using an aggregation pipeline. - -The count() helper is deprecated. It has always been implemented using the -`count` command. The behavior of the count command differs depending on the -options passed to it and may or may not provide an accurate count. When -no query filter is provided the count command provides an estimate using -collection 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 countDocuments helper avoids these sharded cluster problems entirely -when used with MongoDB 3.6+, and when using `Primary` read preference with -older sharded clusters. - -~~~~~~~~~~~~~~~~~~~~~~ -estimatedDocumentCount -~~~~~~~~~~~~~~~~~~~~~~ - -The estimatedDocumentCount function is implemented using the `count` command -with no query filter, skip, limit, or other options that would alter the -results. As documented above, the only supported option is maxTimeMS. - -~~~~~~~~~~~~~~ -countDocuments -~~~~~~~~~~~~~~ - -The countDocuments function is implemented using the `$group` aggregate -pipeline stage with `$sum`. Applications must be required to pass a value -for filter, but an empty document is supported:: - - pipeline = [{'$match': filter}] - if (skip) { - pipeline.push({'$skip': skip}) - } - if (limit) { - pipeline.push({'$limit': limit}) - } - pipeline.push({'$group': {'_id': null, 'n': {'$sum': 1}}}) - -The count of documents is returned in the 'n' field, similar to the `count` -command. countDocuments options other than filter, skip, and limit are added as -options to the `aggregate` command. - -In the event this aggregation is run against an empty collection, an empty -array will be returned with no ``n`` field. Drivers MUST interpret this result -as a ``0`` count. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Combining Limit and Batch Size for the Wire Protocol -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The OP_QUERY wire protocol only contains a numberToReturn value which drivers must calculate to get expected limit and batch size behavior. Subsequent calls to OP_GET_MORE should use the user-specified batchSize or default to 0. If the result is larger than the max Int32 value, an error MUST be raised as the computed value is impossible to send to the server. Below is pseudo-code for calculating numberToReturn for OP_QUERY. - -.. code:: typescript - - function calculateFirstNumberToReturn(options: FindOptions): Int32 { - Int32 numberToReturn; - Int32 limit = options.limit || 0; - Int32 batchSize = options.batchSize || 0; - - if (limit < 0) { - numberToReturn = limit; - } - else if (limit == 0) { - numberToReturn = batchSize; - } - else if (batchSize == 0) { - numberToReturn = limit; - } - else if (limit < batchSize) { - numberToReturn = limit; - } - else { - numberToReturn = batchSize; - } - - return numberToReturn; - } - -Because of this anomaly in the wire protocol, it is up to the driver to enforce the user-specified limit. Each driver MUST keep track of how many documents have been iterated and stop iterating once the limit has been reached. When the limit has been reached, if the cursor is still open, a driver MUST kill the cursor. - -Write ------ - -~~~~~ -Basic -~~~~~ - -.. code:: typescript - - interface Collection { - - /** - * Executes multiple write operations. - * - * An error MUST be raised if the requests parameter is empty. - * - * For servers < 3.4, if a collation was explicitly set for any request, an error MUST be raised - * and no documents sent. - * - * NOTE: see the FAQ about the previous bulk API and how it relates to this. - * @see https://docs.mongodb.com/manual/reference/command/delete/ - * @see https://docs.mongodb.com/manual/reference/command/insert/ - * @see https://docs.mongodb.com/manual/reference/command/update/ - * @throws InvalidArgumentException if requests is empty - * @throws BulkWriteException - */ - bulkWrite(requests: WriteModel[], options: Optional): BulkWriteResult; - - /** - * Inserts the provided document. If the document is missing an identifier, - * the driver should generate one. - * - * @see https://docs.mongodb.com/manual/reference/command/insert/ - * @throws WriteException - */ - insertOne(document: Document, options: Optional): InsertOneResult; - - /** - * Inserts the provided documents. If any documents are missing an identifier, - * the driver should generate them. - * - * An error MUST be raised if the documents parameter is empty. - * - * Note that this uses the bulk insert command underneath and should not - * use OP_INSERT. - * - * @see https://docs.mongodb.com/manual/reference/command/insert/ - * @throws InvalidArgumentException if documents is empty - * @throws BulkWriteException - */ - insertMany(documents: Iterable, options: Optional): InsertManyResult; - - /** - * Deletes one document. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - * @throws WriteException - */ - deleteOne(filter: Document, options: Optional): DeleteResult; - - /** - * Deletes multiple documents. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - * @throws WriteException - */ - deleteMany(filter: Document, options: Optional): DeleteResult; - - /** - * Replaces a single document. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - * @throws WriteException - */ - replaceOne(filter: Document, replacement: Document, options: Optional): UpdateResult; - - /** - * Updates one document. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - * @throws WriteException - */ - updateOne(filter: Document, update: Document, options: Optional): UpdateResult; - - /** - * Updates multiple documents. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - * @throws WriteException - */ - updateMany(filter: Document, update: Document, options: Optional): UpdateResult; - } - - class BulkWriteOptions { - - /** - * If true, when a write fails, return without performing the remaining - * writes. If false, when a write fails, continue with the remaining writes, if any. - * Defaults to true. - */ - ordered: Boolean; - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - */ - bypassDocumentValidation: Optional; - } - - class InsertOneOptions { - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - */ - bypassDocumentValidation: Optional; - } - - class InsertManyOptions { - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - */ - bypassDocumentValidation: Optional; - - /** - * If true, when an insert fails, return without performing the remaining - * writes. If false, when a write fails, continue with the remaining writes, if any. - * Defaults to true. - */ - ordered: Boolean; - } - - class UpdateOptions { - - /** - * A set of filters specifying to which array elements an update should apply. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - arrayFilters: Optional>; - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - */ - bypassDocumentValidation: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - collation: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - upsert: Optional; - } - - class ReplaceOptions { - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - */ - bypassDocumentValidation: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - collation: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - upsert: Optional; - } - - class DeleteOptions { - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - */ - collation: Optional; - } - - -Bulk Write Models -~~~~~~~~~~~~~~~~~ - -.. code:: typescript - - interface WriteModel { - // marker interface for writes that can be batched together. - } - - class InsertOneModel implements WriteModel { - - /** - * The document to insert. - * - * @see https://docs.mongodb.com/manual/reference/command/insert/ - */ - document: Document; - } - - class DeleteOneModel implements WriteModel { - - /** - * The filter to limit the deleted documents. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - */ - filter: Document; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - */ - collation: Optional; - } - - class DeleteManyModel implements WriteModel { - - /** - * The filter to limit the deleted documents. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - */ - filter: Document; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/delete/ - */ - collation: Optional; - } - - class ReplaceOneModel implements WriteModel { - - /** - * The filter to limit the replaced document. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - filter: Document; - - /** - * The document with which to replace the matched document. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - replacement: Document; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - collation: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - upsert: Optional; - } - - class UpdateOneModel implements WriteModel { - - /** - * The filter to limit the updated documents. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - filter: Document; - - /** - * A document containing update operators. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - update: Update; - - /** - * A set of filters specifying to which array elements an update should apply. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - arrayFilters: Optional>; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - collation: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - upsert: Optional; - } - - class UpdateManyModel implements WriteModel { - - /** - * The filter to limit the updated documents. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - filter: Document; - - /** - * A document containing update operators. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - update: Update; - - /** - * A set of filters specifying to which array elements an update should apply. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - arrayFilters: Optional>; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - collation: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - upsert: Optional; - } - - -Results -~~~~~~~ - -The acknowledged property is defined for languages/frameworks without a sufficient optional type. Hence, a driver may choose to return an Optional such that unacknowledged writes don't have a value and acknowledged writes do have a value. - -.. note:: - If you have a choice, consider providing the acknowledged member and raising an error if the other fields are accessed in an unacknowledged write. Instead of users receiving a null reference exception, you have the opportunity to provide an informative error message indicating the correct way to handle the situation. For instance, "The insertedCount member is not available when the write was unacknowledged. Check the acknowledged member to avoid this error." - -Any result class with all parameters marked NOT REQUIRED is ultimately NOT REQUIRED as well. For instance, the ``InsertOneResult`` has all NOT REQUIRED parameters and is therefore also NOT REQUIRED allowing a driver to use "void" as the return value for the ``insertOne`` method. - -.. code:: typescript - - class BulkWriteResult { - - /** - * Indicates whether this write result was acknowledged. If not, then all - * other members of this result will be undefined. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - acknowledged: Boolean; - - /** - * Number of documents inserted. - */ - insertedCount: Int64; - - /** - * Map of the index of the operation to the id of the inserted document. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - insertedIds: Map; - - /** - * Number of documents matched for update. - */ - matchedCount: Int64; - - /** - * Number of documents modified. - */ - modifiedCount: Int64; - - /** - * Number of documents deleted. - */ - deletedCount: Int64; - - /** - * Number of documents upserted. - */ - upsertedCount: Int64; - - /** - * Map of the index of the operation to the id of the upserted document. - */ - upsertedIds: Map; - - } - - class InsertOneResult { - - /** - * Indicates whether this write result was acknowledged. If not, then all - * other members of this result will be undefined. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - acknowledged: Boolean; - - /** - * The identifier that was inserted. If the server generated the identifier, this value - * will be null as the driver does not have access to that data. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - insertedId: any; - - } - - class InsertManyResult { - - /** - * Indicates whether this write result was acknowledged. If not, then all - * other members of this result will be undefined. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - acknowledged: Boolean; - - /** - * Map of the index of the inserted document to the id of the inserted document. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - insertedIds: Map; - - } - - class DeleteResult { - - /** - * Indicates whether this write result was acknowledged. If not, then all - * other members of this result will be undefined. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - acknowledged: Boolean; - - /** - * The number of documents that were deleted. - */ - deletedCount: Int64; - - } - - class UpdateResult { - - /** - * Indicates whether this write result was acknowledged. If not, then all - * other members of this result will be undefined. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - acknowledged: Boolean; - - /** - * The number of documents that matched the filter. - */ - matchedCount: Int64; - - /** - * The number of documents that were modified. - */ - modifiedCount: Int64; - - /** - * The number of documents that were upserted. - * - * NOT REQUIRED: Drivers may choose to not provide this property so long as - * it is always possible to infer whether an upsert has taken place. Since - * the "_id" of an upserted document could be null, a null "upsertedId" may - * be ambiguous in some drivers. If so, this field can be used to indicate - * whether an upsert has taken place. - */ - upsertedCount: Int64; - - /** - * The identifier of the inserted document if an upsert took place. - */ - upsertedId: any; - - } - - -Error Handling -~~~~~~~~~~~~~~ - -Below are defined the exceptions that should be thrown from the various write methods. Since exceptions across languages would be impossible to reconcile, the below definitions represent the fields and names for the information that should be present. Structure isn't important as long as the information is available. - -.. note:: - The actual implementation of correlating, merging, and interpreting write errors from the server is not defined here. This spec is solely about the API for users. - -.. code:: typescript - - class WriteConcernError { - - /** - * An integer value identifying the write concern error. - * - * @see https://docs.mongodb.com/manual/reference/method/WriteResult/ - */ - code: Int32; - - /** - * A document identifying the write concern setting related to the error. - * - * @see https://docs.mongodb.com/manual/reference/method/WriteResult/ - */ - details: Document; - - /** - * A description of the error. - * - * @see https://docs.mongodb.com/manual/reference/method/WriteResult/ - */ - message: String; - - } - - class WriteError { - - /** - * An integer value identifying the error. - * - * @see https://docs.mongodb.com/manual/reference/method/WriteResult/ - */ - code: Int32; - - /** - * A description of the error. - * - * @see https://docs.mongodb.com/manual/reference/method/WriteResult/ - */ - message: String; - - } - - class BulkWriteError : WriteError { - - /** - * The index of the request that errored. - */ - index: Int32; - - /** - * The request that errored. - */ - request: Optional; - - } - - /** - * NOTE: Only one of writeConcernError or writeError will be populated at a time. Your driver must present the offending - * error to the user. - */ - class WriteException { - - /** - * The error that occurred on account of write concern failure. - */ - writeConcernError: Optional; - - /** - * The error that occurred on account of a non-write concern failure. - */ - writeError: Optional; - - } - - class BulkWriteException { - - /** - * The requests that were sent to the server. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - processedRequests: Optional>; - - /** - * The requests that were not sent to the server. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - unprocessedRequests: Optional>; - - /** - * The intermediary write result for any operations that succeeded before - * the bulk write was interrupted. - * - * NOT REQUIRED: Drivers may choose to not provide this property. - */ - writeResult: Optional; - - /** - * The error that occured on account of write concern failure. If the error was a Write Concern related, this field must be present. - */ - writeConcernError: Optional; - - /** - * The error that occured on account of a non-write concern failure. This might be empty if the error was a Write Concern related error. - */ - writeErrors: Optional>; - - } - - -~~~~~~~~~~~~~~~ -Find And Modify -~~~~~~~~~~~~~~~ - -.. code:: typescript - - interface Collection { - - /** - * Finds a single document and deletes it, returning the original. The document to return may be null. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - * @throws WriteException - */ - findOneAndDelete(filter: Document, options: Optional): Document; - - /** - * Finds a single document and replaces it, returning either the original or the replaced - * document. The document to return may be null. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - * @throws WriteException - */ - findOneAndReplace(filter: Document, replacement: Document, options: Optional): Document; - - /** - * Finds a single document and updates it, returning either the original or the updated - * document. The document to return may be null. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - * @throws WriteException - */ - findOneAndUpdate(filter: Document, update: Document, options: Optional): Document; - - } - - enum ReturnDocument { - /** - * Indicates to return the document before the update, replacement, or insert occured. - */ - BEFORE, - /** - * Indicates to return the document after the update, replacement, or insert occured. - */ - AFTER - } - - class FindOneAndDeleteOptions { - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - collation: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - maxTimeMS: Optional; - - /** - * Limits the fields to return for all matching documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * Note: this option is mapped to the "fields" findAndModify command option. - * - * @see https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results - */ - projection: Optional; - - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - sort: Optional; - } - - class FindOneAndReplaceOptions { - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - */ - bypassDocumentValidation: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - collation: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - maxTimeMS: Optional; - - /** - * Limits the fields to return for all matching documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * Note: this option is mapped to the "fields" findAndModify command option. - * - * @see https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results - */ - projection: Optional; - - /** - * When ReturnDocument.After, returns the replaced or inserted document rather than the original. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * Note: this option is mapped to the "new" findAndModify boolean field. ReturnDocument.Before represents false, - * and ReturnDocument.After represents true. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - returnDocument: Optional; - - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - sort: Optional; - - /** - * When true, findAndModify creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - upsert: Optional; - } - - class FindOneAndUpdateOptions { - - /** - * A set of filters specifying to which array elements an update should apply. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.6, the driver MUST raise an error if the caller explicitly provides a value. - * For unacknowledged writes using opcodes, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/update/ - */ - arrayFilters: Optional>; - - /** - * If true, allows the write to opt-out of document level validation. - * - * This option is sent only if the caller explicitly provides a true value. The default is to not send a value. - * For servers < 3.2, this option is ignored and not sent as document validation is not available. - */ - bypassDocumentValidation: Optional; - - /** - * Specifies a collation. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * For servers < 3.4, the driver MUST raise an error if the caller explicitly provides a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - collation: Optional; - - /** - * The maximum amount of time to allow the query to run. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - maxTimeMS: Optional; - - /** - * Limits the fields to return for all matching documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * Note: this option is mapped to the "fields" findAndModify command option. - * - * @see https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results - */ - projection: Optional; - - /** - * When ReturnDocument.After, returns the replaced or inserted document rather than the original. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * Note: this option is mapped to the "new" findAndModify boolean field. ReturnDocument.Before represents false, - * and ReturnDocument.After represents true. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - returnDocument: Optional; - - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - sort: Optional; - - /** - * When true, creates a new document if no document matches the query. - * - * This option is sent only if the caller explicitly provides a value. The default is to not send a value. - * - * @see https://docs.mongodb.com/manual/reference/command/findAndModify/ - */ - upsert: Optional; - } - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Update vs. Replace Validation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``update`` family of operations require that the update document parameter MUST have only atomic modifiers. In practice, this means that introspection needs to happen on that document to enforce this. However, it is enough to only check the first element in the document. If it begins with a ``$`` sign and the rest of the document's elements do not, the server will throw an error. Note that it is required that an update document have at least one atomic modifier. - -The ``replace`` family of operations require that the replacement document parameter MUST NOT begin with an atomic modifier. In practice, this means that introspection needs to happen on that document to enforce this. However, it is enough to only check the first element in the document. If it does not begin with a ``$`` sign but an element later on does, the server will throw an error. - - -Test Plan -====================================== - -See the `README `_ for tests. - -In addition, we have constructed some example usages in different languages that show how different implementations are able to conform to the specification and still look and feel idiomatic to a user. - -* `C++ `_ -* `Javascript `_ -* `Java `_ -* `Node `_ -* `PHP `_ - - -Motivation -========== - -Current drivers have chosen slightly different names and semantics for the same operations and options. In addition, not all drivers offer all the same operations and methods. As such, it is difficult to transition from driver to driver making the jobs of polyglot developers, documentation authors, and support engineers more difficult. - - -Backwards Compatibility -======================= - -This spec should be mostly backwards compatible as it is very lenient. Drivers finding a backwards compatibility problem should attempt to work around it using an acceptable deviation. In rare cases, a driver may need to break backwards compatibility. This should be done in accordance with a versioning scheme indicating that a backwards compatible break may have occured in conjunction with release documentation and warnings. - - -Reference Implementation -======================== - -See Test Plan - - -Related Terminology -=================== - -If a driver needs to refer to items in the following list, the below are the accepted forms of those terms and deviations from the Naming section are still permissible. - -* Read Preference: readPreference -* Read Concern: readConcern -* Write Concern: writeConcern - - -Q & A -===== - -Q: Why do the names of the fields differ from those defined in the MongoDB manual? - Documentation and commands often refer to same-purposed fields with different names making it difficult to have a cohesive API. In addition, occasionally the name was correct at one point and its purpose has expanded to a point where the initial name doesn't accurately describe its current function. - - In addition, responses from the servers are sometimes cryptic and used for the purposes of compactness. In these cases, we felt the more verbose form was desirable for self-documentation purposes. - -Q: Where is read preference? - Read preference is about selecting a server with which to perform a read operation, such as a query, a count, or an aggregate. Since all operations defined in this specification are performed on a collection, it's uncommon that two different read operations on the same collection would use a different read preference, potentially getting out-of-sync results. As such, the most natural place to indicate read preference is on the client, the database, or the collection itself and not the operations within it. - - However, it might be that a driver needs to expose this selection filter to a user per operation for various reasons. As noted before, it is permitted to specify this, along with other driver-specific options, in some alternative way. - -Q: Where is read concern? - Read concern is about indicating how reads are handled. Since all operations defined in this specification are performed on a collection, it's uncommon that two different read operations on the same collection would use a different read concern, potentially causing mismatched and out-of-sync data. As such, the most natural place to indicate read concern is on the client, the database, or the collection itself and not the operations within it. - - However, it might be that a driver needs to expose read concern to a user per operation for various reasons. As noted before, it is permitted to specify this, along with other driver-specific options, in some alternative way. - -Q: Where is write concern? - Write concern is about indicating how writes are acknowledged. Since all operations defined in this specification are performed on a collection, it's uncommon that two different write operations on the same collection would use a different write concern, potentially causing mismatched and out-of-sync data. As such, the most natural place to indicate write concern is on the client, the database, or the collection itself and not the operations within it. - - However, it might be that a driver needs to expose write concern to a user per operation for various reasons. As noted before, it is permitted to specify this, along with other driver-specific options, in some alternative way. - -Q: How do I throttle unacknowledged writes now that write concern is no longer defined on a per operation basis? - Some users used to throttle unacknowledged writes by using an acknowledged write concern every X number of operations. Going forward, the proper way to handle this is by using the bulk write API. - -Q: What is the logic for adding "One" or "Many" into the method and model names? - If the maximum number of documents affected can only be one, we added "One" into the name. This makes it explicit that the maximum number of documents that could be affected is one vs. infinite. - - In addition, the current API exposed by all our drivers has the default value for "one" or "many" set differently for update and delete. This generally causes some issues for new developers and is a minor annoyance for existing developers. The safest way to combat this without introducing discrepencies between drivers/driver versions or breaking backwards compatibility was to use multiple methods, each signifying the number of documents that could be affected. - -Q: Speaking of "One", where is ``findOne``? - If your driver wishes to offer a ``findOne`` method, that is perfectly fine. If you choose to implement ``findOne``, please keep to the naming conventions followed by the ``FindOptions`` and keep in mind that certain things don't make sense like limit (which should be -1), tailable, awaitData, etc... - -Q: What considerations have been taken for the eventual merging of query and the aggregation framework? - In the future, it is probable that a new query engine (QE) will look very much like the aggregation framework. Given this assumption, we know that both ``find`` and ``aggregate`` will be renderable in QE, each maintaining their ordering guarantees for full backwards compatibility. - - Hence, the only real concern is how to initiate a query using QE. While ``find`` is preferable, it would be a backwards breaking change. It might be decided that ``find`` is what should be used, and all drivers will release major revisions with this backwards breaking change. Alternatively, it might be decided that another initiator would be used. - -Q: Didn't we just build a bulk API? - Yes, most drivers did just build out a bulk API (fluent-bulk-api). While unfortunate, we felt it better to have the bulk api be consistent with the rest of the methods in the CRUD family of operations. However, the fluent-bulk-api is still able to be used as this change is non-backwards breaking. Any driver which implemented the fluent bulk API should deprecate it and drivers that have not built it should not do so. - -Q: What about explain? - Explain has been determined to be not a normal use-case for a driver. We'd like users to use the shell for this purpose. However, explain is still possible from a driver. For find, it can be passed as a modifier. Aggregate can be run using a runCommand method passing the explain option. In addition, server 3.0 offers an explain command that can be run using a runCommand method. - -Q: Where did modifiers go in FindOptions? - MongoDB 3.2 introduced the find command. As opposed to using the general "modifiers" field any longer, each relevant option is listed explicitly. Some options, such as "tailable" or "singleBatch" are not listed as they are derived from other fields. Upgrading a driver should be a simple procedure of deprecating the "modifiers" field and introducing the new fields. When a collision occurs, the explicitly specified field should override the value in "modifiers". - -Q: Where is ``save``? - Drivers have historically provided a ``save`` method, which was syntactic sugar for upserting or inserting a document based on whether it contained an identifier, respectively. While the ``save`` method may be convenient for interactive environments, such as the shell, it was intentionally excluded from the CRUD specification for language drivers for several reasons. The ``save`` method promotes a design pattern of "fetch, modify, replace" and invites race conditions in application logic. Additionally, the split nature of ``save`` makes it difficult to discern at a glance if application code will perform an insert or potentially dangerous full-document replacement. Instead of relying on ``save``, application code should know whether document already has an identifier and explicitly call ``insertOne`` or ``replaceOne`` with the ``upsert`` option. - -Q: Where is ``useCursor`` in AggregateOptions? - Inline aggregation results are no longer supported in server 3.5.2+. The `aggregate command `_ must be provided either the ``cursor`` document or the ``explain`` boolean. AggregateOptions does not define an ``explain`` option. If a driver does support an ``explain`` option, the ``cursor`` document should be omitted if ``explain`` is ``true``. Otherwise a ``cursor`` document must be added to the ``aggregate`` command. Regardless, ``useCursor`` is no longer needed. Removing ``useCursor`` is a backwards breaking change, so drivers should first deprecate this option in a minor release, and remove it in a major release. - -Q: Where is ``singleBatch`` in FindOptions? - Drivers have historically allowed users to request a single batch of results (after which the cursor is closed) by specifying a negative value for the ``limit`` option. For servers < 3.2, a single batch may be requested by specifying a negative value in the ``numberToReturn`` wire protocol field. For servers >= 3.2, the ``find`` command defines ``limit`` as a non-negative integer option but introduces a ``singleBatch`` boolean option. Rather than introduce a ``singleBatch`` option to FindOptions, the spec preserves the existing API for ``limit`` and instructs drivers to convert negative values accordingly for servers >= 3.2. - -Changes -======= - -* 2018-07-25: Added upsertedCount to UpdateResult. -* 2018-06-07: Deprecated the count helper. Added the estimatedDocumentCount and countDocuments helpers. -* 2018-03-05: Deprecate snapshot option -* 2018-03-01: Deprecate maxScan query option. -* 2018-02-06: Note that batchSize in FindOptions and AggregateOptions should also apply to getMore. -* 2018-01-26: Only send bypassDocumentValidation option if it's true, don't send false. -* 2017-10-23: Allow BulkWriteException to provide an intermediary write result. -* 2017-10-17: Document negative limit for FindOptions. -* 2017-10-09: Bumped minimum server version to 2.6 and removed references to older versions in spec and tests. -* 2017-10-09: Prohibit empty insertMany() and bulkWrite() operations. -* 2017-10-09: Split UpdateOptions and ReplaceOptions. Since replaceOne() previously used UpdateOptions, this may have BC implications for drivers using option classes. -* 2017-10-05: Removed useCursor option from AggregateOptions. -* 2017-09-26: Added hint option to AggregateOptions. -* 2017-09-25: Added comment option to AggregateOptions. -* 2017-08-31: Added arrayFilters to bulk write update models. -* 2017-06-29: Remove requirement of using OP_KILL_CURSOR to kill cursors. -* 2017-06-27: Added arrayFilters to UpdateOptions and FindOneAndUpdateOptions. -* 2017-06-26: Added FAQ entry for omission of save method. -* 2017-05-12: Removed extra "collation" option added to several bulk write models. -* 2017-01-09: Removed modifiers from FindOptions and added in all options. -* 2017-01-09: Changed the value type of FindOptions.skip and FindOptions.limit to Int64 with a note related to calculating batchSize for opcode writes. -* 2017-01-09: Reworded description of how default values are handled and when to send certain options. -* 2016-09-23: Included collation option in the bulk write models. -* 2016-08-05: Added in collation option. -* 2015-11-05: Typos in comments about bypassDocumentValidation -* 2015-10-16: Added maxAwaitTimeMS to FindOptions. -* 2015-10-01: Moved bypassDocumentValidation into BulkWriteOptions and removed it from the individual write models. -* 2015-09-16: Added bypassDocumentValidation. -* 2015-09-16: Added readConcern notes. -* 2015-06-17: Added limit/batchSize calculation logic. + This specification has been converted to Markdown and renamed to + `crud.md `_. diff --git a/source/crud/examples/cpp/usage_example.cpp b/source/crud/examples/cpp/usage_example.cpp deleted file mode 100644 index c2327dfeec..0000000000 --- a/source/crud/examples/cpp/usage_example.cpp +++ /dev/null @@ -1,104 +0,0 @@ -///////////////////////////////////////////////////////////////////// -/// find -///////////////////////////////////////////////////////////////////// - - // basic find - auto cursor = coll.find(doc_view); - - // find with filter and limit - options::find opts(); - opts.limit(10); - auto cursor = coll.find(doc_view, opts); - -///////////////////////////////////////////////////////////////////// -/// aggregate -///////////////////////////////////////////////////////////////////// - - // create the pipeline - pipeline pl; - pl.match(doc_view); - pl.skip(10); - - // perform the aggregation - auto cursor = coll.aggregate(pl); - -///////////////////////////////////////////////////////////////////// -/// count -///////////////////////////////////////////////////////////////////// - - // count documents matching doc_view; - auto count = coll.count(doc_view); - -///////////////////////////////////////////////////////////////////// -/// distinct -///////////////////////////////////////////////////////////////////// - - // get distinct bson values for key "x" - auto distinct_x = coll.distinct("x"); - -///////////////////////////////////////////////////////////////////// -/// delete -///////////////////////////////////////////////////////////////////// - - // delete one document matching view - auto result = coll.delete_one(doc_view); - - // delete all documents matching view - auto result = coll.delete_many(doc_view); - -///////////////////////////////////////////////////////////////////// -/// insert -///////////////////////////////////////////////////////////////////// - - // insert a single document - auto result = coll.insert_one(doc_view); - - // insert a bunch of documents - std::vector docs{doc1, doc2, doc3}; - auto result = coll.insert_many(docs); - -///////////////////////////////////////////////////////////////////// -/// replaceOne -///////////////////////////////////////////////////////////////////// - - auto result = coll.replace_one(filter, replacement); - -///////////////////////////////////////////////////////////////////// -/// update -///////////////////////////////////////////////////////////////////// - - // update a single document matching filter - auto result = coll.update_one(filter, update); - auto result = coll.update_many(filter, update); - -///////////////////////////////////////////////////////////////////// -/// bulkWrite -///////////////////////////////////////////////////////////////////// - - // build up array of model::writes - std::vector writes; - writes.push_back(model::insert_one{doc_view}); - writes.push_back(model::insert_one{doc_view}); - writes.push_back(model::delete_one{doc_view}); - //... - - // perform the bulk write -- ordered default - auto result = coll.bulk_write(writes); - -///////////////////////////////////////////////////////////////////// -/// findOneAndDelete -///////////////////////////////////////////////////////////////////// - - auto doc = coll.find_one_and_delete(filter); - -///////////////////////////////////////////////////////////////////// -/// findOneAndReplace -///////////////////////////////////////////////////////////////////// - - auto doc = coll.find_one_and_replace(filter, replacement); - -///////////////////////////////////////////////////////////////////// -/// findOneAndUpdate -///////////////////////////////////////////////////////////////////// - - auto doc = coll.find_one_and_update(filter, update); diff --git a/source/crud/examples/java/build.gradle b/source/crud/examples/java/build.gradle deleted file mode 100644 index 55be737c85..0000000000 --- a/source/crud/examples/java/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'java' - -repositories { - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } - mavenCentral() -} - -dependencies { - compile(group:"org.mongodb", name: "mongo-java-driver", version : "3.0.0-SNAPSHOT", changing : true) - runtime(group:"ch.qos.logback", name: "logback-classic", version : "1.1.2") -} - -task wrapper(type: Wrapper) { - gradleVersion = '2.1' -} - diff --git a/source/crud/examples/java/gradle/wrapper/gradle-wrapper.jar b/source/crud/examples/java/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 42d9b0e9c5..0000000000 Binary files a/source/crud/examples/java/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/source/crud/examples/java/gradle/wrapper/gradle-wrapper.properties b/source/crud/examples/java/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e0006926ea..0000000000 --- a/source/crud/examples/java/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Sep 26 13:57:42 EDT 2014 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/source/crud/examples/java/gradlew b/source/crud/examples/java/gradlew deleted file mode 100644 index 91a7e269e1..0000000000 --- a/source/crud/examples/java/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/source/crud/examples/java/src/main/java/examples/MongoCollectionUsageExample.java b/source/crud/examples/java/src/main/java/examples/MongoCollectionUsageExample.java deleted file mode 100644 index 56881f37ab..0000000000 --- a/source/crud/examples/java/src/main/java/examples/MongoCollectionUsageExample.java +++ /dev/null @@ -1,198 +0,0 @@ -package examples; - -import com.mongodb.ExplainVerbosity; -import com.mongodb.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.model.AggregateOptions; -import com.mongodb.client.model.BulkWriteOptions; -import com.mongodb.client.model.CountModel; -import com.mongodb.client.model.CountOptions; -import com.mongodb.client.model.DeleteOneModel; -import com.mongodb.client.model.DistinctOptions; -import com.mongodb.client.model.FindModel; -import com.mongodb.client.model.FindOneAndDeleteOptions; -import com.mongodb.client.model.FindOneAndReplaceOptions; -import com.mongodb.client.model.FindOneAndUpdateOptions; -import com.mongodb.client.model.FindOptions; -import com.mongodb.client.model.InsertManyOptions; -import com.mongodb.client.model.InsertOneModel; -import com.mongodb.client.model.UpdateManyModel; -import com.mongodb.client.model.UpdateOptions; -import org.mongodb.Document; - -import static java.util.Arrays.asList; -import static java.util.concurrent.TimeUnit.SECONDS; - -public class MongoCollectionUsageExample { - - public static void usageExample(MongoCollection col) { - - // Java's argument passing is very rudimentary compared to most other - // languages. No named parameters, no optional parameters, etc. - // To make it as easy as possible for users to do the simple things simply - // and the slightly more complex thing not too much more complex, - // and still abiding by the spec rules for no positional optional parameters - // the Java driver provides two overloads for each method: - // - // 1. An overload with positional parameters for all required model fields - // 2. An overload with the same positional parameters for all required model - // fields, plus an options parameter whose type defines a fluent API for - // specifying each optional parameter - - - // find - - // find with no criteria, since criteria is optional - col.find(); - - // find with criteria, specified in options since...criteria is optional - col.find(new FindOptions().criteria(new Document("x", 1))); - - // adding limit is easy once you have options - col.find(new FindOptions().criteria(new Document("x", 1)) - .limit(10)); - - // as are query flags - col.find(new FindOptions().tailable(true) - .awaitData(true)); - - - // aggregation - - // aggregate is just a list of documents - col.aggregate(asList(new Document("$match", new Document("x", 1)), - new Document("$skip", 10))); - - // and again with a few (non-positional) options. - col.aggregate(asList(new Document("$match", new Document("x", 1)), - new Document("$skip", 10)), - new AggregateOptions().allowDiskUse(true) - .maxTime(1, SECONDS)); - - - // count - - // count with all required parameters: there are none - col.count(); - - // count with some optional parameters - col.count(new CountOptions().criteria(new Document("x", 1)) - .maxTime(1, SECONDS)); - - - // distinct - - col.distinct("y"); - - col.distinct("y", new DistinctOptions().criteria(new Document("x", 1))); - - - // delete (no optional parameters for delete, so no options - - col.deleteOne(new Document("x", 1)); - - col.deleteMany(new Document("x", 1)); - - - // insert - - // insert a single document - - col.insertOne(new Document("x", 1)); - - // insert many documents (well, at least one) - col.insertMany(asList(new Document("x", 1), new Document("x", 2))); - - // insert many documents where order does not matter. Note that even though there - // is currently only one option to insertMany, it's still not specified positionally - // this leaves room for adding more options down the line without having to add - // more overloads - col.insertMany(asList(new Document("x", 1), new Document("x", 2)), - new InsertManyOptions().ordered(false)); - - - // replaceOne - - col.replaceOne(new Document("x", 1), new Document("x", 2)); - - col.replaceOne(new Document("x", 1), new Document("x", 2), - new UpdateOptions().upsert(true)); - - - // update - - col.updateOne(new Document("x", 1), new Document("$set", new Document("x", 2))); - - col.updateOne(new Document("x", 1), new Document("$set", new Document("x", 2)), - new UpdateOptions().upsert(true)); - - col.updateMany(new Document("x", 1), new Document("$set", new Document("x", 2))); - - col.updateMany(new Document("x", 1), new Document("$set", new Document("x", 2)), - new UpdateOptions().upsert(true)); - - - // bulkWrite - - // an ordered bulk write (the default) - col.bulkWrite(asList(new InsertOneModel<>(new Document("x", 1)), - new UpdateManyModel<>(new Document("x", 1), - new Document("$set", new Document("x", 2))), - new DeleteOneModel<>(new Document("x", 1)))); - - // an unordered bulk write (specified as an option) - col.bulkWrite(asList(new InsertOneModel<>(new Document("x", 1)), - new UpdateManyModel<>(new Document("x", 1), - new Document("$set", new Document("x", 2))), - new DeleteOneModel<>(new Document("x", 1))), - new BulkWriteOptions().ordered(false)); - - - // findOneAndDelete - - // just the query - col.findOneAndDelete(new Document("x", 1)); - - // query plus optional projection - col.findOneAndDelete(new Document("x", 1), - new FindOneAndDeleteOptions().projection(new Document("y", 1))); - - - // findOneAndReplace - - // query and replacement - col.findOneAndReplace(new Document("x", 1), new Document("_id", 3).append("x", 2)); - - // query and replacement and optional returnReplaced (note we are changing this to returnOriginal) - col.findOneAndReplace(new Document("x", 1), new Document("_id", 3).append("x", 2), - new FindOneAndReplaceOptions().returnDocument(ReturnDocument.after)); - - - // findOneAndUpdate - - // query and update - col.findOneAndUpdate(new Document("x", 1), new Document("$set", new Document("x", 2))); - - // query and update and optional returnUpdated (note we are changing this to returnOriginal) - col.findOneAndUpdate(new Document("x", 1), new Document("$set", new Document("x", 2)), - new FindOneAndUpdateOptions().returnDocument(ReturnDocument.after)); - - - // explain. note that because there is just a single explain method, we can't use the - // "required positional parameters plus options" strategy of above. Instead we have to - // resort to Model classes that encapsulate the required and optional parameters - - // explain a count. note the use of CountModel - col.explain(new CountModel(new CountOptions().criteria(new Document("x", 1))), - ExplainVerbosity.QUERY_PLANNER); - - // explain a find. note the use of FindModel - col.explain(new FindModel(new FindOptions().criteria(new Document("x", 1))), - ExplainVerbosity.ALL_PLANS_EXECUTIONS); - } - - public static void main(String[] args) { - MongoClient client = new MongoClient(); - usageExample(client.getDatabase("test").getCollection("test")); - } -} diff --git a/source/crud/examples/javascript/usage_example.js b/source/crud/examples/javascript/usage_example.js deleted file mode 100644 index ae19c69778..0000000000 --- a/source/crud/examples/javascript/usage_example.js +++ /dev/null @@ -1,83 +0,0 @@ - - -// find - - col.find({x:1}); - - col.find({x:1}).limit(10); - - col.find({x:1}, {limit: 10}); - - -// aggregate - - col.aggregate([ - {$match : {x: 1}}, - {$skip: 10} - ]); - - col.aggregate() - .match({x:1}) - .skip(20); - - -// count - - col.count({x: 1}); - - col.count({x: 1}, {maxTimeMS: 2000}); - - -// distinct - - col.distinct("y", {x: 1}); - -// delete - - col.deleteOne({x: 1}); - - col.deleteMany({x: 1}); - -// insert - - col.insertOne({x: 1}); - - col.insertMany([{x: 1}, {x: 2}], {continueOnError: true}); - - -// replaceOne - - col.replaceOne({x: 1}, {x: 2}, {upsert: true}); - - -// update - - col.updateOne({x: 1}, {$set: {x: 2}}, {upsert: true}); - - col.updateMany({x: 1}, {$set: {x: 2}}, {upsert: true}); - - -// bulkWrite - - col.bulkWrite([ - { insertOne: {document: {x: 1}} }, - { updateMany: {filter: {x: 1}, update: {$set: {x: 2}} }, - { deleteOne: {filter: {x: 1}} } - ], - { ordered: false } - ); - - -// findOneAndDelete - - col.findOneAndDelete({x: 1}, {projection: {y: 1, _id: 0}}); - - -// findOneAndReplace - - col.findOneAndReplace({x: 1}, {x: 2}, {returnDocument: ReturnDocument.after}); - - -// findOneAndUpdate - - col.findOneAndUpdate({x: 1}, {$set: {x: 2}}, {returnDocument: ReturnDocument.after}); \ No newline at end of file diff --git a/source/crud/examples/node/usage_example.js b/source/crud/examples/node/usage_example.js deleted file mode 100644 index 0aeebbfc55..0000000000 --- a/source/crud/examples/node/usage_example.js +++ /dev/null @@ -1,100 +0,0 @@ -// Idiomatic Javascript is to make required arguments parameters and the rest be options in a dictionary. -// So if function A requires the arguments B and C but has options D and E it would be like this. -// -// A(B, C, {D:1, E:0}, function(err, r) {}) - -// find - - col.find({x:1}) - .limit(10) - .toArray(function(err, docs) { - }); - - col.find() - .skip(20) - .awaitData(true) - .tailable(true) - .toArray(function(err, docs) { - }); - -// aggregate - - col.aggregate([ - { $match : { x: 1 } }, - { $skip: 10 } - ]).toArray(function(err, docs) { - }); - -// count - - col.count({ x: 1 }, {maxTimeMS:2000}, function(err, count) { - }); - - -// distinct - - col.distinct("y", {criteria: { x: 1 }}, function(err, r) {}); - -// delete - - col.deleteOne({ x: 1 }, function(err, r) {}); - - col.deleteOne({ x: 1 }, function(err, r) {}); - - col.deleteMany({ x: 1 }, function(err, r) {}); - - -// insert - - col.insertOne({ x: 1 }, function(err, r) {}); - - col.insertMany([ - { x: 1 }, - { x: 2 }, - ], { ordered : true }, function(err, r) {}); - - -// replaceOne - - col.replaceOne({ x: 1 }, { x: 2 }, {upsert: true}, function(err, r) {}); - -// update - - col.updateOne({ x: 1 }, { $set: { x: 2 } }, { upsert: true}, function(err, r) {}); - -// bulkWrite - - col.bulkWrite([ - { - insertOne: { x: 1 } - }, - { - updateMany: { - criteria: { x: 1 }, - update: { $set: { x: 2 } } - } - }, - { - deleteOne: { - criteria: { x: 1 } - } - }], { ordered: false }, function(err, r) {}); - -// findOneAndDelete - - col.findOneAndDelete({ x: 1 }, { - projection: { y: 1, _id: 0 }, - }, function(err, r) {}); - - -// findOneAndReplace - - col.findOneAndReplace({ x: 1 }, { x: 2 }, { - returnDocument: ReturnDocument.after - }, function(err, r) {}); - -// findOneAndUpdate - - col.findOneAndUpdate({ x: 1 }, { $set: { x: 2 } }, { - returnDocument: ReturnDocument.after - }, function(err, r) {}); diff --git a/source/crud/examples/php/usage_example.php b/source/crud/examples/php/usage_example.php deleted file mode 100644 index da0a1b7e1f..0000000000 --- a/source/crud/examples/php/usage_example.php +++ /dev/null @@ -1,240 +0,0 @@ -find( - // Optional criteria param, which we allow as the first positional argument for BC - ['x' => 1], - // Optional named params in an associative array (BC break, projection in 1.x) - ['limit' => 10] -); - -$collection->find( - // Optional criteria param, which we allow as the first positional argument for BC - [], - // Optional named params in an associative array (BC break, projection in 1.x) - [ - 'skip' => 20, - 'tailable' => true, - 'awaitData' => true, - ] -); - - -// aggregate - -$collection->aggregate( - // Required pipeline param - [ - ['$match' => ['x' => 1]], - ['$skip' => 10], - ], - // Optional named params in an associative array - ['allowDiskUse' => true] -); - - -// count - -$collection->count( - // Optional criteria param, which we allow as the first positional argument for BC - ['x' => 1], - // Optional named params in an associative array - ['maxTimeMS' => 2000] -); - -$collection->count( - // Optional criteria param, which we allow as the first positional argument for BC - [], - // Optional named params in an associative array - ['maxTimeMS' => 2000] -); - - -// distinct - -$collection->distinct( - // Required field param - 'y', - // Optional named params in an associative array (BC break, criteria in 1.x) - [ - 'criteria' => ['x' => 1], - 'maxTimeMS' => 2000, - ] -); - - -// delete - -$collection->deleteOne([]); // First position argument is required criteria -$collection->deleteOne(['x' => 1]); -$collection->deleteMany(['x' => 1]); - - -// insert - -$collection->insertOne(['x' => 1]); - -$collection->insertMany( - // Required documents param (array of documents) - [ - ['x' => 1], - ['x' => 2], - ], - // Optional named params in an associative array - ['ordered' => true] -); - -$collection->insertOne( - // Required document param - ['x' => 1], - // Optional named params in an associative array (allow mixing of write concern options?) - ['w' => 'majority'] -); - -$collection->insertMany( - // Required documents param (array of documents) - [ - ['x' => 1], - ['x' => 2], - ], - // Optional named params in an associative array (allow mixing of write concern options?) - [ - 'ordered' => true, - 'w' => 'majority', - ] -); - - -// replaceOne - -$collection->replaceOne( - ['x' => 1], - ['x' => 2], - ['upsert' => true] -); - - -// update - -$collection->updateOne( - ['x' => 1], - ['$set' => ['x' => 2]], - ['upsert' => true] -); - -$collection->updateMany( - ['x' => 1], - ['$set' => ['x' => 2]], - ['upsert' => true] -); - - -// bulkWrite - -$collection->bulkWrite( - // Required writes param (an array of operations) - [ - // Like explain(), operations identified by single key - [ - 'insertOne' => [ - ['x' => 1] - ], - ], - [ - 'updateMany' => [ - ['x' => 1], - ['$set' => ['x' => 2]], - ], - ], - [ - 'updateOne' => [ - ['x' => 3], - ['$set' => ['x' => 4]], - // Optional params are still permitted - ['upsert' => true], - ], - ], - [ - 'deleteOne' => [ - ['x' => 1], - ], - ], - [ - 'deleteMany' => [ - // Required arguments must still be specified - [], - ], - ], - ], - // Optional named params in an associative array - ['ordered' => false] -); - - -// findOneAndDelete - -$collection->findOneAndDelete( - // Required criteria param - ['x' => 1], - // Optional named params in an associative array - [ - 'projection' => ['y' => 1, '_id' => 0], - ] -); - - -// findOneAndReplace - -$collection->findOneAndReplace( - // Required criteria param - ['x' => 1], - // Required replacement param - ['x' => 2], - // Optional named params in an associative array - ['returnDocument' => ReturnDocument::Before] -); - - -// findOneAndUpdate - - -$collection->findOneAndUpdate( - // Required criteria param - ['x' => 1], - // Required update param - ['$set' => ['x' => 2]], - // Optional named params in an associative array - ['returnDocument' => ReturnDocument::After] -); - - -// explain - -$collection->explain( - // Required command param - [ - // Single key identifies the command - 'count' => [ - // Optional criteria param (in first position for consistency with method) - ['x' => 1], - // Optional named params in an associative array - ['maxTimeMS' => 2000], - ], - ] -); - -$collection->explain( - // Required command param - [ - // Single key identifies the command - 'updateOne' => [ - // Required criteria param - ['x' => 1], - // Required update param - ['$inc' => ['x' => 2]], - // Optional named params in an associative array - ['upsert' => false], - ], - ] -); diff --git a/source/crud/tests/README.md b/source/crud/tests/README.md new file mode 100644 index 0000000000..465fff7db6 --- /dev/null +++ b/source/crud/tests/README.md @@ -0,0 +1,680 @@ +# CRUD Tests + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +the CRUD specification. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +## Prose Tests + +### 1. WriteConcernError.details exposes writeConcernError.errInfo + +Test that `writeConcernError.errInfo` in a command response is propagated as `WriteConcernError.details` (or equivalent) +in the driver. + +Using a 4.0+ server, set the following failpoint: + +```javascript +{ + "configureFailPoint": "failCommand", + "data": { + "failCommands": ["insert"], + "writeConcernError": { + "code": 100, + "codeName": "UnsatisfiableWriteConcern", + "errmsg": "Not enough data-bearing nodes", + "errInfo": { + "writeConcern": { + "w": 2, + "wtimeout": 0, + "provenance": "clientSupplied" + } + } + } + }, + "mode": { "times": 1 } +} +``` + +Then, perform an insert operation and assert that a WriteConcernError occurs and that its `details` property is both +accessible and matches the `errInfo` object from the failpoint. + +### 2. WriteError.details exposes writeErrors[].errInfo + +Test that `writeErrors[].errInfo` in a command response is propagated as `WriteError.details` (or equivalent) in the +driver. + +Using a 5.0+ server, create a collection with +[document validation](https://www.mongodb.com/docs/manual/core/schema-validation/) like so: + +```javascript +{ + "create": "test", + "validator": { + "x": { $type: "string" } + } +} +``` + +Enable [command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) to observe +CommandSucceededEvents. Then, insert an invalid document (e.g. `{x: 1}`) and assert that a WriteError occurs, that its +code is `121` (i.e. DocumentValidationFailure), and that its `details` property is accessible. Additionally, assert that +a CommandSucceededEvent was observed and that the `writeErrors[0].errInfo` field in the response document matches the +WriteError's `details` property. + +### 3. `MongoClient.bulkWrite` batch splits a `writeModels` input with greater than `maxWriteBatchSize` operations + +Test that `MongoClient.bulkWrite` properly handles `writeModels` inputs containing a number of writes greater than +`maxWriteBatchSize`. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxWriteBatchSize` value contained in the +response. Then, construct the following write model (referred to as `model`): + +```javascript +InsertOne: { + "namespace": "db.coll", + "document": { "a": "b" } +} +``` + +Construct a list of write models (referred to as `models`) with `model` repeated `maxWriteBatchSize + 1` times. Execute +`bulkWrite` on `client` with `models`. Assert that the bulk write succeeds and returns a `BulkWriteResult` with an +`insertedCount` value of `maxWriteBatchSize + 1`. + +Assert that two CommandStartedEvents (referred to as `firstEvent` and `secondEvent`) were observed for the `bulkWrite` +command. Assert that the length of `firstEvent.command.ops` is `maxWriteBatchSize`. Assert that the length of +`secondEvent.command.ops` is 1. If the driver exposes `operationId`s in its CommandStartedEvents, assert that +`firstEvent.operationId` is equal to `secondEvent.operationId`. + +### 4. `MongoClient.bulkWrite` batch splits when an `ops` payload exceeds `maxMessageSizeBytes` + +Test that `MongoClient.bulkWrite` properly handles a `writeModels` input which constructs an `ops` array larger than +`maxMessageSizeBytes`. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the following values from the response: +`maxBsonObjectSize` and `maxMessageSizeBytes`. Then, construct the following document (referred to as `document`): + +```javascript +{ + "a": "b".repeat(maxBsonObjectSize - 500) +} +``` + +Construct the following write model (referred to as `model`): + +```javascript +InsertOne: { + "namespace": "db.coll", + "document": document +} +``` + +Use the following calculation to determine the number of inserts that should be provided to `MongoClient.bulkWrite`: +`maxMessageSizeBytes / maxBsonObjectSize + 1` (referred to as `numModels`). This number ensures that the inserts +provided to `MongoClient.bulkWrite` will require multiple `bulkWrite` commands to be sent to the server. + +Construct as list of write models (referred to as `models`) with `model` repeated `numModels` times. Then execute +`bulkWrite` on `client` with `models`. Assert that the bulk write succeeds and returns a `BulkWriteResult` with an +`insertedCount` value of `numModels`. + +Assert that two CommandStartedEvents (referred to as `firstEvent` and `secondEvent`) were observed. Assert that the +length of `firstEvent.command.ops` is `numModels - 1`. Assert that the length of `secondEvent.command.ops` is 1. If the +driver exposes `operationId`s in its CommandStartedEvents, assert that `firstEvent.operationId` is equal to +`secondEvent.operationId`. + +### 5. `MongoClient.bulkWrite` collects `WriteConcernError`s across batches + +Test that `MongoClient.bulkWrite` properly collects and reports `writeConcernError`s returned in separate batches. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with `retryWrites: false` configured and +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxWriteBatchSize` value contained in the +response. Then, configure the following fail point with `client`: + +```javascript +{ + "configureFailPoint": "failCommand", + "mode": { "times": 2 }, + "data": { + "failCommands": ["bulkWrite"], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } +} +``` + +Construct the following write model (referred to as `model`): + +```javascript +InsertOne: { + "namespace": "db.coll", + "document": { "a": "b" } +} +``` + +Construct a list of write models (referred to as `models`) with `model` repeated `maxWriteBatchSize + 1` times. Execute +`bulkWrite` on `client` with `models`. Assert that the bulk write fails and returns a `BulkWriteException` (referred to +as `error`). + +Assert that `error.writeConcernErrors` has a length of 2. + +Assert that `error.partialResult` is populated. Assert that `error.partialResult.insertedCount` is equal to +`maxWriteBatchSize + 1`. + +Assert that two CommandStartedEvents were observed for the `bulkWrite` command. + +### 6. `MongoClient.bulkWrite` handles individual `WriteError`s across batches + +Test that `MongoClient.bulkWrite` handles individual write errors across batches for ordered and unordered bulk writes. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxWriteBatchSize` value contained in the +response. + +Construct a `MongoCollection` (referred to as `collection`) with the namespace "db.coll" (referred to as `namespace`). +Drop `collection`. Then, construct the following document (referred to as `document`): + +```javascript +{ + "_id": 1 +} +``` + +Insert `document` into `collection`. + +Create the following write model (referred to as `model`): + +```javascript +InsertOne { + "namespace": namespace, + "document": document +} +``` + +Construct a list of write models (referred to as `models`) with `model` repeated `maxWriteBatchSize + 1` times. + +#### Unordered + +Test that an unordered bulk write collects `WriteError`s across batches. + +Execute `bulkWrite` on `client` with `models` and `ordered` set to false. Assert that the bulk write fails and returns a +`BulkWriteException` (referred to as `unorderedError`). + +Assert that `unorderedError.writeErrors` has a length of `maxWriteBatchSize + 1`. + +Assert that two CommandStartedEvents were observed for the `bulkWrite` command. + +#### Ordered + +Test that an ordered bulk write does not execute further batches when a `WriteError` occurs. + +Execute `bulkWrite` on `client` with `models` and `ordered` set to true. Assert that the bulk write fails and returns a +`BulkWriteException` (referred to as `orderedError`). + +Assert that `orderedError.writeErrors` has a length of 1. + +Assert that one CommandStartedEvent was observed for the `bulkWrite` command. + +### 7. `MongoClient.bulkWrite` handles a cursor requiring a `getMore` + +Test that `MongoClient.bulkWrite` properly iterates the results cursor when `getMore` is required. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxBsonObjectSize` value from the +response. + +Construct a `MongoCollection` (referred to as `collection`) with the namespace "db.coll" (referred to as `namespace`). +Drop `collection`. Then create the following list of write models (referred to as `models`): + +```javascript +UpdateOne { + "namespace": namespace, + "filter": { "_id": "a".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +UpdateOne { + "namespace": namespace, + "filter": { "_id": "b".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +``` + +Execute `bulkWrite` on `client` with `models` and `verboseResults` set to true. Assert that the bulk write succeeds and +returns a `BulkWriteResult` (referred to as `result`). + +Assert that `result.upsertedCount` is equal to 2. + +Assert that the length of `result.updateResults` is equal to 2. + +Assert that a CommandStartedEvent was observed for the `getMore` command. + +### 8. `MongoClient.bulkWrite` handles a cursor requiring `getMore` within a transaction + +Test that `MongoClient.bulkWrite` executed within a transaction properly iterates the results cursor when `getMore` is +required. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. This test must not be run +against standalone servers. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxBsonObjectSize` value from the +response. + +Construct a `MongoCollection` (referred to as `collection`) with the namespace "db.coll" (referred to as `namespace`). +Drop `collection`. + +Start a session on `client` (referred to as `session`). Start a transaction on `session`. + +Create the following list of write models (referred to as `models`): + +```javascript +UpdateOne { + "namespace": namespace, + "filter": { "_id": "a".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +UpdateOne { + "namespace": namespace, + "filter": { "_id": "b".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +``` + +Execute `bulkWrite` on `client` with `models`, `session`, and `verboseResults` set to true. Assert that the bulk write +succeeds and returns a `BulkWriteResult` (referred to as `result`). + +Assert that `result.upsertedCount` is equal to 2. + +Assert that the length of `result.updateResults` is equal to 2. + +Assert that a CommandStartedEvent was observed for the `getMore` command. + +### 9. `MongoClient.bulkWrite` handles a `getMore` error + +Test that `MongoClient.bulkWrite` properly handles a failure that occurs when attempting a `getMore`. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxBsonObjectSize` value from the +response. Then, configure the following fail point with `client`: + +```javascript +{ + "configureFailPoint": "failCommand", + "mode": { "times": 1 }, + "data": { + "failCommands": ["getMore"], + "errorCode": 8 + } +} +``` + +Construct a `MongoCollection` (referred to as `collection`) with the namespace "db.coll" (referred to as `namespace`). +Drop `collection`. Then create the following list of write models (referred to as `models`): + +```javascript +UpdateOne { + "namespace": namespace, + "filter": { "_id": "a".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +UpdateOne { + "namespace": namespace, + "filter": { "_id": "b".repeat(maxBsonObjectSize / 2) }, + "update": { "$set": { "x": 1 } }, + "upsert": true +}, +``` + +Execute `bulkWrite` on `client` with `models` and `verboseResults` set to true. Assert that the bulk write fails and +returns a `BulkWriteException` (referred to as `bulkWriteError`). + +Assert that `bulkWriteError.error` is populated with an error (referred to as `topLevelError`). Assert that +`topLevelError.errorCode` is equal to 8. + +Assert that `bulkWriteError.partialResult` is populated with a result (referred to as `partialResult`). Assert that +`partialResult.upsertedCount` is equal to 2. Assert that the length of `partialResult.updateResults` is equal to 1. + +Assert that a CommandStartedEvent was observed for the `getMore` command. + +Assert that a CommandStartedEvent was observed for the `killCursors` command. + +### 10. `MongoClient.bulkWrite` returns error for unacknowledged too-large insert + +Removed. + +#### With insert + +Removed. + +#### With replace + +Removed. + +### 11. `MongoClient.bulkWrite` batch splits when the addition of a new namespace exceeds the maximum message size + +Test that `MongoClient.bulkWrite` batch splits a bulk write when the addition of a new namespace to `nsInfo` causes the +size of the message to exceed `maxMessageSizeBytes - 1000`. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +Repeat the following setup for each test case: + +### Setup + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the following values from the response: +`maxBsonObjectSize` and `maxMessageSizeBytes`. + +Calculate the following values: + +```javascript +opsBytes = maxMessageSizeBytes - 1122 +numModels = opsBytes / maxBsonObjectSize +remainderBytes = opsBytes % maxBsonObjectSize +``` + +Construct the following write model (referred to as `firstModel`): + +```javascript +InsertOne { + "namespace": "db.coll", + "document": { "a": "b".repeat(maxBsonObjectSize - 57) } +} +``` + +Create a list of write models (referred to as `models`) with `firstModel` repeated `numModels` times. + +If `remainderBytes` is greater than or equal to 217, add 1 to `numModels` and append the following write model to +`models`: + +```javascript +InsertOne { + "namespace": "db.coll", + "document": { "a": "b".repeat(remainderBytes - 57) } +} +``` + +Then perform the following two tests: + +#### Case 1: No batch-splitting required + +Create the following write model (referred to as `sameNamespaceModel`): + +```javascript +InsertOne { + "namespace": "db.coll", + "document": { "a": "b" } +} +``` + +Append `sameNamespaceModel` to `models`. + +Execute `bulkWrite` on `client` with `models`. Assert that the bulk write succeeds and returns a `BulkWriteResult` +(referred to as `result`). + +Assert that `result.insertedCount` is equal to `numModels + 1`. + +Assert that one CommandStartedEvent was observed for the `bulkWrite` command (referred to as `event`). + +Assert that the length of `event.command.ops` is `numModels + 1`. Assert that the length of `event.command.nsInfo` is 1. +Assert that the namespace contained in `event.command.nsInfo` is "db.coll". + +#### Case 2: Batch-splitting required + +Construct the following namespace (referred to as `namespace`): + +```javascript +"db." + "c".repeat(200) +``` + +Create the following write model (referred to as `newNamespaceModel`): + +```javascript +InsertOne { + "namespace": namespace, + "document": { "a": "b" } +} +``` + +Append `newNamespaceModel` to `models`. + +Execute `bulkWrite` on `client` with `models`. Assert that the bulk write succeeds and returns a `BulkWriteResult` +(referred to as `result`). + +Assert that `result.insertedCount` is equal to `numModels + 1`. + +Assert that two CommandStartedEvents were observed for the `bulkWrite` command (referred to as `firstEvent` and +`secondEvent`). + +Assert that the length of `firstEvent.command.ops` is equal to `numModels`. Assert that the length of +`firstEvent.command.nsInfo` is equal to 1. Assert that the namespace contained in `firstEvent.command.nsInfo` is +"db.coll". + +Assert that the length of `secondEvent.command.ops` is equal to 1. Assert that the length of +`secondEvent.command.nsInfo` is equal to 1. Assert that the namespace contained in `secondEvent.command.nsInfo` is +`namespace`. + +#### Details on size calculations + +This information is not needed to implement this prose test, but is documented for future reference. This test is +designed to work if `maxBsonObjectSize` or `maxMessageSizeBytes` changes, but will need to be updated if a required +field is added to the `bulkWrite` command or the `insert` operation document, or if the overhead `OP_MSG` allowance is +changed in the bulk write specification. + +The command document for the `bulkWrite` has the following structure and size: + +```typescript +{ + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true +} + +// Size: 43 bytes +``` + +Each write model will create an `ops` document with the following structure and size: + +```typescript +{ + "insert": <0 | 1>, + "document": { + "_id": , + "a": + } +} + +// Size: 57 bytes + +``` + +The `ops` document for both `newNamespaceModel` and `sameNamespaceModel` has a string with one character, so it is a +total of 58 bytes. + +The models using the "db.coll" namespace will create one `nsInfo` document with the following structure and size: + +```javascript +{ + "ns": "db.coll" +} + +// Size: 21 bytes +``` + +`newNamespaceModel` will create an `nsInfo` document with the following structure and size: + +```javascript +{ + "ns": "db." +} + +// Size: 217 bytes +``` + +We need to fill up the rest of the message with bytes such that another `ops` document will fit, but another `nsInfo` +entry will not. The following calculations are used: + +```python +# 1000 is the OP_MSG overhead required in the spec +maxBulkWriteBytes = maxMessageSizeBytes - 1000 + +# bulkWrite command + first namespace entry +existingMessageBytes = 43 + 21 + +# Space to fit the last model's ops entry +lastModelBytes = 58 + +remainingBulkWriteBytes = maxBulkWriteBytes - existingMessageBytes - lastModelBytes + +# With the actual numbers plugged in +remainingBulkWriteBytes = maxMessageSizeBytes - 1122 +``` + +### 12. `MongoClient.bulkWrite` returns an error if no operations can be added to `ops` + +Test that `MongoClient.bulkWrite` returns an error if an operation provided exceeds `maxMessageSizeBytes` such that an +empty `ops` payload would be sent. + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. This test may be skipped by +drivers that are not able to construct arbitrarily large documents. + +Construct a `MongoClient` (referred to as `client`). Perform a `hello` command using `client` and record the +`maxMessageSizeBytes` value contained in the response. + +#### Case 1: `document` too large + +Construct the following write model (referred to as `largeDocumentModel`): + +```javascript +InsertOne { + "namespace": "db.coll", + "document": { "a": "b".repeat(maxMessageSizeBytes) } +} +``` + +Execute `bulkWrite` on `client` with `largeDocumentModel`. Assert that an error (referred to as `error`) is returned. +Assert that `error` is a client error. If a `BulkWriteException` was thrown, assert `BulkWriteException.partialResult` +is unset. + +#### Case 2: `namespace` too large + +Construct the following namespace (referred to as `namespace`): + +```javascript +"db." + "c".repeat(maxMessageSizeBytes) +``` + +Construct the following write model (referred to as `largeNamespaceModel`): + +```javascript +InsertOne { + "namespace": namespace, + "document": { "a": "b" } +} +``` + +Execute `bulkWrite` on `client` with `largeNamespaceModel`. Assert that an error (referred to as `error`) is returned. +Assert that `error` is a client error. If a `BulkWriteException` was thrown, assert `BulkWriteException.partialResult` +is unset. + +### 13. *Removed* + +### 14. `explain` helpers allow users to specify `maxTimeMS` + +Drivers that provide multiple APIs to specify explain should ensure this test is run at least once with each distinct +API. For example, the Node driver runs this test with option API (`collection.find({}, { explain: ... })`) and the +fluent API (`collection.find({}).explain(...)`). + +Create a MongoClient with command monitoring enabled (referred to as `client`). + +Create a collection, referred to as `collection`, with the namespace `explain-test.collection`. + +Run an explained find on `collection`. The find will have the query predicate `{ name: 'john doe' }`. Specify a +maxTimeMS value of 2000ms for the `explain`. + +Obtain the command started event for the explain. Confirm that the top-level explain command should has a `maxTimeMS` +value of `2000`. + +### 15. `MongoClient.bulkWrite` with unacknowledged write concern uses `w:0` for all batches + +This test must only be run on 8.0+ servers. This test must be skipped on Atlas Serverless. + +If testing with a sharded cluster, only connect to one mongos. This is intended to ensure the `countDocuments` operation +uses the same connection as the `bulkWrite` to get the correct connection count. (See +[DRIVERS-2921](https://jira.mongodb.org/browse/DRIVERS-2921)). + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. Perform a `hello` command using `client` and record the `maxBsonObjectSize` and +`maxMessageSizeBytes` values in the response. + +Construct a `MongoCollection` (referred to as `coll`) for the collection "db.coll". Drop `coll`. + +Use the `create` command to create "db.coll" to workaround [SERVER-95537](https://jira.mongodb.org/browse/SERVER-95537). + +Construct the following write model (referred to as `model`): + +```javascript +InsertOne: { + "namespace": "db.coll", + "document": { "a": "b".repeat(maxBsonObjectSize - 500) } +} +``` + +Construct a list of write models (referred to as `models`) with `model` repeated +`maxMessageSizeBytes / maxBsonObjectSize + 1` times. + +Call `client.bulkWrite` with `models`. Pass `BulkWriteOptions` with `ordered` set to `false` and `writeConcern` set to +an unacknowledged write concern. Assert no error occurred. Assert the result indicates the write was unacknowledged. + +Assert that two CommandStartedEvents (referred to as `firstEvent` and `secondEvent`) were observed for the `bulkWrite` +command. Assert that the length of `firstEvent.command.ops` is `maxMessageSizeBytes / maxBsonObjectSize`. Assert that +the length of `secondEvent.command.ops` is 1. If the driver exposes `operationId`s in its CommandStartedEvents, assert +that `firstEvent.operationId` is equal to `secondEvent.operationId`. Assert both commands include +`writeConcern: {w: 0}`. + +To force completion of the `w:0` writes, execute `coll.countDocuments` and expect the returned count is +`maxMessageSizeBytes / maxBsonObjectSize + 1`. This is intended to avoid incomplete writes interfering with other tests +that may use this collection. + +### 16. Generated document identifiers are the first field in their document + +Construct a `MongoClient` (referred to as `client`) with +[command monitoring](../../command-logging-and-monitoring/command-logging-and-monitoring.md) enabled to observe +CommandStartedEvents. For each of `insertOne`, client `bulkWrite`, and collection `bulkWrite`, do the following: + +- Execute the command with a document that does not contain an `_id` field. +- If possible, capture the wire protocol message (referred to as `request`) of the command and assert that the first + field of `request.documents[0]` is `_id`. +- Otherwise, capture the CommandStartedEvent (referred to as `event`) emitted by the command and assert that the first + field of `event.command.documents[0]` is `_id`. diff --git a/source/crud/tests/README.rst b/source/crud/tests/README.rst deleted file mode 100644 index fd222b022d..0000000000 --- a/source/crud/tests/README.rst +++ /dev/null @@ -1,74 +0,0 @@ -========== -CRUD Tests -========== - -The YAML and JSON files in this directory tree are platform-independent tests -meant to exercise the translation from the API to underlying commands that -MongoDB understands. Given the variety of languages and implementations and -limited nature of a description of a test, there are a number of things that -aren't testable. For instance, none of these tests assert that maxTimeMS was -properly sent to the server. This would involve a lot of infrastructure to -define and setup. Therefore, these YAML tests are in no way a replacement for -more thorough testing. However, they can provide an initial verification of your -implementation. - -Version -======= - -Files in the "specifications" repository have no version scheme. They are not -tied to a MongoDB server version. - -Format -====== - -Each YAML file has the following keys: - -- ``data``: The data that should exist in the collection under test before each - test run. - -- ``minServerVersion`` (optional): The minimum server version (inclusive) - required to successfully run the test. If this field is not present, it should - be assumed that there is no lower bound on the required server version. - -- ``maxServerVersion`` (optional): The maximum server version (exclusive) - against which this test can run successfully. If this field is not present, - it should be assumed that there is no upper bound on the required server - version. - -- ``tests``: An array of tests that are to be run independently of each other. - Each test will have some or all of the following fields: - - - ``description``: The name of the test. - - - ``operation``: Document describing the operation to be executed. This will - have the following fields: - - - ``name``: The name of the operation as defined in the specification. - - - ``arguments``: The names and values of arguments from the specification. - - - ``outcome``: Document describing the return value and/or expected state of - the collection after the operation is executed. This will have some or all - of the following fields: - - - ``result``: The return value from the operation. Note that some tests - specify an ``upsertedCount`` field when the server does not provide - one in the result document. In these cases, an ``upsertedCount`` field - with a value of 0 should be manually added to the document received - from the server to facilitate comparison. - - - ``collection``: - - - ``name`` (optional): The name of the collection to verify. If this - isn't present then use the collection under test. - - - ``data``: The data that should exist in the collection after the - operation has been run. - -Use as integration tests -======================== - -Running these as integration tests will require a running mongod server. Each of -these tests is valid against a standalone mongod, a replica set, and a sharded -system for server version 3.0 and later. Many of them will run against 2.6, but -some will require conditional code. diff --git a/source/crud/tests/read/aggregate-collation.json b/source/crud/tests/read/aggregate-collation.json deleted file mode 100644 index 85662a442f..0000000000 --- a/source/crud/tests/read/aggregate-collation.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "Aggregate with collation", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "x": "PING" - } - } - ], - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": "ping" - } - ] - } - } - ] -} diff --git a/source/crud/tests/read/aggregate-collation.yml b/source/crud/tests/read/aggregate-collation.yml deleted file mode 100644 index d526db6d12..0000000000 --- a/source/crud/tests/read/aggregate-collation.yml +++ /dev/null @@ -1,17 +0,0 @@ -data: - - {_id: 1, x: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "Aggregate with collation" - operation: - name: aggregate - arguments: - pipeline: - - $match: - x: 'PING' - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - outcome: - result: - - {_id: 1, x: 'ping'} diff --git a/source/crud/tests/read/aggregate-out.json b/source/crud/tests/read/aggregate-out.json deleted file mode 100644 index 205cf76571..0000000000 --- a/source/crud/tests/read/aggregate-out.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "Aggregate with $out", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "batchSize": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $out and batch size of 0", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "batchSize": 0 - } - }, - "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/read/aggregate-out.yml b/source/crud/tests/read/aggregate-out.yml deleted file mode 100644 index 747127c2ed..0000000000 --- a/source/crud/tests/read/aggregate-out.yml +++ /dev/null @@ -1,49 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} -minServerVersion: '2.6' - -tests: - - - description: "Aggregate with $out" - operation: - name: aggregate - arguments: - pipeline: - - $sort: {x: 1} - - $match: - _id: {$gt: 1} - - $out: "other_test_collection" - batchSize: 2 - - outcome: - result: - - {_id: 2, x: 22} - - {_id: 3, x: 33} - collection: - name: "other_test_collection" - data: - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "Aggregate with $out and batch size of 0" - operation: - name: aggregate - arguments: - pipeline: - - $sort: {x: 1} - - $match: - _id: {$gt: 1} - - $out: "other_test_collection" - batchSize: 0 - - outcome: - result: - - {_id: 2, x: 22} - - {_id: 3, x: 33} - collection: - name: "other_test_collection" - data: - - {_id: 2, x: 22} - - {_id: 3, x: 33} diff --git a/source/crud/tests/read/aggregate.json b/source/crud/tests/read/aggregate.json deleted file mode 100644 index 797a922395..0000000000 --- a/source/crud/tests/read/aggregate.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Aggregate with multiple stages", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "batchSize": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - ] -} diff --git a/source/crud/tests/read/aggregate.yml b/source/crud/tests/read/aggregate.yml deleted file mode 100644 index 98b69445c2..0000000000 --- a/source/crud/tests/read/aggregate.yml +++ /dev/null @@ -1,21 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "Aggregate with multiple stages" - operation: - name: aggregate - arguments: - pipeline: - - $sort: {x: 1} - - $match: - _id: {$gt: 1} - batchSize: 2 - - outcome: - result: - - {_id: 2, x: 22} - - {_id: 3, x: 33} diff --git a/source/crud/tests/read/count-collation.json b/source/crud/tests/read/count-collation.json deleted file mode 100644 index 6f75282fe0..0000000000 --- a/source/crud/tests/read/count-collation.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "PING" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "Count documents with collation", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": { - "x": "ping" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": 1 - } - }, - { - "description": "Deprecated count with collation", - "operation": { - "name": "count", - "arguments": { - "filter": { - "x": "ping" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": 1 - } - } - ] -} diff --git a/source/crud/tests/read/count-collation.yml b/source/crud/tests/read/count-collation.yml deleted file mode 100644 index a463a7aa03..0000000000 --- a/source/crud/tests/read/count-collation.yml +++ /dev/null @@ -1,25 +0,0 @@ -data: - - {_id: 1, x: 'PING'} -minServerVersion: '3.4' - -tests: - - - description: "Count documents with collation" - operation: - name: countDocuments - arguments: - filter: { x: 'ping' } - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: 1 - - - description: "Deprecated count with collation" - operation: - name: count - arguments: - filter: { x: 'ping' } - collation: { locale: 'en_US', strength: 2 } - - outcome: - result: 1 diff --git a/source/crud/tests/read/count-empty.json b/source/crud/tests/read/count-empty.json deleted file mode 100644 index 2b8627e0c6..0000000000 --- a/source/crud/tests/read/count-empty.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [], - "tests": [ - { - "description": "Estimated document count with empty collection", - "operation": { - "name": "estimatedDocumentCount", - "arguments": {} - }, - "outcome": { - "result": 0 - } - }, - { - "description": "Count documents with empty collection", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 0 - } - }, - { - "description": "Deprecated count with empty collection", - "operation": { - "name": "count", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 0 - } - } - ] -} diff --git a/source/crud/tests/read/count-empty.yml b/source/crud/tests/read/count-empty.yml deleted file mode 100644 index 83098ec18f..0000000000 --- a/source/crud/tests/read/count-empty.yml +++ /dev/null @@ -1,29 +0,0 @@ -data: [] - -tests: - - - description: "Estimated document count with empty collection" - operation: - name: estimatedDocumentCount - arguments: { } - - outcome: - result: 0 - - - description: "Count documents with empty collection" - operation: - name: countDocuments - arguments: - filter: { } - - outcome: - result: 0 - - - description: "Deprecated count with empty collection" - operation: - name: count - arguments: - filter: { } - - outcome: - result: 0 diff --git a/source/crud/tests/read/count.json b/source/crud/tests/read/count.json deleted file mode 100644 index 9642b2fbd0..0000000000 --- a/source/crud/tests/read/count.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Estimated document count", - "operation": { - "name": "estimatedDocumentCount", - "arguments": {} - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Count documents without a filter", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Count documents with a filter", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Count documents with skip and limit", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {}, - "skip": 1, - "limit": 3 - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Deprecated count without a filter", - "operation": { - "name": "count", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Deprecated count with a filter", - "operation": { - "name": "count", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Deprecated count with skip and limit", - "operation": { - "name": "count", - "arguments": { - "filter": {}, - "skip": 1, - "limit": 3 - } - }, - "outcome": { - "result": 2 - } - } - ] -} diff --git a/source/crud/tests/read/count.yml b/source/crud/tests/read/count.yml deleted file mode 100644 index 33d380b7d5..0000000000 --- a/source/crud/tests/read/count.yml +++ /dev/null @@ -1,74 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "Estimated document count" - operation: - name: estimatedDocumentCount - arguments: { } - - outcome: - result: 3 - - - description: "Count documents without a filter" - operation: - name: countDocuments - arguments: - filter: { } - - outcome: - result: 3 - - - description: "Count documents with a filter" - operation: - name: countDocuments - arguments: - filter: - _id: {$gt: 1} - - outcome: - result: 2 - - - description: "Count documents with skip and limit" - operation: - name: countDocuments - arguments: - filter: {} - skip: 1 - limit: 3 - - outcome: - result: 2 - - - description: "Deprecated count without a filter" - operation: - name: count - arguments: - filter: { } - - outcome: - result: 3 - - - description: "Deprecated count with a filter" - operation: - name: count - arguments: - filter: - _id: {$gt: 1} - - outcome: - result: 2 - - - description: "Deprecated count with skip and limit" - operation: - name: count - arguments: - filter: {} - skip: 1 - limit: 3 - - outcome: - result: 2 diff --git a/source/crud/tests/read/distinct-collation.json b/source/crud/tests/read/distinct-collation.json deleted file mode 100644 index 0af0c67cb7..0000000000 --- a/source/crud/tests/read/distinct-collation.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "string": "PING" - }, - { - "_id": 2, - "string": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "Distinct with a collation", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "string", - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - "PING" - ] - } - } - ] -} diff --git a/source/crud/tests/read/distinct-collation.yml b/source/crud/tests/read/distinct-collation.yml deleted file mode 100644 index 08bd6a9292..0000000000 --- a/source/crud/tests/read/distinct-collation.yml +++ /dev/null @@ -1,17 +0,0 @@ -data: - - {_id: 1, string: 'PING'} - - {_id: 2, string: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "Distinct with a collation" - operation: - name: distinct - arguments: - fieldName: "string" - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - - 'PING' diff --git a/source/crud/tests/read/distinct.json b/source/crud/tests/read/distinct.json deleted file mode 100644 index a57ee36a83..0000000000 --- a/source/crud/tests/read/distinct.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Distinct without a filter", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "x", - "filter": {} - } - }, - "outcome": { - "result": [ - 11, - 22, - 33 - ] - } - }, - { - "description": "Distinct with a filter", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": [ - 22, - 33 - ] - } - } - ] -} diff --git a/source/crud/tests/read/distinct.yml b/source/crud/tests/read/distinct.yml deleted file mode 100644 index aefc7e0fdf..0000000000 --- a/source/crud/tests/read/distinct.yml +++ /dev/null @@ -1,32 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "Distinct without a filter" - operation: - name: distinct - arguments: - fieldName: "x" - filter: {} - - outcome: - result: - - 11 - - 22 - - 33 - - - description: "Distinct with a filter" - operation: - name: distinct - arguments: - fieldName: "x" - filter: - _id: {$gt: 1} - - outcome: - result: - - 22 - - 33 \ No newline at end of file diff --git a/source/crud/tests/read/find-collation.json b/source/crud/tests/read/find-collation.json deleted file mode 100644 index 53d0e94900..0000000000 --- a/source/crud/tests/read/find-collation.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "Find with a collation", - "operation": { - "name": "find", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": "ping" - } - ] - } - } - ] -} diff --git a/source/crud/tests/read/find-collation.yml b/source/crud/tests/read/find-collation.yml deleted file mode 100644 index a014dc2510..0000000000 --- a/source/crud/tests/read/find-collation.yml +++ /dev/null @@ -1,15 +0,0 @@ -data: - - {_id: 1, x: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "Find with a collation" - operation: - name: "find" - arguments: - filter: {x: 'PING'} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - outcome: - result: - - {_id: 1, x: 'ping'} diff --git a/source/crud/tests/read/find.json b/source/crud/tests/read/find.json deleted file mode 100644 index 3597e37be6..0000000000 --- a/source/crud/tests/read/find.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "Find with filter", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": 11 - } - ] - } - }, - { - "description": "Find with filter, sort, skip, and limit", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gt": 2 - } - }, - "sort": { - "_id": 1 - }, - "skip": 2, - "limit": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 5, - "x": 55 - } - ] - } - }, - { - "description": "Find with limit, sort, and batchsize", - "operation": { - "name": "find", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4, - "batchSize": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - ] -} diff --git a/source/crud/tests/read/find.yml b/source/crud/tests/read/find.yml deleted file mode 100644 index 25099df7f1..0000000000 --- a/source/crud/tests/read/find.yml +++ /dev/null @@ -1,49 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} - - {_id: 5, x: 55} - -tests: - - - description: "Find with filter" - operation: - name: "find" - arguments: - filter: {_id: 1} - - outcome: - result: - - {_id: 1, x: 11} - - - - description: "Find with filter, sort, skip, and limit" - operation: - name: "find" - arguments: - filter: - _id: {$gt: 2} - sort: {_id: 1} - skip: 2 - limit: 2 - - outcome: - result: - - {_id: 5, x: 55} - - - description: "Find with limit, sort, and batchsize" - operation: - name: "find" - arguments: - filter: {} - sort: {_id: 1} - limit: 4 - batchSize: 2 - - outcome: - result: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} diff --git a/source/crud/tests/unified/aggregate-allowdiskuse.json b/source/crud/tests/unified/aggregate-allowdiskuse.json new file mode 100644 index 0000000000..2e54175b8a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/aggregate-allowdiskuse.yml b/source/crud/tests/unified/aggregate-allowdiskuse.yml new file mode 100644 index 0000000000..d9d2556205 --- /dev/null +++ b/source/crud/tests/unified/aggregate-allowdiskuse.yml @@ -0,0 +1,75 @@ +description: aggregate-allowdiskuse + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: 'Aggregate does not send allowDiskUse when value is not specified' + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: &pipeline [ { $match: {} } ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline + allowDiskUse: { $$exists: false } + commandName: aggregate + databaseName: *database0Name + + - description: 'Aggregate sends allowDiskUse false when false is specified' + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: *pipeline + allowDiskUse: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline + allowDiskUse: false + commandName: aggregate + databaseName: *database0Name + + - description: 'Aggregate sends allowDiskUse true when true is specified' + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: *pipeline + allowDiskUse: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline + allowDiskUse: true + commandName: aggregate + databaseName: *database0Name diff --git a/source/crud/tests/unified/aggregate-collation.json b/source/crud/tests/unified/aggregate-collation.json new file mode 100644 index 0000000000..e7f0c3a7f1 --- /dev/null +++ b/source/crud/tests/unified/aggregate-collation.json @@ -0,0 +1,73 @@ +{ + "description": "aggregate-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with collation", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "x": "PING" + } + } + ], + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": [ + { + "_id": 1, + "x": "ping" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/aggregate-collation.yml b/source/crud/tests/unified/aggregate-collation.yml new file mode 100644 index 0000000000..088dc5705b --- /dev/null +++ b/source/crud/tests/unified/aggregate-collation.yml @@ -0,0 +1,48 @@ +description: aggregate-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: ping } + +tests: + - + description: 'Aggregate with collation' + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $match: { x: PING } } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + - { _id: 1, x: ping } diff --git a/source/crud/tests/unified/aggregate-let.json b/source/crud/tests/unified/aggregate-let.json new file mode 100644 index 0000000000..039900920f --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/aggregate-let.yml b/source/crud/tests/unified/aggregate-let.yml new file mode 100644 index 0000000000..7fa1f9b798 --- /dev/null +++ b/source/crud/tests/unified/aggregate-let.yml @@ -0,0 +1,138 @@ +description: "aggregate-let" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - collectionName: *collection1Name + databaseName: *database0Name + documents: [ ] + +tests: + - description: "Aggregate with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline0 + # $match takes a query expression, so $expr is necessary to utilize + # an aggregate expression context and access "let" variables. + - $match: { $expr: { $eq: ["$_id", "$$id"] } } + - $project: { _id: 0, x: "$$x", y: "$$y", rand: "$$rand" } + # Values in "let" must be constant or closed expressions that do not + # depend on document values. This test demonstrates a basic constant + # value, a value wrapped with $literal (to avoid expression parsing), + # and a closed expression (e.g. $rand). + let: &let0 + id: 1 + x: foo + y: { $literal: "$bar" } + rand: { $rand: {} } + expectResult: + - { x: "foo", y: "$bar", rand: { $$type: "double" } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline0 + let: *let0 + + - 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: &pipeline1 + - $match: { _id: 1 } + let: &let1 + x: foo + expectError: + # Older server versions may not report an error code, but the error + # message is consistent between 2.6.x and 4.4.x server versions. + errorContains: "unrecognized field 'let'" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline1 + let: *let1 + + - description: "Aggregate to collection with let option" + runOnRequirements: + - minServerVersion: "5.0" + serverless: "forbid" + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline2 + - $match: { $expr: { $eq: ["$_id", "$$id"] } } + - $project: { _id: 1 } + - $out: *collection1Name + let: &let2 + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline2 + let: *let2 + outcome: + - collectionName: *collection1Name + databaseName: *database0Name + 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: *pipeline2 + let: *let2 + expectError: + errorContains: "unrecognized field 'let'" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline2 + let: *let2 diff --git a/source/crud/tests/unified/aggregate-merge-errorResponse.json b/source/crud/tests/unified/aggregate-merge-errorResponse.json new file mode 100644 index 0000000000..6c7305fd91 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/aggregate-merge-errorResponse.yml b/source/crud/tests/unified/aggregate-merge-errorResponse.yml new file mode 100644 index 0000000000..5fd679bffb --- /dev/null +++ b/source/crud/tests/unified/aggregate-merge-errorResponse.yml @@ -0,0 +1,42 @@ +description: "aggregate-merge-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 1 } + - { _id: 2, x: 1 } + +tests: + - description: "aggregate $merge DuplicateKey error is accessible" + runOnRequirements: + - minServerVersion: "5.1" # SERVER-59097 + # Exclude sharded topologies since the aggregate command fails with + # IllegalOperation(20) instead of DuplicateKey(11000) + topologies: [ single, replicaset ] + operations: + - name: aggregate + object: *database0 + arguments: + pipeline: + - { $documents: [ { _id: 2, x: 1 } ] } + - { $merge: { into: *collection0Name, whenMatched: "fail" } } + expectError: + errorCode: 11000 # DuplicateKey + errorResponse: + keyPattern: { _id: 1 } + keyValue: { _id: 2 } diff --git a/source/crud/tests/unified/aggregate-merge.json b/source/crud/tests/unified/aggregate-merge.json new file mode 100644 index 0000000000..ac61ceb8a6 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/aggregate-merge.yml b/source/crud/tests/unified/aggregate-merge.yml new file mode 100644 index 0000000000..48dd54d1bd --- /dev/null +++ b/source/crud/tests/unified/aggregate-merge.yml @@ -0,0 +1,182 @@ +description: aggregate-merge +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.1.11 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_aggregate_merge + - + collection: + id: &collection_readConcern_majority collection_readConcern_majority + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "majority" } + - + collection: + id: &collection_readConcern_local collection_readConcern_local + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "local" } + - + collection: + id: &collection_readConcern_available collection_readConcern_available + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "available" } +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 +tests: + - + description: 'Aggregate with $merge' + operations: + - + object: *collection0 + name: aggregate + arguments: &arguments + pipeline: &pipeline + - + $sort: + x: 1 + - + $match: + _id: + $gt: 1 + - + $merge: + into: &output_collection other_test_collection + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + outcome: &outcome + - + collectionName: *output_collection + databaseName: *database_name + 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: &pipeline + - + $sort: + x: 1 + - + $match: + _id: + $gt: 1 + - + $merge: + into: &output_collection other_test_collection + batchSize: 0 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + cursor: { } + outcome: *outcome + - + description: 'Aggregate with $merge and majority readConcern' + operations: + - + object: *collection_readConcern_majority + name: aggregate + arguments: *arguments + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: majority + outcome: *outcome + - + description: 'Aggregate with $merge and local readConcern' + operations: + - + object: *collection_readConcern_local + name: aggregate + arguments: *arguments + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: local + outcome: *outcome + - + description: 'Aggregate with $merge and available readConcern' + operations: + - + object: *collection_readConcern_available + name: aggregate + arguments: *arguments + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: available + outcome: *outcome diff --git a/source/crud/tests/unified/aggregate-out-readConcern.json b/source/crud/tests/unified/aggregate-out-readConcern.json new file mode 100644 index 0000000000..e293457c1c --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/aggregate-out-readConcern.yml b/source/crud/tests/unified/aggregate-out-readConcern.yml new file mode 100644 index 0000000000..67bdee676a --- /dev/null +++ b/source/crud/tests/unified/aggregate-out-readConcern.yml @@ -0,0 +1,168 @@ +description: aggregate-out-readConcern +schemaVersion: '1.4' +runOnRequirements: + - + minServerVersion: 4.1.0 + topologies: + - replicaset + - sharded + serverless: "forbid" +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_aggregate_out_readconcern + - + collection: + id: &collection_readConcern_majority collection_readConcern_majority + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "majority" } + - + collection: + id: &collection_readConcern_local collection_readConcern_local + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "local" } + - + collection: + id: &collection_readConcern_available collection_readConcern_available + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "available" } + - + collection: + id: &collection_readConcern_linearizable collection_readConcern_linearizable + database: database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: "linearizable" } +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &arguments + pipeline: + - + $sort: + x: 1 + - + $match: + _id: + $gt: 1 + - + $out: &output_collection other_test_collection + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: &pipeline + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: other_test_collection } + readConcern: + level: majority + outcome: &outcome + - + collectionName: *output_collection + databaseName: *database_name + documents: + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + description: 'readConcern local with out stage' + operations: + - + object: *collection_readConcern_local + name: aggregate + arguments: *arguments + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: local + outcome: *outcome + - + description: 'readConcern available with out stage' + operations: + - + object: *collection_readConcern_available + name: aggregate + arguments: *arguments + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: available + outcome: *outcome + - + description: 'readConcern linearizable with out stage' + operations: + - + object: *collection_readConcern_linearizable + name: aggregate + arguments: *arguments + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + readConcern: + level: linearizable diff --git a/source/crud/tests/unified/aggregate-out.json b/source/crud/tests/unified/aggregate-out.json new file mode 100644 index 0000000000..db0d7918cf --- /dev/null +++ b/source/crud/tests/unified/aggregate-out.json @@ -0,0 +1,143 @@ +{ + "description": "aggregate-out", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "2.6", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $out", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "batchSize": 2 + } + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $out and batch size of 0", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "batchSize": 0 + } + } + ], + "outcome": [ + { + "collectionName": "other_test_collection", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/aggregate-out.yml b/source/crud/tests/unified/aggregate-out.yml new file mode 100644 index 0000000000..a85eed0322 --- /dev/null +++ b/source/crud/tests/unified/aggregate-out.yml @@ -0,0 +1,72 @@ +description: aggregate-out + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '2.6' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'Aggregate with $out' + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: other_test_collection } + batchSize: 2 + outcome: + - + collectionName: other_test_collection + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'Aggregate with $out and batch size of 0' + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: other_test_collection } + batchSize: 0 + outcome: + - + collectionName: other_test_collection + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/aggregate-rawdata.json b/source/crud/tests/unified/aggregate-rawdata.json new file mode 100644 index 0000000000..d34a359f9d --- /dev/null +++ b/source/crud/tests/unified/aggregate-rawdata.json @@ -0,0 +1,116 @@ +{ + "description": "aggregate-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Aggregate with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": {} + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": {} + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/aggregate-rawdata.yml b/source/crud/tests/unified/aggregate-rawdata.yml new file mode 100644 index 0000000000..11a398e08a --- /dev/null +++ b/source/crud/tests/unified/aggregate-rawdata.yml @@ -0,0 +1,55 @@ +description: "aggregate-rawData" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Aggregate with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline0 + - $match: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline0 + rawData: *rawdata + - description: "Aggregate with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline0 + - $match: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline0 + rawData: + $$exists: false diff --git a/source/crud/tests/unified/aggregate-write-readPreference.json b/source/crud/tests/unified/aggregate-write-readPreference.json new file mode 100644 index 0000000000..c1fa3b4574 --- /dev/null +++ b/source/crud/tests/unified/aggregate-write-readPreference.json @@ -0,0 +1,391 @@ +{ + "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 + } + ] + } + ], + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/aggregate-write-readPreference.yml b/source/crud/tests/unified/aggregate-write-readPreference.yml new file mode 100644 index 0000000000..16f1035752 --- /dev/null +++ b/source/crud/tests/unified/aggregate-write-readPreference.yml @@ -0,0 +1,143 @@ +description: aggregate-write-readPreference + +schemaVersion: '1.4' + +runOnRequirements: + # 3.6+ non-standalone is needed to utilize $readPreference in OP_MSG + - minServerVersion: "3.6" + topologies: [ replicaset, sharded, load-balanced ] + +_yamlAnchors: + readConcern: &readConcern + level: &readConcernLevel "local" + writeConcern: &writeConcern + w: &writeConcernW 1 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + # Used to test that read and write concerns are still inherited + uriOptions: + readConcernLevel: *readConcernLevel + w: *writeConcernW + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + readPreference: &readPreference + # secondaryPreferred is specified for compatibility with clusters that + # may not have a secondary (e.g. each shard is only a primary). + mode: secondaryPreferred + # maxStalenessSeconds is specified to ensure that drivers forward the + # read preference to mongos or a load balancer. That would not be the + # case with only secondaryPreferred. + maxStalenessSeconds: 600 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +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: &outPipeline + - { $match: { _id: { $gt: 1 } } } + - { $sort: { x: 1 } } + - { $out: *collection1Name } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *outPipeline + $readPreference: *readPreference + readConcern: *readConcern + writeConcern: *writeConcern + + - description: "Aggregate with $out omits read preference for pre-5.0 server" + runOnRequirements: + # MongoDB 4.2 introduced support for read concerns and write stages. + # Pre-4.2 servers may allow a "local" read concern anyway, but some + # drivers may avoid inheriting a client-level read concern for pre-4.2. + - minServerVersion: "4.2" + maxServerVersion: "4.4.99" + serverless: "forbid" + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: *outPipeline + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *outPipeline + $readPreference: { $$exists: false } + readConcern: *readConcern + writeConcern: *writeConcern + + - description: "Aggregate with $merge includes read preference for 5.0+ server" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: &mergePipeline + - { $match: { _id: { $gt: 1 } } } + - { $sort: { x: 1 } } + - { $merge: { into: *collection1Name } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *mergePipeline + $readPreference: *readPreference + readConcern: *readConcern + writeConcern: *writeConcern + + - 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: *mergePipeline + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *mergePipeline + $readPreference: { $$exists: false } + readConcern: *readConcern + writeConcern: *writeConcern diff --git a/source/crud/tests/unified/aggregate.json b/source/crud/tests/unified/aggregate.json new file mode 100644 index 0000000000..55634f05f6 --- /dev/null +++ b/source/crud/tests/unified/aggregate.json @@ -0,0 +1,615 @@ +{ + "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" + } + } + ] + } + ] + }, + { + "description": "Aggregate with multiple stages", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2 + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/aggregate.yml b/source/crud/tests/unified/aggregate.yml new file mode 100644 index 0000000000..6f99aaf0fe --- /dev/null +++ b/source/crud/tests/unified/aggregate.yml @@ -0,0 +1,232 @@ +description: "aggregate" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true # ensure cursors pin to a single server + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name aggregate-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + cursor: { batchSize: 2 } + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + commandName: getMore + databaseName: *database0Name + + - 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: *collection0Name + 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: &comment0 { content: "test" } + object: *collection0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } } } ] + comment: *comment0 + + - 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: *comment0 + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + comment: *comment0 + commandName: aggregate + databaseName: *database0Name + + - 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: *comment0 + 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: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + cursor: { batchSize: 2 } + comment: *comment0 + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment0 + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment0 + commandName: getMore + databaseName: *database0Name + + - 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: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + cursor: { batchSize: 2 } + comment: "comment" + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + commandName: getMore + databaseName: *database0Name + - + description: 'Aggregate with multiple stages' + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + batchSize: 2 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } diff --git a/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.json b/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.json new file mode 100644 index 0000000000..63815e3233 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.json @@ -0,0 +1,151 @@ +{ + "description": "bulkWrite-arrayFilters-clientError", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "3.5.5" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "crud-v2" + } + } + ], + "initialData": [ + { + "collectionName": "crud-v2", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite on server that doesn't support arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": {}, + "update": { + "$set": { + "y.0.b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "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": [ + { + "updateOne": { + "filter": {}, + "update": { + "$set": { + "y.0.b": 2 + } + } + } + }, + { + "updateMany": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml b/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml new file mode 100644 index 0000000000..12bc84c704 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-arrayFilters-clientError.yml @@ -0,0 +1,95 @@ +description: bulkWrite-arrayFilters-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.5.5 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name crud-v2 +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + 'y': + - + b: 3 + - + b: 1 + - + _id: 2 + 'y': + - + b: 0 + - + b: 1 +tests: + - + description: 'BulkWrite on server that doesn''t support arrayFilters' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateOne: + filter: { } + update: + $set: + y.0.b: 2 + arrayFilters: + - + i.b: 1 + ordered: true + expectError: + isError: true + 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: + - + updateOne: + filter: { } + update: + $set: + y.0.b: 2 + - + updateMany: + filter: { } + update: + $set: + 'y.$[i].b': 2 + arrayFilters: + - + i.b: 1 + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] diff --git a/source/crud/tests/unified/bulkWrite-arrayFilters.json b/source/crud/tests/unified/bulkWrite-arrayFilters.json new file mode 100644 index 0000000000..bc4e7b9fcb --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-arrayFilters.json @@ -0,0 +1,364 @@ +{ + "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 + } + ] + } + ] + } + ] + }, + { + "description": "BulkWrite with arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + } + }, + { + "updateMany": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 3, + "modifiedCount": 3, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 2 + }, + { + "b": 2 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 2 + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-arrayFilters.yml b/source/crud/tests/unified/bulkWrite-arrayFilters.yml new file mode 100644 index 0000000000..9db833581f --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-arrayFilters.yml @@ -0,0 +1,210 @@ +description: bulkWrite-arrayFilters +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.5.6 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: *collection_name + updates: + - + q: { } + u: + $set: { 'y.$[i].b': 2 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + arrayFilters: + - { i.b: 3 } + ordered: true + commandName: update + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + 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: *collection_name + updates: + - + q: { } + u: + $set: { 'y.$[i].b': 2 } + multi: true + upsert: { $$unsetOrMatches: false } + arrayFilters: + - { i.b: 1 } + ordered: true + commandName: update + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + 'y': + - + b: 3 + - + b: 2 + - + _id: 2 + 'y': + - + b: 0 + - + b: 2 + - + description: 'BulkWrite with arrayFilters' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + # UpdateOne when one document matches arrayFilters + updateOne: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 3 } + - + # UpdateMany when multiple documents match arrayFilters + updateMany: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 1 } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 3 + modifiedCount: 3 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 2 }, { b: 2 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 2 } ] } diff --git a/source/crud/tests/unified/bulkWrite-collation.json b/source/crud/tests/unified/bulkWrite-collation.json new file mode 100644 index 0000000000..fe54b1a1e3 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-collation.json @@ -0,0 +1,254 @@ +{ + "description": "bulkWrite-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + }, + { + "_id": 4, + "x": "pong" + }, + { + "_id": 5, + "x": "pONg" + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite with delete operations and collation", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "x": "PING" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + } + }, + { + "deleteOne": { + "filter": { + "x": "PING" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + } + }, + { + "deleteMany": { + "filter": { + "x": "PONG" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 4, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "BulkWrite with update operations and collation", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "x": "ping" + }, + "update": { + "$set": { + "x": "PONG" + } + }, + "collation": { + "locale": "en_US", + "strength": 3 + } + } + }, + { + "updateOne": { + "filter": { + "x": "ping" + }, + "update": { + "$set": { + "x": "PONG" + } + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + } + }, + { + "replaceOne": { + "filter": { + "x": "ping" + }, + "replacement": { + "_id": 6, + "x": "ping" + }, + "upsert": true, + "collation": { + "locale": "en_US", + "strength": 3 + } + } + }, + { + "updateMany": { + "filter": { + "x": "pong" + }, + "update": { + "$set": { + "x": "PONG" + } + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 6, + "modifiedCount": 4, + "upsertedCount": 1, + "upsertedIds": { + "2": 6 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "PONG" + }, + { + "_id": 3, + "x": "PONG" + }, + { + "_id": 4, + "x": "PONG" + }, + { + "_id": 5, + "x": "PONG" + }, + { + "_id": 6, + "x": "ping" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-collation.yml b/source/crud/tests/unified/bulkWrite-collation.yml new file mode 100644 index 0000000000..b9b3fc4fe9 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-collation.yml @@ -0,0 +1,130 @@ +description: bulkWrite-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + - { _id: 4, x: pong } + - { _id: 5, x: pONg } + +# See: https://www.mongodb.com/docs/manual/reference/collation/#collation-document +tests: + - + description: 'BulkWrite with delete operations and collation' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + # matches two documents but deletes one + deleteOne: + filter: { x: "PING" } + collation: { locale: "en_US", strength: 2 } + - + # matches the remaining document and deletes it + deleteOne: + filter: { x: "PING" } + collation: { locale: "en_US", strength: 2 } + - + # matches two documents and deletes them + deleteMany: + filter: { x: "PONG" } + collation: { locale: "en_US", strength: 2 } + ordered: true + expectResult: + deletedCount: 4 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'BulkWrite with update operations and collation' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + # matches only one document due to strength and updates + updateMany: + filter: { x: "ping" } + update: { $set: { x: "PONG" } } + collation: { locale: "en_US", strength: 3 } + - + # matches one document and updates + updateOne: + filter: { x: "ping" } + update: { $set: { x: "PONG" } } + collation: { locale: "en_US", strength: 2 } + - + # matches no document due to strength and upserts + replaceOne: + filter: { x: "ping" } + replacement: { _id: 6, x: "ping" } + upsert: true + collation: { locale: "en_US", strength: 3 } + - + # matches two documents and updates + updateMany: + filter: { x: "pong" } + update: { $set: { x: "PONG" } } + collation: { locale: "en_US", strength: 2 } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 6 + modifiedCount: 4 + upsertedCount: 1 + upsertedIds: { '2': 6 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - {_id: 1, x: 11 } + - {_id: 2, x: "PONG" } + - {_id: 3, x: "PONG" } + - {_id: 4, x: "PONG" } + - {_id: 5, x: "PONG" } + - {_id: 6, x: "ping" } diff --git a/source/crud/tests/unified/bulkWrite-comment.json b/source/crud/tests/unified/bulkWrite-comment.json new file mode 100644 index 0000000000..0b2addc850 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-comment.yml b/source/crud/tests/unified/bulkWrite-comment.yml new file mode 100644 index 0000000000..965a0b8384 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-comment.yml @@ -0,0 +1,175 @@ +description: bulkWrite-comment +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name BulkWrite_comment + +initialData: &initial_data + - collectionName: *collection_name + databaseName: *database_name + 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: &requests + - insertOne: + document: &inserted_document + _id: 5 + x: "inserted" + - replaceOne: + filter: &replaceOne_filter + _id: 1 + replacement: &replacement { _id: 1, x: "replaced" } + - updateOne: + filter: &updateOne_filter + _id: 2 + update: &update { $set: {x: "updated"} } + - deleteOne: + filter: &deleteOne_filter + _id: 3 + comment: &string_comment "comment" + expectResult: &expect_results + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { 0: 5} } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *replaceOne_filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: *updateOne_filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + limit: 1 + ordered: true + comment: *string_comment + outcome: &outcome + - collectionName: *collection_name + databaseName: *database_name + 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: *requests + comment: &document_comment { key: "value" } + expectResult: *expect_results + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *replaceOne_filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: *updateOne_filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + limit: 1 + ordered: true + comment: *document_comment + outcome: *outcome + + - description: 'BulkWrite with comment - pre 4.4' + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: *requests + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: "comment" + outcome: *initial_data diff --git a/source/crud/tests/unified/bulkWrite-delete-hint-clientError.json b/source/crud/tests/unified/bulkWrite-delete-hint-clientError.json new file mode 100644 index 0000000000..2961b55dc0 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-delete-hint-clientError.json @@ -0,0 +1,193 @@ +{ + "description": "bulkWrite-delete-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": "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 (client-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": [] + } + ], + "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 (client-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": [] + } + ], + "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/source/crud/tests/unified/bulkWrite-delete-hint-clientError.yml b/source/crud/tests/unified/bulkWrite-delete-hint-clientError.yml new file mode 100644 index 0000000000..5c17261004 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-delete-hint-clientError.yml @@ -0,0 +1,110 @@ +description: bulkWrite-delete-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name BulkWrite_delete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 +tests: + - + description: 'BulkWrite deleteOne with hints unsupported (client-side error)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: &deleteOne_filter1 + _id: 1 + hint: &hint_string _id_ + - + deleteOne: + filter: &deleteOne_filter2 + _id: 2 + hint: &hint_doc + _id: 1 + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + description: 'BulkWrite deleteMany with hints unsupported (client-side error)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: &deleteMany_filter1 + _id: + $lt: 3 + hint: *hint_string + - + deleteMany: + filter: &deleteMany_filter2 + _id: + $gte: 4 + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/bulkWrite-delete-hint-serverError.json b/source/crud/tests/unified/bulkWrite-delete-hint-serverError.json new file mode 100644 index 0000000000..fa99522093 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-delete-hint-serverError.yml b/source/crud/tests/unified/bulkWrite-delete-hint-serverError.yml new file mode 100644 index 0000000000..06ad7787a7 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-delete-hint-serverError.yml @@ -0,0 +1,139 @@ +description: bulkWrite-delete-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.3.3 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name BulkWrite_delete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &deleteOne_filter1 + _id: 1 + hint: &hint_string _id_ + - + deleteOne: + filter: &deleteOne_filter2 + _id: 2 + hint: &hint_doc + _id: 1 + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *deleteOne_filter1 + hint: *hint_string + limit: 1 + - + q: *deleteOne_filter2 + hint: *hint_doc + limit: 1 + ordered: true + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: &deleteMany_filter1 + _id: + $lt: 3 + hint: *hint_string + - + deleteMany: + filter: &deleteMany_filter2 + _id: + $gte: 4 + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *deleteMany_filter1 + hint: *hint_string + limit: 0 + - + q: *deleteMany_filter2 + hint: *hint_doc + limit: 0 + ordered: true + outcome: *outcome diff --git a/source/crud/tests/unified/bulkWrite-delete-hint.json b/source/crud/tests/unified/bulkWrite-delete-hint.json new file mode 100644 index 0000000000..9fcdecefd7 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-delete-hint.yml b/source/crud/tests/unified/bulkWrite-delete-hint.yml new file mode 100644 index 0000000000..ac451bfc46 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-delete-hint.yml @@ -0,0 +1,151 @@ +description: bulkWrite-delete-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.4 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name BulkWrite_delete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &deleteOne_filter1 + _id: 1 + hint: &hint_string _id_ + - + deleteOne: + filter: &deleteOne_filter2 + _id: 2 + hint: &hint_doc + _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: *collection_name + deletes: + - + q: *deleteOne_filter1 + hint: *hint_string + limit: 1 + - + q: *deleteOne_filter2 + hint: *hint_doc + limit: 1 + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + description: 'BulkWrite deleteMany with hints' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: &deleteMany_filter1 + _id: + $lt: 3 + hint: *hint_string + - + deleteMany: + filter: &deleteMany_filter2 + _id: + $gte: 4 + hint: *hint_doc + ordered: true + expectResult: + deletedCount: 3 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: {} } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *deleteMany_filter1 + hint: *hint_string + limit: 0 + - + q: *deleteMany_filter2 + hint: *hint_doc + limit: 0 + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 3 + x: 33 diff --git a/source/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.json b/source/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.json new file mode 100644 index 0000000000..2dda9486e8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.yml b/source/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.yml new file mode 100644 index 0000000000..aa2cdbce92 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.yml @@ -0,0 +1,98 @@ +description: bulkWrite-deleteMany-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged deleteMany with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteMany: + filter: *filter + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + 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: *filter + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/bulkWrite-deleteMany-let.json b/source/crud/tests/unified/bulkWrite-deleteMany-let.json new file mode 100644 index 0000000000..45c20ea49a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-deleteMany-let.yml b/source/crud/tests/unified/bulkWrite-deleteMany-let.yml new file mode 100644 index 0000000000..48b3a06d2e --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteMany-let.yml @@ -0,0 +1,86 @@ +description: "BulkWrite deleteMany-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$_id", "$$id" ] + let: &let + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + let: *let + expectError: + errorContains: "'delete.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.json b/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.json new file mode 100644 index 0000000000..a74149e979 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.json @@ -0,0 +1,122 @@ +{ + "description": "BulkWrite deleteMany-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "BulkWrite deleteMany with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite deleteMany with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.yml b/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.yml new file mode 100644 index 0000000000..f1722058af --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteMany-rawdata.yml @@ -0,0 +1,61 @@ +description: "BulkWrite deleteMany-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "BulkWrite deleteMany with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteMany: + filter: &filter {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + rawData: *rawdata + - description: "BulkWrite deleteMany with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteMany: + filter: &filter {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + rawData: + $$exists: false diff --git a/source/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.json b/source/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.json new file mode 100644 index 0000000000..aadf6d9e99 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.yml b/source/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..820697008e --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.yml @@ -0,0 +1,97 @@ +description: bulkWrite-deleteOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged deleteOne with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteOne: + filter: *filter + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + 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: *filter + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/bulkWrite-deleteOne-let.json b/source/crud/tests/unified/bulkWrite-deleteOne-let.json new file mode 100644 index 0000000000..f3268163cb --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-deleteOne-let.yml b/source/crud/tests/unified/bulkWrite-deleteOne-let.yml new file mode 100644 index 0000000000..128d74a441 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteOne-let.yml @@ -0,0 +1,86 @@ +description: "BulkWrite deleteOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$_id", "$$id" ] + let: &let + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + let: *let + expectError: + errorContains: "'delete.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.json b/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.json new file mode 100644 index 0000000000..534e62c9e8 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.json @@ -0,0 +1,122 @@ +{ + "description": "BulkWrite deleteOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "BulkWrite deleteOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite deleteOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.yml b/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.yml new file mode 100644 index 0000000000..75a66bf851 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-deleteOne-rawdata.yml @@ -0,0 +1,61 @@ +description: "BulkWrite deleteOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "BulkWrite deleteOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteOne: + filter: &filter {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + rawData: *rawdata + - description: "BulkWrite deleteOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - deleteOne: + filter: &filter {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + rawData: + $$exists: false diff --git a/source/crud/tests/unified/bulkWrite-errorResponse.json b/source/crud/tests/unified/bulkWrite-errorResponse.json new file mode 100644 index 0000000000..157637c713 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-errorResponse.yml b/source/crud/tests/unified/bulkWrite-errorResponse.yml new file mode 100644 index 0000000000..d4f335dfd3 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-errorResponse.yml @@ -0,0 +1,50 @@ +description: "bulkWrite-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + # This test intentionally executes only a single insert operation in the bulk + # write to make the error code and response assertions less ambiguous. That + # said, some drivers may still need to skip this test because the CRUD spec + # does not prescribe how drivers should formulate a BulkWriteException beyond + # collecting write and write concern errors. + - 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: &errorCode 8 # UnknownError + - name: bulkWrite + object: *collection0 + arguments: + requests: + - insertOne: + document: { _id: 1 } + expectError: + errorCode: *errorCode + errorResponse: + code: *errorCode diff --git a/source/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json b/source/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.json new file mode 100644 index 0000000000..92bbb1aaf2 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml b/source/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml new file mode 100644 index 0000000000..de3885ac8c --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-insertOne-dots_and_dollars.yml @@ -0,0 +1,138 @@ +description: "bulkWrite-insertOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey { _id: 1, $a: 1 } + expectResult: &bulkWriteResult + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { 0: 1 } } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + expectEvents: &expectEventsDollarPrefixedKey + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKey + + - 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: *dollarPrefixedKey + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKey + outcome: *initialData + + - description: "Inserting document with top-level dotted key" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - insertOne: + document: &dottedKey { _id: 1, a.b: 1 } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - description: "Inserting document with dollar-prefixed key in embedded doc" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - insertOne: + document: &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - description: "Inserting document with dotted key in embedded doc" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - insertOne: + document: &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json b/source/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.json new file mode 100644 index 0000000000..fce647d8f4 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml b/source/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml new file mode 100644 index 0000000000..65c414c1d6 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-dots_and_dollars.yml @@ -0,0 +1,165 @@ +description: "bulkWrite-replaceOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey { _id: 1, a.b: 1 } + expectResult: &bulkWriteResult + deletedCount: 0 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: { } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + expectEvents: &expectEventsDottedKey + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - 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: *dottedKey + expectError: + isClientError: false + expectEvents: *expectEventsDottedKey + outcome: *initialData + + - 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: &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *bulkWriteResult + expectEvents: &expectEventsDollarPrefixedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - 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: *dollarPrefixedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKeyInEmbedded + outcome: *initialData + + - 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: &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *bulkWriteResult + expectEvents: &expectEventsDottedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded + + - 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: *dottedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDottedKeyInEmbedded + outcome: *initialData diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.json b/source/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.json new file mode 100644 index 0000000000..e54cd704df --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.yml b/source/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..d656c5fc6b --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.yml @@ -0,0 +1,103 @@ +description: bulkWrite-replaceOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + replacement: &replacement { x: 111 } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged replaceOne with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: *filter + replacement: *replacement + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-let.json b/source/crud/tests/unified/bulkWrite-replaceOne-let.json new file mode 100644 index 0000000000..70f63837a8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-replaceOne-let.yml b/source/crud/tests/unified/bulkWrite-replaceOne-let.yml new file mode 100644 index 0000000000..5453868ba4 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-let.yml @@ -0,0 +1,93 @@ +description: "BulkWrite replaceOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$_id", "$$id" ] + replacement: &replacement {"x": 3} + let: &let + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + replacement: *replacement + let: *let + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.json b/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.json new file mode 100644 index 0000000000..50403fc5c3 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.json @@ -0,0 +1,136 @@ +{ + "description": "BulkWrite replaceOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "BulkWrite replaceOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": {}, + "replacement": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": {}, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": {}, + "replacement": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": {}, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.yml b/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.yml new file mode 100644 index 0000000000..60f3af8c9b --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-rawdata.yml @@ -0,0 +1,71 @@ +description: "BulkWrite replaceOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "BulkWrite replaceOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: &filter {} + replacement: &replacement {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: *rawdata + - description: "BulkWrite replaceOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: &filter {} + replacement: &replacement {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: + $$exists: false diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-sort.json b/source/crud/tests/unified/bulkWrite-replaceOne-sort.json new file mode 100644 index 0000000000..c0bd383514 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-sort.json @@ -0,0 +1,239 @@ +{ + "description": "BulkWrite replaceOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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": "BulkWrite replaceOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-replaceOne-sort.yml b/source/crud/tests/unified/bulkWrite-replaceOne-sort.yml new file mode 100644 index 0000000000..6f326fe043 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-replaceOne-sort.yml @@ -0,0 +1,94 @@ +description: BulkWrite replaceOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: BulkWrite replaceOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: BulkWrite replaceOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/bulkWrite-update-hint-clientError.json b/source/crud/tests/unified/bulkWrite-update-hint-clientError.json new file mode 100644 index 0000000000..d5eb71c29e --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-hint-clientError.json @@ -0,0 +1,284 @@ +{ + "description": "bulkWrite-update-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_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 (client-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": [] + } + ], + "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 (client-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": [] + } + ], + "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 (client-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": [] + } + ], + "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/source/crud/tests/unified/bulkWrite-update-hint-clientError.yml b/source/crud/tests/unified/bulkWrite-update-hint-clientError.yml new file mode 100644 index 0000000000..cf75a7c450 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-hint-clientError.yml @@ -0,0 +1,145 @@ +description: bulkWrite-update-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_bulkwrite_update_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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 (client-side error)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateOne: + filter: &updateOne_filter + _id: 1 + update: &updateOne_update + $inc: + x: 1 + hint: &hint_string _id_ + - + updateOne: + filter: *updateOne_filter + update: *updateOne_update + hint: &hint_doc + _id: 1 + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + description: 'BulkWrite updateMany with update hints unsupported (client-side error)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateMany: + filter: &updateMany_filter + _id: + $lt: 3 + update: &updateMany_update + $inc: + x: 1 + hint: *hint_string + - + updateMany: + filter: *updateMany_filter + update: *updateMany_update + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome + - + description: 'BulkWrite replaceOne with update hints unsupported (client-side error)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + replaceOne: + filter: + _id: 3 + replacement: + x: 333 + hint: *hint_string + - + replaceOne: + filter: + _id: 4 + replacement: + x: 444 + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/bulkWrite-update-hint-serverError.json b/source/crud/tests/unified/bulkWrite-update-hint-serverError.json new file mode 100644 index 0000000000..b0f7e1b381 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-update-hint-serverError.yml b/source/crud/tests/unified/bulkWrite-update-hint-serverError.yml new file mode 100644 index 0000000000..64310a2eaf --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-hint-serverError.yml @@ -0,0 +1,236 @@ +description: bulkWrite-update-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.1.9 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_bulkwrite_update_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &updateOne_filter + _id: 1 + update: &updateOne_update + $inc: + x: 1 + hint: &hint_string _id_ + - + updateOne: + filter: *updateOne_filter + update: *updateOne_update + hint: &hint_doc + _id: 1 + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *updateOne_filter + u: *updateOne_update + hint: *hint_string + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - + q: *updateOne_filter + u: *updateOne_update + hint: *hint_doc + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + 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: &updateMany_filter + _id: + $lt: 3 + update: &updateMany_update + $inc: + x: 1 + hint: *hint_string + - + updateMany: + filter: *updateMany_filter + update: *updateMany_update + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *updateMany_filter + u: *updateMany_update + multi: true + hint: *hint_string + upsert: { $$unsetOrMatches: false } + - + q: *updateMany_filter + u: *updateMany_update + multi: true + hint: *hint_doc + upsert: { $$unsetOrMatches: false } + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + 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: *hint_string + - + replaceOne: + filter: + _id: 4 + replacement: + x: 444 + hint: *hint_doc + ordered: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: + _id: 3 + u: + x: 333 + hint: *hint_string + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - + q: + _id: 4 + u: + x: 444 + hint: *hint_doc + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 diff --git a/source/crud/tests/unified/bulkWrite-update-hint.json b/source/crud/tests/unified/bulkWrite-update-hint.json new file mode 100644 index 0000000000..4206359891 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-update-hint.yml b/source/crud/tests/unified/bulkWrite-update-hint.yml new file mode 100644 index 0000000000..e739074ea2 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-hint.yml @@ -0,0 +1,253 @@ +description: bulkWrite-update-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_bulkwrite_update_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &updateOne_filter + _id: 1 + update: &updateOne_update + $inc: + x: 1 + hint: &hint_string _id_ + - + updateOne: + filter: *updateOne_filter + update: *updateOne_update + hint: &hint_doc + _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: *collection_name + updates: + - + q: *updateOne_filter + u: *updateOne_update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + hint: *hint_string + - + q: *updateOne_filter + u: *updateOne_update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + hint: *hint_doc + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + 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: &updateMany_filter + _id: + $lt: 3 + update: &updateMany_update + $inc: + x: 1 + hint: *hint_string + - + updateMany: + filter: *updateMany_filter + update: *updateMany_update + hint: *hint_doc + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: {} } + matchedCount: 4 + modifiedCount: 4 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *updateMany_filter + u: *updateMany_update + multi: true + upsert: { $$unsetOrMatches: false } + hint: *hint_string + - + q: *updateMany_filter + u: *updateMany_update + multi: true + upsert: { $$unsetOrMatches: false } + hint: *hint_doc + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + 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: *hint_string + - + replaceOne: + filter: + _id: 4 + replacement: + x: 444 + hint: *hint_doc + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: {} } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: + _id: 3 + u: + x: 333 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + hint: *hint_string + - + q: + _id: 4 + u: + x: 444 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + hint: *hint_doc + ordered: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 333 + - + _id: 4 + x: 444 diff --git a/source/crud/tests/unified/bulkWrite-update-validation.json b/source/crud/tests/unified/bulkWrite-update-validation.json new file mode 100644 index 0000000000..f9bfda0edd --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-validation.json @@ -0,0 +1,210 @@ +{ + "description": "bulkWrite-update-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": "BulkWrite replaceOne prohibits atomic modifiers", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "replaceOne": { + "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 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite updateOne requires atomic modifiers", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "x": 22 + } + } + } + ] + }, + "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 + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany requires atomic modifiers", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "updateMany": { + "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/source/crud/tests/unified/bulkWrite-update-validation.yml b/source/crud/tests/unified/bulkWrite-update-validation.yml new file mode 100644 index 0000000000..57defd56a4 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-update-validation.yml @@ -0,0 +1,73 @@ +description: "bulkWrite-update-validation" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "BulkWrite replaceOne prohibits atomic modifiers" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - replaceOne: + filter: { _id: 1 } + replacement: { $set: { x: 22 } } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData + + - description: "BulkWrite updateOne requires atomic modifiers" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - updateOne: + filter: { _id: 1 } + update: { x: 22 } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData + + - description: "BulkWrite updateMany requires atomic modifiers" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - updateMany: + filter: { _id: { $gt: 1 } } + update: { x: 44 } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData diff --git a/source/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json b/source/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.json new file mode 100644 index 0000000000..35a5cdd52a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml b/source/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml new file mode 100644 index 0000000000..68cebd09cf --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-dots_and_dollars.yml @@ -0,0 +1,150 @@ +description: "bulkWrite-updateMany-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey + - { $replaceWith: { $setField: { field: { $literal: $a }, value: 1, input: $$ROOT } } } + expectResult: &bulkWriteResult + deletedCount: 0 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: { } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKey + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey + - { $replaceWith: { $setField: { field: { $literal: a.b }, value: 1, input: $$ROOT } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: $a }, value: 1, input: $foo } } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: a.b }, value: 1, input: $foo } } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, foo: { a.b: 1 } } diff --git a/source/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.json b/source/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.json new file mode 100644 index 0000000000..87478918d2 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.yml b/source/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.yml new file mode 100644 index 0000000000..7cb376c90c --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.yml @@ -0,0 +1,104 @@ +description: bulkWrite-updateMany-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + update: &update { $inc: { x: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + update: *update + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged updateMany with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateMany: + filter: *filter + update: *update + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + 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: *filter + update: *update + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/bulkWrite-updateMany-let.json b/source/crud/tests/unified/bulkWrite-updateMany-let.json new file mode 100644 index 0000000000..fbeba1a607 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateMany-let.yml b/source/crud/tests/unified/bulkWrite-updateMany-let.yml new file mode 100644 index 0000000000..f67d000334 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-let.yml @@ -0,0 +1,96 @@ +description: "BulkWrite updateMany-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$_id", "$$id" ] + update: &update + - $set: + x: 21 + let: &let + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + update: *update + let: *let + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + let: *let + + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 20 } + - { _id: 2, x: 21 } diff --git a/source/crud/tests/unified/bulkWrite-updateMany-pipeline.json b/source/crud/tests/unified/bulkWrite-updateMany-pipeline.json new file mode 100644 index 0000000000..e938ea7535 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-pipeline.json @@ -0,0 +1,148 @@ +{ + "description": "bulkWrite-updateMany-pipeline", + "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": "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/source/crud/tests/unified/bulkWrite-updateMany-pipeline.yml b/source/crud/tests/unified/bulkWrite-updateMany-pipeline.yml new file mode 100644 index 0000000000..d6503e2aa4 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-pipeline.yml @@ -0,0 +1,67 @@ +description: bulkWrite-updateMany-pipeline + +schemaVersion: '1.0' + +runOnRequirements: + - minServerVersion: 4.1.11 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, y: 1, t: { u: { v: 1 } } } + - { _id: 2, x: 2, y: 1 } + +tests: + - + 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: *collection_name + updates: + - q: { } + u: + - { $project: { x: 1 } } + - { $addFields: { foo: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + commandName: update + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, foo: 1 } + - { _id: 2, x: 2, foo: 1 } diff --git a/source/crud/tests/unified/bulkWrite-updateMany-rawdata.json b/source/crud/tests/unified/bulkWrite-updateMany-rawdata.json new file mode 100644 index 0000000000..4f807ae42a --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-rawdata.json @@ -0,0 +1,157 @@ +{ + "description": "BulkWrite updateMany-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "BulkWrite updateMany with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite updateMany with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-updateMany-rawdata.yml b/source/crud/tests/unified/bulkWrite-updateMany-rawdata.yml new file mode 100644 index 0000000000..ac5d67fb45 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateMany-rawdata.yml @@ -0,0 +1,72 @@ +description: "BulkWrite updateMany-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "BulkWrite updateMany with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateMany: + filter: &filter {} + update: &update + - $set: + x: 1 + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + rawData: *rawdata + - description: "BulkWrite updateMany with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "4.2.0" + maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateMany: + filter: &filter {} + update: &update + - $set: + x: 1 + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + rawData: + $$exists: false diff --git a/source/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json b/source/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.json new file mode 100644 index 0000000000..cbbe113ce8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml b/source/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml new file mode 100644 index 0000000000..c5a1662db1 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-dots_and_dollars.yml @@ -0,0 +1,150 @@ +description: "bulkWrite-updateOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey + - { $replaceWith: { $setField: { field: { $literal: $a }, value: 1, input: $$ROOT } } } + expectResult: &bulkWriteResult + deletedCount: 0 + insertedCount: 0 + insertedIds: { $$unsetOrMatches: { } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey + - { $replaceWith: { $setField: { field: { $literal: a.b }, value: 1, input: $$ROOT } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: $a }, value: 1, input: $foo } } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: a.b }, value: 1, input: $foo } } } } + expectResult: *bulkWriteResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, foo: { a.b: 1 } } diff --git a/source/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.json b/source/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.json new file mode 100644 index 0000000000..1345f6b536 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.yml b/source/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..5997c54c2b --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.yml @@ -0,0 +1,103 @@ +description: bulkWrite-updateOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + update: &update { $inc: { x: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + update: *update + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged updateOne with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: *filter + update: *update + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + 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: *filter + update: *update + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/bulkWrite-updateOne-let.json b/source/crud/tests/unified/bulkWrite-updateOne-let.json new file mode 100644 index 0000000000..96783c782f --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/bulkWrite-updateOne-let.yml b/source/crud/tests/unified/bulkWrite-updateOne-let.yml new file mode 100644 index 0000000000..dc3cd2b599 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-let.yml @@ -0,0 +1,95 @@ +description: "BulkWrite updateOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$_id", "$$id" ] + update: &update + - $set: + x: 22 + let: &let + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + update: *update + let: *let + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 20 } + - { _id: 2, x: 21 } diff --git a/source/crud/tests/unified/bulkWrite-updateOne-pipeline.json b/source/crud/tests/unified/bulkWrite-updateOne-pipeline.json new file mode 100644 index 0000000000..769bd106f8 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-pipeline.json @@ -0,0 +1,156 @@ +{ + "description": "bulkWrite-updateOne-pipeline", + "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 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 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-updateOne-pipeline.yml b/source/crud/tests/unified/bulkWrite-updateOne-pipeline.yml new file mode 100644 index 0000000000..9e15c0bfa9 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-pipeline.yml @@ -0,0 +1,66 @@ +description: bulkWrite-updateOne-pipeline + +schemaVersion: '1.0' + +runOnRequirements: + - minServerVersion: 4.1.11 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, y: 1, t: { u: { v: 1 } } } + - { _id: 2, x: 2, y: 1 } + +tests: + - + 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: *collection_name + updates: + - q: { _id: 1 } + u: + - { $replaceRoot: { newRoot: $t } } + - { $addFields: { foo: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + commandName: update + databaseName: *database_name + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, u: { v: 1 }, foo: 1 } + - { _id: 2, x: 2, y: 1 } diff --git a/source/crud/tests/unified/bulkWrite-updateOne-rawdata.json b/source/crud/tests/unified/bulkWrite-updateOne-rawdata.json new file mode 100644 index 0000000000..32b53cbf29 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-rawdata.json @@ -0,0 +1,161 @@ +{ + "description": "BulkWrite updateOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite updateOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-updateOne-rawdata.yml b/source/crud/tests/unified/bulkWrite-updateOne-rawdata.yml new file mode 100644 index 0000000000..86415a5412 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-rawdata.yml @@ -0,0 +1,76 @@ +description: "BulkWrite updateOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "BulkWrite updateOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: &filter {} + update: &update + - $set: + x: 1 + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: *rawdata + - description: "BulkWrite updateOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "4.2.0" + maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: &filter {} + update: &update + - $set: + x: 1 + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: + $$exists: false diff --git a/source/crud/tests/unified/bulkWrite-updateOne-sort.json b/source/crud/tests/unified/bulkWrite-updateOne-sort.json new file mode 100644 index 0000000000..f78bd3bf3e --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-sort.json @@ -0,0 +1,255 @@ +{ + "description": "BulkWrite updateOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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": "BulkWrite updateOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "BulkWrite updateOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite-updateOne-sort.yml b/source/crud/tests/unified/bulkWrite-updateOne-sort.yml new file mode 100644 index 0000000000..72bc814d69 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite-updateOne-sort.yml @@ -0,0 +1,94 @@ +description: BulkWrite updateOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: BulkWrite updateOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: [ $set: { x: 1 } ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: [ $set: { x: 1 } ] + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: BulkWrite updateOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: [ $set: { x: 1 } ] + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: [ $set: { x: 1 } ] + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/bulkWrite.json b/source/crud/tests/unified/bulkWrite.json new file mode 100644 index 0000000000..59b33cbac5 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite.json @@ -0,0 +1,829 @@ +{ + "description": "bulkWrite", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite with deleteOne operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 2 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "BulkWrite with deleteMany operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "x": { + "$lt": 11 + } + } + } + }, + { + "deleteMany": { + "filter": { + "x": { + "$lte": 22 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 2, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [] + } + ] + }, + { + "description": "BulkWrite with insertOne operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "1": 4 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite with replaceOne operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "x": 33 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 12 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "x": 33 + }, + "upsert": true + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 1, + "upsertedIds": { + "2": 3 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite with updateOne operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 0 + }, + "update": { + "$set": { + "x": 0 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 11 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "x": 33 + } + }, + "upsert": true + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 2, + "modifiedCount": 1, + "upsertedCount": 1, + "upsertedIds": { + "3": 3 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite with updateMany operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "x": { + "$lt": 11 + } + }, + "update": { + "$set": { + "x": 0 + } + } + } + }, + { + "updateMany": { + "filter": { + "x": { + "$lte": 22 + } + }, + "update": { + "$unset": { + "y": 1 + } + } + } + }, + { + "updateMany": { + "filter": { + "x": { + "$lte": 22 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "x": 33 + } + }, + "upsert": true + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 4, + "modifiedCount": 2, + "upsertedCount": 1, + "upsertedIds": { + "3": 3 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite with mixed ordered operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "deleteMany": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 2, + "insertedCount": 2, + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "3": 4 + } + }, + "matchedCount": 3, + "modifiedCount": 3, + "upsertedCount": 1, + "upsertedIds": { + "5": 4 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 34 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite with mixed unordered operations", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "_id": 3, + "x": 33 + }, + "upsert": true + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": false + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 1, + "upsertedIds": { + "0": 3 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite continue-on-error behavior with unordered (preexisting duplicate key)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "ordered": false + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "BulkWrite continue-on-error behavior with unordered (duplicate key in requests)", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "ordered": false + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bulkWrite.yml b/source/crud/tests/unified/bulkWrite.yml new file mode 100644 index 0000000000..677e6725c4 --- /dev/null +++ b/source/crud/tests/unified/bulkWrite.yml @@ -0,0 +1,448 @@ +description: bulkWrite + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '2.6' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'BulkWrite with deleteOne operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + # Note: as in the "DeleteOne when many documents match" test in + # deleteOne.yml, we omit a deleteOne operation that might match + # multiple documents as that would hinder our ability to assert the + # final state of the collection under test. + requests: + - + # does not match an existing document + deleteOne: + filter: { _id: 3 } + - + # deletes the matched document + deleteOne: + filter: { _id: 2 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'BulkWrite with deleteMany operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + # does not match any existing documents + deleteMany: + filter: { x: { $lt: 11 } } + - + # deletes the matched documents + deleteMany: + filter: { x: { $lte: 22 } } + ordered: true + expectResult: + deletedCount: 2 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'BulkWrite with insertOne operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 3, x: 33 } + - + insertOne: + document: { _id: 4, x: 44 } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 2 + insertedIds: + $$unsetOrMatches: + '0': 3 + '1': 4 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'BulkWrite with replaceOne operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + # Note: as in the "ReplaceOne when many documents match" test in + # replaceOne.yml, we omit a replaceOne operation that might match + # multiple documents as that would hinder our ability to assert the + # final state of the collection under test. + requests: + - + # does not match an existing document + replaceOne: + filter: { _id: 3 } + replacement: { x: 33 } + - + # modifies the matched document + replaceOne: + filter: { _id: 1 } + replacement: { x: 12 } + - + # does not match an existing document and upserts + replaceOne: + filter: { _id: 3 } + replacement: { x: 33 } + upsert: true + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 1 + upsertedIds: { '2': 3 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite with updateOne operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + # Note: as in the "UpdateOne when many documents match" test in + # updateOne.yml, we omit an updateOne operation that might match + # multiple documents as that would hinder our ability to assert the + # final state of the collection under test. + requests: + - + # does not match an existing document + updateOne: + filter: { _id: 0 } + update: { $set: { x: 0 } } + - + # does not modify the matched document + updateOne: + filter: { _id: 1 } + update: { $set: { x: 11 } } + - + # modifies the matched document + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + # does not match an existing document and upserts + updateOne: + filter: { _id: 3 } + update: { $set: { x: 33 } } + upsert: true + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 2 + modifiedCount: 1 + upsertedCount: 1 + upsertedIds: { '3': 3 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite with updateMany operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + # does not match any existing documents + updateMany: + filter: { x: { $lt: 11 } } + update: { $set: { x: 0 } } + - + # does not modify the matched documents + updateMany: + filter: { x: { $lte: 22 } } + update: { $unset: { y: 1 } } + - + # modifies the matched documents + updateMany: + filter: { x: { $lte: 22 } } + update: { $inc: { x: 1 } } + - + # does not match any existing documents and upserts + updateMany: + filter: { _id: 3 } + update: { $set: { x: 33 } } + upsert: true + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 4 + modifiedCount: 2 + upsertedCount: 1 + upsertedIds: { '3': 3 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite with mixed ordered operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + updateMany: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 4, x: 44 } + - + deleteMany: + filter: { x: { $nin: [ 24, 34 ] } } + - + replaceOne: + filter: { _id: 4 } + replacement: { _id: 4, x: 44 } + upsert: true + ordered: true + expectResult: + deletedCount: 2 + insertedCount: 2 + insertedIds: + $$unsetOrMatches: + '0': 3 + '3': 4 + matchedCount: 3 + modifiedCount: 3 + upsertedCount: 1 + upsertedIds: { '5': 4 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 24 } + - { _id: 3, x: 34 } + - { _id: 4, x: 44 } + - + description: 'BulkWrite with mixed unordered operations' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + # We omit inserting multiple documents and updating documents that may + # not exist at the start of this test as we cannot assume the order in + # which the operations will execute. + requests: + - + replaceOne: + filter: { _id: 3 } + replacement: { _id: 3, x: 33 } + upsert: true + - + deleteOne: + filter: { _id: 1 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: false + expectResult: + deletedCount: 1 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 1 + upsertedIds: { '0': 3 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite continue-on-error behavior with unordered (preexisting duplicate key)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + insertOne: + document: { _id: 3, x: 33 } + - + insertOne: + document: { _id: 4, x: 44 } + ordered: false + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 2 + # Since the map of insertedIds is generated before execution it + # could indicate inserts that did not actually succeed. We omit this + # field rather than expect drivers to provide an accurate map + # filtered by write errors. + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'BulkWrite continue-on-error behavior with unordered (duplicate key in requests)' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 3, x: 33 } + - + insertOne: + document: { _id: 3, x: 33 } + - + insertOne: + document: { _id: 4, x: 44 } + ordered: false + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 2 + # Since the map of insertedIds is generated before execution it + # could indicate inserts that did not actually succeed. We omit this + # field rather than expect drivers to provide an accurate map + # filtered by write errors. + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } diff --git a/source/crud/tests/unified/bypassDocumentValidation.json b/source/crud/tests/unified/bypassDocumentValidation.json new file mode 100644 index 0000000000..aff2d37f81 --- /dev/null +++ b/source/crud/tests/unified/bypassDocumentValidation.json @@ -0,0 +1,493 @@ +{ + "description": "bypassDocumentValidation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.2", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $out passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_test_collection" + } + ], + "bypassDocumentValidation": false + }, + "commandName": "aggregate", + "databaseName": "crud" + } + } + ] + } + ] + }, + { + "description": "BulkWrite passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 4, + "x": 44 + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "FindOneAndReplace passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 32 + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "x": 32 + }, + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll", + "query": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "InsertMany passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 4, + "x": 44 + } + ], + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 4, + "x": 44 + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "InsertOne passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 4, + "x": 44 + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 4, + "x": 44 + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "ReplaceOne passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 32 + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 32 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "UpdateMany passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + }, + { + "description": "UpdateOne passes bypassDocumentValidation: false", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "bypassDocumentValidation": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "bypassDocumentValidation": false + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/bypassDocumentValidation.yml b/source/crud/tests/unified/bypassDocumentValidation.yml new file mode 100644 index 0000000000..5082564752 --- /dev/null +++ b/source/crud/tests/unified/bypassDocumentValidation.yml @@ -0,0 +1,222 @@ +description: bypassDocumentValidation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.2' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'Aggregate with $out passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: &pipeline + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: other_test_collection } + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + bypassDocumentValidation: false + commandName: aggregate + databaseName: *database_name + - + description: 'BulkWrite passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: &inserted_document { _id: 4, x: 44 } + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + bypassDocumentValidation: false + - + description: 'FindOneAndReplace passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: &filter { _id: { $gt: 1 } } + replacement: &replacement { x: 32 } + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *replacement + bypassDocumentValidation: false + - + description: 'FindOneAndUpdate passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: *filter + update: &update { $inc: { x: 1 } } + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *update + bypassDocumentValidation: false + - + description: 'InsertMany passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: insertMany + arguments: + documents: + - *inserted_document + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + bypassDocumentValidation: false + - + description: 'InsertOne passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: insertOne + arguments: + document: *inserted_document + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + bypassDocumentValidation: false + - + description: 'ReplaceOne passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: *filter + replacement: *replacement + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + bypassDocumentValidation: false + - + description: 'UpdateMany passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: *filter + update: *update + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + bypassDocumentValidation: false + - + description: 'UpdateOne passes bypassDocumentValidation: false' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: *filter + update: *update + bypassDocumentValidation: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + bypassDocumentValidation: false diff --git a/source/crud/tests/unified/client-bulkWrite-delete-options.json b/source/crud/tests/unified/client-bulkWrite-delete-options.json new file mode 100644 index 0000000000..d9987897dc --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-delete-options.json @@ -0,0 +1,268 @@ +{ + "description": "client bulkWrite delete options", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "collation": { + "locale": "simple" + }, + "hint": "_id_" + }, + "tests": [ + { + "description": "client bulk write delete with collation", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "collation": { + "locale": "simple" + } + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "collation": { + "locale": "simple" + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 3, + "insertResults": {}, + "updateResults": {}, + "deleteResults": { + "0": { + "deletedCount": 1 + }, + "1": { + "deletedCount": 2 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "delete": 0, + "filter": { + "_id": 1 + }, + "collation": { + "locale": "simple" + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "collation": { + "locale": "simple" + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [] + } + ] + }, + { + "description": "client bulk write delete with hint", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "hint": "_id_" + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_" + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 3, + "insertResults": {}, + "updateResults": {}, + "deleteResults": { + "0": { + "deletedCount": 1 + }, + "1": { + "deletedCount": 2 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "delete": 0, + "filter": { + "_id": 1 + }, + "hint": "_id_", + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "hint": "_id_", + "multi": true + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-delete-options.yml b/source/crud/tests/unified/client-bulkWrite-delete-options.yml new file mode 100644 index 0000000000..9297838535 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-delete-options.yml @@ -0,0 +1,137 @@ +description: "client bulkWrite delete options" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + collation: &collation { "locale": "simple" } + hint: &hint _id_ + +tests: + - description: "client bulk write delete with collation" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: { _id: 1 } + collation: *collation + - deleteMany: + namespace: *namespace + filter: { _id: { $gt: 1 } } + collation: *collation + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 3 + insertResults: {} + updateResults: {} + deleteResults: + 0: + deletedCount: 1 + 1: + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - delete: 0 + filter: { _id: 1 } + collation: *collation + multi: false + - delete: 0 + filter: { _id: { $gt: 1 } } + collation: *collation + multi: true + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + - description: "client bulk write delete with hint" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: { _id: 1 } + hint: *hint + - deleteMany: + namespace: *namespace + filter: { _id: { $gt: 1 } } + hint: *hint + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 3 + insertResults: {} + updateResults: {} + deleteResults: + 0: + deletedCount: 1 + 1: + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - delete: 0 + filter: { _id: 1 } + hint: *hint + multi: false + - delete: 0 + filter: { _id: { $gt: 1 } } + hint: *hint + multi: true + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] diff --git a/source/crud/tests/unified/client-bulkWrite-delete-rawdata.json b/source/crud/tests/unified/client-bulkWrite-delete-rawdata.json new file mode 100644 index 0000000000..a9f476cfa4 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-delete-rawdata.json @@ -0,0 +1,171 @@ +{ + "description": "client bulkWrite delete-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "filter": {} + }, + "tests": [ + { + "description": "client bulk write delete with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": {} + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ordered": true, + "ops": [ + { + "delete": 0, + "filter": {}, + "multi": false + }, + { + "delete": 0, + "filter": {}, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "client bulk write delete with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "maxServerVersion": "8.1.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": {} + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": {} + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ordered": true, + "ops": [ + { + "delete": 0, + "filter": {}, + "multi": false + }, + { + "delete": 0, + "filter": {}, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-delete-rawdata.yml b/source/crud/tests/unified/client-bulkWrite-delete-rawdata.yml new file mode 100644 index 0000000000..9c7e9dc3aa --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-delete-rawdata.yml @@ -0,0 +1,94 @@ +description: "client bulkWrite delete-rawData" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + filter: &filter {} + +tests: + - description: "client bulk write delete with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: *filter + - deleteMany: + namespace: *namespace + filter: *filter + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ordered: true + ops: + - delete: 0 + filter: *filter + multi: false + - delete: 0 + filter: *filter + multi: true + nsInfo: + - ns: *namespace + rawData: *rawdata + - description: "client bulk write delete with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "8.0" + maxServerVersion: "8.1.99" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: *filter + - deleteMany: + namespace: *namespace + filter: *filter + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ordered: true + ops: + - delete: 0 + filter: *filter + multi: false + - delete: 0 + filter: *filter + multi: true + nsInfo: + - ns: *namespace + rawData: + $$exists: false diff --git a/source/crud/tests/unified/client-bulkWrite-errorResponse.json b/source/crud/tests/unified/client-bulkWrite-errorResponse.json new file mode 100644 index 0000000000..b828aad3b9 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-errorResponse.json @@ -0,0 +1,69 @@ +{ + "description": "client bulkWrite errorResponse", + "schemaVersion": "1.12", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite operations support errorResponse assertions", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 8 + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorCode": 8, + "errorResponse": { + "code": 8 + } + } + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-errorResponse.yml b/source/crud/tests/unified/client-bulkWrite-errorResponse.yml new file mode 100644 index 0000000000..d63010afc7 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-errorResponse.yml @@ -0,0 +1,38 @@ +description: "client bulkWrite errorResponse" +schemaVersion: "1.12" +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false # Avoid setting fail points with multiple mongoses + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite operations support errorResponse assertions" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ bulkWrite ] + errorCode: &errorCode 8 # UnknownError + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 1 } + expectError: + errorCode: *errorCode + errorResponse: + code: *errorCode diff --git a/source/crud/tests/unified/client-bulkWrite-errors.json b/source/crud/tests/unified/client-bulkWrite-errors.json new file mode 100644 index 0000000000..015bd95c99 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-errors.json @@ -0,0 +1,513 @@ +{ + "description": "client bulkWrite errors", + "schemaVersion": "1.21", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "uriOptions": { + "retryWrites": false + }, + "useMultipleMongoses": false + } + }, + { + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "writeConcernErrorCode": 91, + "writeConcernErrorMessage": "Replication is being shut down", + "undefinedVarCode": 17276 + }, + "tests": [ + { + "description": "an individual operation fails during an ordered bulkWrite", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 1, + "insertResults": {}, + "updateResults": {}, + "deleteResults": { + "0": { + "deletedCount": 1 + } + } + }, + "writeErrors": { + "1": { + "code": 17276 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "an individual operation fails during an unordered bulkWrite", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true, + "ordered": false + }, + "expectError": { + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 2, + "insertResults": {}, + "updateResults": {}, + "deleteResults": { + "0": { + "deletedCount": 1 + }, + "2": { + "deletedCount": 1 + } + } + }, + "writeErrors": { + "1": { + "code": 17276 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "detailed results are omitted from error when verboseResults is false", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": false + }, + "expectError": { + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 1, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + }, + "writeErrors": { + "1": { + "code": 17276 + } + } + } + } + ] + }, + { + "description": "a top-level failure occurs during a bulkWrite", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 8 + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "x": 1 + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "errorCode": 8 + } + } + ] + }, + { + "description": "a bulk write with only errors does not report a partial result", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "expectResult": { + "$$unsetOrMatches": {} + }, + "writeErrors": { + "0": { + "code": 17276 + } + } + } + } + ] + }, + { + "description": "a write concern error occurs during a bulkWrite", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 10 + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 10 + } + }, + "updateResults": {}, + "deleteResults": {} + }, + "writeConcernErrors": [ + { + "code": 91, + "message": "Replication is being shut down" + } + ] + } + } + ] + }, + { + "description": "an empty list of write models is a client-side error", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [], + "verboseResults": true + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "Requesting unacknowledged write with verboseResults is a client-side error", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 10 + } + } + } + ], + "verboseResults": true, + "ordered": false, + "writeConcern": { + "w": 0 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "Cannot request unacknowledged write concern and verbose results" + } + } + ] + }, + { + "description": "Requesting unacknowledged write with ordered is a client-side error", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 10 + } + } + } + ], + "writeConcern": { + "w": 0 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "Cannot request unacknowledged write concern and ordered writes" + } + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-errors.yml b/source/crud/tests/unified/client-bulkWrite-errors.yml new file mode 100644 index 0000000000..79c0496161 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-errors.yml @@ -0,0 +1,270 @@ +description: "client bulkWrite errors" +schemaVersion: "1.21" +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + uriOptions: + retryWrites: false + useMultipleMongoses: false # Target a single mongos with failpoint + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + writeConcernErrorCode: &writeConcernErrorCode 91 + writeConcernErrorMessage: &writeConcernErrorMessage "Replication is being shut down" + undefinedVarCode: &undefinedVarCode 17276 # Use of an undefined variable + +tests: + - description: "an individual operation fails during an ordered bulkWrite" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: { _id: 1 } + - deleteOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id2" ] # Attempt to access a nonexistent let var + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + expectError: + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 1 + insertResults: {} + updateResults: {} + deleteResults: + 0: + deletedCount: 1 + writeErrors: + 1: + code: *undefinedVarCode + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - description: "an individual operation fails during an unordered bulkWrite" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: { _id: 1 } + - deleteOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id2" ] # Attempt to access a nonexistent let var + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + ordered: false + expectError: + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 2 + insertResults: {} + updateResults: {} + deleteResults: + 0: + deletedCount: 1 + 2: + deletedCount: 1 + writeErrors: + 1: + code: *undefinedVarCode + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 2, x: 22 } + - description: "detailed results are omitted from error when verboseResults is false" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: { _id: 1 } + - deleteOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id2" ] # Attempt to access a nonexistent let var + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: false + expectError: + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 1 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + writeErrors: + 1: + code: *undefinedVarCode + - description: "a top-level failure occurs during a bulkWrite" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - bulkWrite + errorCode: 8 # UnknownError + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { x: 1 } + verboseResults: true + expectError: + errorCode: 8 + - description: "a bulk write with only errors does not report a partial result" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - deleteOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id2" ] # Attempt to access a nonexistent let var + verboseResults: true + expectError: + expectResult: + $$unsetOrMatches: {} # Empty or nonexistent result when no successful writes occurred + writeErrors: + 0: + code: *undefinedVarCode + - description: "a write concern error occurs during a bulkWrite" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - bulkWrite + writeConcernError: + code: *writeConcernErrorCode + errmsg: *writeConcernErrorMessage + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 10 } + verboseResults: true + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 10 + updateResults: {} + deleteResults: {} + writeConcernErrors: + - code: *writeConcernErrorCode + message: *writeConcernErrorMessage + - description: "an empty list of write models is a client-side error" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: [] + verboseResults: true + expectError: + isClientError: true + - description: "Requesting unacknowledged write with verboseResults is a client-side error" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 10 } + verboseResults: true + ordered: false + writeConcern: { w: 0 } + expectError: + isClientError: true + errorContains: "Cannot request unacknowledged write concern and verbose results" + - description: "Requesting unacknowledged write with ordered is a client-side error" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 10 } + # Omit `ordered` option. Defaults to true. + writeConcern: { w: 0 } + expectError: + isClientError: true + errorContains: "Cannot request unacknowledged write concern and ordered writes" diff --git a/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.json b/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.json new file mode 100644 index 0000000000..55f0618923 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.json @@ -0,0 +1,315 @@ +{ + "description": "client bulkWrite with mixed namespaces", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "db0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "db1" + } + }, + { + "collection": { + "id": "collection2", + "database": "database1", + "collectionName": "coll2" + } + } + ], + "initialData": [ + { + "databaseName": "db0", + "collectionName": "coll0", + "documents": [] + }, + { + "databaseName": "db0", + "collectionName": "coll1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + }, + { + "databaseName": "db1", + "collectionName": "coll2", + "documents": [ + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "_yamlAnchors": { + "db0Coll0Namespace": "db0.coll0", + "db0Coll1Namespace": "db0.coll1", + "db1Coll2Namespace": "db1.coll2" + }, + "tests": [ + { + "description": "client bulkWrite with mixed namespaces", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "db0.coll0", + "document": { + "_id": 1 + } + } + }, + { + "insertOne": { + "namespace": "db0.coll0", + "document": { + "_id": 2 + } + } + }, + { + "updateOne": { + "namespace": "db0.coll1", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "namespace": "db1.coll2", + "filter": { + "_id": 3 + } + } + }, + { + "deleteOne": { + "namespace": "db0.coll1", + "filter": { + "_id": 2 + } + } + }, + { + "replaceOne": { + "namespace": "db1.coll2", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 45 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 2, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 2, + "insertResults": { + "0": { + "insertedId": 1 + }, + "1": { + "insertedId": 2 + } + }, + "updateResults": { + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "5": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "3": { + "deletedCount": 1 + }, + "4": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "bulkWrite": 1, + "ops": [ + { + "insert": 0, + "document": { + "_id": 1 + } + }, + { + "insert": 0, + "document": { + "_id": 2 + } + }, + { + "update": 1, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "delete": 2, + "filter": { + "_id": 3 + }, + "multi": false + }, + { + "delete": 1, + "filter": { + "_id": 2 + }, + "multi": false + }, + { + "update": 2, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 45 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "db0.coll0" + }, + { + "ns": "db0.coll1" + }, + { + "ns": "db1.coll2" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "db0", + "collectionName": "coll0", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + { + "databaseName": "db0", + "collectionName": "coll1", + "documents": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "databaseName": "db1", + "collectionName": "coll2", + "documents": [ + { + "_id": 4, + "x": 45 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.yml b/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.yml new file mode 100644 index 0000000000..9788bce8c5 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-mixed-namespaces.yml @@ -0,0 +1,147 @@ +description: "client bulkWrite with mixed namespaces" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + - database: + id: &database1 database1 + client: *client0 + databaseName: &database1Name db1 + - collection: + id: &collection2 collection2 + database: *database1 + collectionName: &collection2Name coll2 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + - databaseName: *database0Name + collectionName: *collection1Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - databaseName: *database1Name + collectionName: *collection2Name + documents: + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + +_yamlAnchors: + db0Coll0Namespace: &db0Coll0Namespace "db0.coll0" + db0Coll1Namespace: &db0Coll1Namespace "db0.coll1" + db1Coll2Namespace: &db1Coll2Namespace "db1.coll2" + +tests: + - description: "client bulkWrite with mixed namespaces" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *db0Coll0Namespace + document: { _id: 1 } + - insertOne: + namespace: *db0Coll0Namespace + document: { _id: 2 } + - updateOne: + namespace: *db0Coll1Namespace + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - deleteOne: + namespace: *db1Coll2Namespace + filter: { _id: 3 } + - deleteOne: + namespace: *db0Coll1Namespace + filter: { _id: 2 } + - replaceOne: + namespace: *db1Coll2Namespace + filter: { _id: 4 } + replacement: { x: 45 } + verboseResults: true + expectResult: + insertedCount: 2 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 2 + insertResults: + 0: + insertedId: 1 + 1: + insertedId: 2 + updateResults: + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 5: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 3: + deletedCount: 1 + 4: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + bulkWrite: 1 + ops: + - insert: 0 + document: { _id: 1 } + - insert: 0 + document: { _id: 2 } + - update: 1 + filter: { _id: 1 } + updateMods: { $inc: { x: 1 } } + multi: false + - delete: 2 + filter: { _id: 3 } + multi: false + - delete: 1 + filter: { _id: 2 } + multi: false + - update: 2 + filter: { _id: 4 } + updateMods: { x: 45 } + multi: false + nsInfo: + - ns: *db0Coll0Namespace + - ns: *db0Coll1Namespace + - ns: *db1Coll2Namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1 } + - { _id: 2 } + - databaseName: *database0Name + collectionName: *collection1Name + documents: + - { _id: 1, x: 12 } + - databaseName: *database1Name + collectionName: *collection2Name + documents: + - { _id: 4, x: 45 } diff --git a/source/crud/tests/unified/client-bulkWrite-options.json b/source/crud/tests/unified/client-bulkWrite-options.json new file mode 100644 index 0000000000..708fe4e85b --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-options.json @@ -0,0 +1,716 @@ +{ + "description": "client bulkWrite top-level options", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "client": { + "id": "writeConcernClient", + "uriOptions": { + "w": 1 + }, + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "comment": { + "bulk": "write" + }, + "let": { + "id1": 1, + "id2": 2 + }, + "writeConcern": { + "w": "majority" + } + }, + "tests": [ + { + "description": "client bulkWrite comment", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "comment": { + "bulk": "write" + }, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "comment": { + "bulk": "write" + }, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "client bulkWrite bypassDocumentValidation", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "bypassDocumentValidation": true, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "bypassDocumentValidation": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "client bulkWrite let", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id1" + ] + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + } + } + } + ], + "let": { + "id1": 1, + "id2": 2 + }, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 1, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "1": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "let": { + "id1": 1, + "id2": 2 + }, + "ops": [ + { + "update": 0, + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id1" + ] + } + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "$expr": { + "$eq": [ + "$_id", + "$$id2" + ] + } + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "x": 12 + } + ] + } + ] + }, + { + "description": "client bulkWrite bypassDocumentValidation: false is sent", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "bypassDocumentValidation": false, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "bypassDocumentValidation": false, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "client bulkWrite writeConcern", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "writeConcern": { + "w": "majority" + }, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "writeConcern": { + "w": "majority" + }, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite inherits writeConcern from client", + "operations": [ + { + "object": "writeConcernClient", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "writeConcernClient", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "writeConcern": { + "w": 1 + }, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite writeConcern option overrides client writeConcern", + "operations": [ + { + "object": "writeConcernClient", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "writeConcern": { + "w": "majority" + }, + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 3 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "writeConcernClient", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "writeConcern": { + "w": "majority" + }, + "ops": [ + { + "insert": 0, + "document": { + "_id": 3, + "x": 33 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-options.yml b/source/crud/tests/unified/client-bulkWrite-options.yml new file mode 100644 index 0000000000..e0cbe747b3 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-options.yml @@ -0,0 +1,351 @@ +description: "client bulkWrite top-level options" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - client: + id: &writeConcernClient writeConcernClient + uriOptions: + &clientWriteConcern { w: 1 } + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + comment: &comment { bulk: "write" } + let: &let { id1: 1, id2: 2 } + writeConcern: &majorityWriteConcern { w: "majority" } + +tests: + - description: "client bulkWrite comment" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + comment: *comment + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + comment: *comment + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - description: "client bulkWrite bypassDocumentValidation" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + bypassDocumentValidation: true + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + bypassDocumentValidation: true + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - description: "client bulkWrite let" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id1" ] + update: + $inc: { x: 1 } + - deleteOne: + namespace: *namespace + filter: + $expr: + $eq: [ "$_id", "$$id2" ] + let: *let + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 1 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 1: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + let: *let + ops: + - update: 0 + filter: + $expr: + $eq: [ "$_id", "$$id1" ] + updateMods: { $inc: { x: 1 } } + multi: false + - delete: 0 + filter: + $expr: + $eq: [ "$_id", "$$id2" ] + multi: false + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, x: 12 } + - description: "client bulkWrite bypassDocumentValidation: false is sent" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + bypassDocumentValidation: false + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + bypassDocumentValidation: false + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - description: "client bulkWrite writeConcern" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + writeConcern: *majorityWriteConcern + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + writeConcern: *majorityWriteConcern + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace + - description: "client bulkWrite inherits writeConcern from client" + operations: + - object: *writeConcernClient + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *writeConcernClient + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + writeConcern: { w: 1 } + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace + - description: "client bulkWrite writeConcern option overrides client writeConcern" + operations: + - object: *writeConcernClient + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 3, x: 33 } + writeConcern: *majorityWriteConcern + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 3 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *writeConcernClient + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + writeConcern: *majorityWriteConcern + ops: + - insert: 0 + document: { _id: 3, x: 33 } + nsInfo: + - ns: *namespace diff --git a/source/crud/tests/unified/client-bulkWrite-ordered.json b/source/crud/tests/unified/client-bulkWrite-ordered.json new file mode 100644 index 0000000000..6fb10d992f --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-ordered.json @@ -0,0 +1,291 @@ +{ + "description": "client bulkWrite with ordered option", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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": [] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with ordered: false", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "verboseResults": true, + "ordered": false + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 1 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": false, + "ops": [ + { + "insert": 0, + "document": { + "_id": 1, + "x": 11 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "client bulkWrite with ordered: true", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "verboseResults": true, + "ordered": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 1 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 1, + "x": 11 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "client bulkWrite defaults to ordered: true", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 1 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 1, + "x": 11 + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-ordered.yml b/source/crud/tests/unified/client-bulkWrite-ordered.yml new file mode 100644 index 0000000000..48aa8ad40a --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-ordered.yml @@ -0,0 +1,153 @@ +description: "client bulkWrite with ordered option" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite with ordered: false" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 1, x: 11 } + verboseResults: true + ordered: false + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 1 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: false + ops: + - insert: 0 + document: { _id: 1, x: 11 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - description: "client bulkWrite with ordered: true" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 1, x: 11 } + verboseResults: true + ordered: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 1 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 1, x: 11 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - description: "client bulkWrite defaults to ordered: true" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 1, x: 11 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 1 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 1, x: 11 } + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } diff --git a/source/crud/tests/unified/client-bulkWrite-partialResults.json b/source/crud/tests/unified/client-bulkWrite-partialResults.json new file mode 100644 index 0000000000..1b75e37834 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-partialResults.json @@ -0,0 +1,540 @@ +{ + "description": "client bulkWrite partial results", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "newDocument": { + "_id": 2, + "x": 22 + } + }, + "tests": [ + { + "description": "partialResult is unset when first operation fails during an ordered bulk write (verbose)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true, + "verboseResults": true + }, + "expectError": { + "expectResult": { + "$$unsetOrMatches": { + "insertedCount": { + "$$exists": false + }, + "upsertedCount": { + "$$exists": false + }, + "matchedCount": { + "$$exists": false + }, + "modifiedCount": { + "$$exists": false + }, + "deletedCount": { + "$$exists": false + }, + "insertResults": { + "$$exists": false + }, + "updateResults": { + "$$exists": false + }, + "deleteResults": { + "$$exists": false + } + } + } + } + } + ] + }, + { + "description": "partialResult is unset when first operation fails during an ordered bulk write (summary)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true, + "verboseResults": false + }, + "expectError": { + "expectResult": { + "$$unsetOrMatches": { + "insertedCount": { + "$$exists": false + }, + "upsertedCount": { + "$$exists": false + }, + "matchedCount": { + "$$exists": false + }, + "modifiedCount": { + "$$exists": false + }, + "deletedCount": { + "$$exists": false + }, + "insertResults": { + "$$exists": false + }, + "updateResults": { + "$$exists": false + }, + "deleteResults": { + "$$exists": false + } + } + } + } + } + ] + }, + { + "description": "partialResult is set when second operation fails during an ordered bulk write (verbose)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "ordered": true, + "verboseResults": true + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 2 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + } + ] + }, + { + "description": "partialResult is set when second operation fails during an ordered bulk write (summary)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "ordered": true, + "verboseResults": false + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + } + ] + }, + { + "description": "partialResult is unset when all operations fail during an unordered bulk write", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "ordered": false + }, + "expectError": { + "expectResult": { + "$$unsetOrMatches": { + "insertedCount": { + "$$exists": false + }, + "upsertedCount": { + "$$exists": false + }, + "matchedCount": { + "$$exists": false + }, + "modifiedCount": { + "$$exists": false + }, + "deletedCount": { + "$$exists": false + }, + "insertResults": { + "$$exists": false + }, + "updateResults": { + "$$exists": false + }, + "deleteResults": { + "$$exists": false + } + } + } + } + } + ] + }, + { + "description": "partialResult is set when first operation fails during an unordered bulk write (verbose)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": false, + "verboseResults": true + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "1": { + "insertedId": 2 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + } + ] + }, + { + "description": "partialResult is set when first operation fails during an unordered bulk write (summary)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": false, + "verboseResults": false + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + } + ] + }, + { + "description": "partialResult is set when second operation fails during an unordered bulk write (verbose)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "ordered": false, + "verboseResults": true + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 2 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + } + ] + }, + { + "description": "partialResult is set when second operation fails during an unordered bulk write (summary)", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "ordered": false, + "verboseResults": false + }, + "expectError": { + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-partialResults.yml b/source/crud/tests/unified/client-bulkWrite-partialResults.yml new file mode 100644 index 0000000000..1cda7318f8 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-partialResults.yml @@ -0,0 +1,262 @@ +description: "client bulkWrite partial results" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - &existingDocument { _id: 1, x: 11 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + newDocument: &newDocument { _id: 2, x: 22 } + +tests: + - description: "partialResult is unset when first operation fails during an ordered bulk write (verbose)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *existingDocument + - insertOne: + namespace: *namespace + document: *newDocument + ordered: true + verboseResults: true + expectError: + expectResult: + $$unsetOrMatches: + insertedCount: { $$exists: false } + upsertedCount: { $$exists: false } + matchedCount: { $$exists: false } + modifiedCount: { $$exists: false } + deletedCount: { $$exists: false } + insertResults: { $$exists: false } + updateResults: { $$exists: false } + deleteResults: { $$exists: false } + - description: "partialResult is unset when first operation fails during an ordered bulk write (summary)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *existingDocument + - insertOne: + namespace: *namespace + document: *newDocument + ordered: true + verboseResults: false + expectError: + expectResult: + $$unsetOrMatches: + insertedCount: { $$exists: false } + upsertedCount: { $$exists: false } + matchedCount: { $$exists: false } + modifiedCount: { $$exists: false } + deletedCount: { $$exists: false } + insertResults: { $$exists: false } + updateResults: { $$exists: false } + deleteResults: { $$exists: false } + - description: "partialResult is set when second operation fails during an ordered bulk write (verbose)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *newDocument + - insertOne: + namespace: *namespace + document: *existingDocument + ordered: true + verboseResults: true + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 2 + updateResults: {} + deleteResults: {} + - description: "partialResult is set when second operation fails during an ordered bulk write (summary)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *newDocument + - insertOne: + namespace: *namespace + document: *existingDocument + ordered: true + verboseResults: false + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + - description: "partialResult is unset when all operations fail during an unordered bulk write" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *existingDocument + - insertOne: + namespace: *namespace + document: *existingDocument + ordered: false + expectError: + expectResult: + $$unsetOrMatches: + insertedCount: { $$exists: false } + upsertedCount: { $$exists: false } + matchedCount: { $$exists: false } + modifiedCount: { $$exists: false } + deletedCount: { $$exists: false } + insertResults: { $$exists: false } + updateResults: { $$exists: false } + deleteResults: { $$exists: false } + - description: "partialResult is set when first operation fails during an unordered bulk write (verbose)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *existingDocument + - insertOne: + namespace: *namespace + document: *newDocument + ordered: false + verboseResults: true + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 1: + insertedId: 2 + updateResults: {} + deleteResults: {} + - description: "partialResult is set when first operation fails during an unordered bulk write (summary)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *existingDocument + - insertOne: + namespace: *namespace + document: *newDocument + ordered: false + verboseResults: false + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + - description: "partialResult is set when second operation fails during an unordered bulk write (verbose)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *newDocument + - insertOne: + namespace: *namespace + document: *existingDocument + ordered: false + verboseResults: true + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 2 + updateResults: {} + deleteResults: {} + - description: "partialResult is set when second operation fails during an unordered bulk write (summary)" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: *newDocument + - insertOne: + namespace: *namespace + document: *existingDocument + ordered: false + verboseResults: false + expectError: + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} diff --git a/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.json b/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.json new file mode 100644 index 0000000000..22da494366 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.json @@ -0,0 +1,169 @@ +{ + "description": "client bulkWrite replaceOne-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite replaceOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ops": [ + { + "update": 0, + "filter": {}, + "updateMods": { + "x": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "client bulk write replaceOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "maxServerVersion": "8.1.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": {}, + "replacement": { + "x": 1 + } + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": {}, + "updateMods": { + "x": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.yml b/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.yml new file mode 100644 index 0000000000..c0cd568194 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-replaceOne-rawdata.yml @@ -0,0 +1,92 @@ +description: "client bulkWrite replaceOne-rawData" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite replaceOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - replaceOne: + namespace: *namespace + filter: &filter {} + replacement: &replacement + x: 1 + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ops: + - update: 0 + filter: *filter + updateMods: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + nsInfo: + - ns: *namespace + rawData: *rawdata + - description: "client bulk write replaceOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "8.0" + maxServerVersion: "8.1.99" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - replaceOne: + namespace: *namespace + filter: &filter {} + replacement: &replacement + x: 1 + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ordered: true + ops: + - update: 0 + filter: *filter + updateMods: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + nsInfo: + - ns: *namespace + rawData: + $$exists: false diff --git a/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.json b/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.json new file mode 100644 index 0000000000..fc66ec015d --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.json @@ -0,0 +1,163 @@ +{ + "description": "client bulkWrite replaceOne-sort", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite replaceOne with sort option", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ops": [ + { + "update": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "updateMods": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "nErrors": 0, + "nMatched": 1, + "nModified": 1 + }, + "commandName": "bulkWrite" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.yml b/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.yml new file mode 100644 index 0000000000..1ea47abc4c --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-replaceOne-sort.yml @@ -0,0 +1,77 @@ +description: client bulkWrite replaceOne-sort + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid # Serverless does not support bulkWrite: CLOUDP-256344. + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: client bulkWrite replaceOne with sort option + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - replaceOne: + namespace: *namespace + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ops: + - update: 0 + filter: { _id: { $gt: 1 } } + updateMods: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + nsInfo: + - ns: *namespace + - commandSucceededEvent: + reply: + ok: 1 + nErrors: 0 + nMatched: 1 + nModified: 1 + commandName: bulkWrite + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } diff --git a/source/crud/tests/unified/client-bulkWrite-results.json b/source/crud/tests/unified/client-bulkWrite-results.json new file mode 100644 index 0000000000..accf5a9cbf --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-results.json @@ -0,0 +1,833 @@ +{ + "description": "client bulkWrite results", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with verboseResults: true returns detailed results", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 1, + "matchedCount": 3, + "modifiedCount": 3, + "deletedCount": 3, + "insertResults": { + "0": { + "insertedId": 8 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + }, + "3": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedId": 4 + } + }, + "deleteResults": { + "4": { + "deletedCount": 1 + }, + "5": { + "deletedCount": 2 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + }, + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 5 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 35 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 8, + "x": 88 + } + ] + } + ] + }, + { + "description": "client bulkWrite with verboseResults: false omits detailed results", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ], + "verboseResults": false + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 1, + "matchedCount": 3, + "modifiedCount": 3, + "deletedCount": 3, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + }, + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 5 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 35 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 8, + "x": 88 + } + ] + } + ] + }, + { + "description": "client bulkWrite defaults to verboseResults: false", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ] + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 1, + "matchedCount": 3, + "modifiedCount": 3, + "deletedCount": 3, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + }, + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 5 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 35 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 8, + "x": 88 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-results.yml b/source/crud/tests/unified/client-bulkWrite-results.yml new file mode 100644 index 0000000000..86cb5346ae --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-results.yml @@ -0,0 +1,312 @@ +description: "client bulkWrite results" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + - { _id: 7, x: 77 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite with verboseResults: true returns detailed results" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace + filter: { _id: 5 } + - deleteMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 1 + matchedCount: 3 + modifiedCount: 3 + deletedCount: 3 + insertResults: + 0: + insertedId: 8 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { $$exists: false } + 3: + matchedCount: 1 + modifiedCount: 0 + upsertedId: 4 + deleteResults: + 4: + deletedCount: 1 + 5: + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 8, x: 88 } + - update: 0 + filter: { _id: 1 } + updateMods: { $inc: { x: 1 } } + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $inc: { x: 2 } } + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { x: 44 } + upsert: true + multi: false + - delete: 0 + filter: { _id: 5 } + multi: false + - delete: 0 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + multi: true + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 24 } + - { _id: 3, x: 35 } + - { _id: 4, x: 44 } + - { _id: 8, x: 88 } + - description: "client bulkWrite with verboseResults: false omits detailed results" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace + filter: { _id: 5 } + - deleteMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + verboseResults: false + expectResult: + insertedCount: 1 + upsertedCount: 1 + matchedCount: 3 + modifiedCount: 3 + deletedCount: 3 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - insert: 0 + document: { _id: 8, x: 88 } + - update: 0 + filter: { _id: 1 } + updateMods: { $inc: { x: 1 } } + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $inc: { x: 2 } } + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { x: 44 } + upsert: true + multi: false + - delete: 0 + filter: { _id: 5 } + multi: false + - delete: 0 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + multi: true + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 24 } + - { _id: 3, x: 35 } + - { _id: 4, x: 44 } + - { _id: 8, x: 88 } + - description: "client bulkWrite defaults to verboseResults: false" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace + filter: { _id: 5 } + - deleteMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + expectResult: + insertedCount: 1 + upsertedCount: 1 + matchedCount: 3 + modifiedCount: 3 + deletedCount: 3 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - insert: 0 + document: { _id: 8, x: 88 } + - update: 0 + filter: { _id: 1 } + updateMods: { $inc: { x: 1 } } + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $inc: { x: 2 } } + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { x: 44 } + upsert: true + multi: false + - delete: 0 + filter: { _id: 5 } + multi: false + - delete: 0 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + multi: true + nsInfo: + - ns: *namespace + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 24 } + - { _id: 3, x: 35 } + - { _id: 4, x: 44 } + - { _id: 8, x: 88 } diff --git a/source/crud/tests/unified/client-bulkWrite-update-options.json b/source/crud/tests/unified/client-bulkWrite-update-options.json new file mode 100644 index 0000000000..ce6241c681 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-options.json @@ -0,0 +1,949 @@ +{ + "description": "client bulkWrite update options", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 2, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 3, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 4, + "array": [ + 1, + 2, + 3 + ] + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "collation": { + "locale": "simple" + }, + "hint": "_id_" + }, + "tests": [ + { + "description": "client bulkWrite update with arrayFilters", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "array.$[i]": 4 + } + }, + "arrayFilters": [ + { + "i": { + "$gte": 2 + } + } + ] + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$set": { + "array.$[i]": 5 + } + }, + "arrayFilters": [ + { + "i": { + "$gte": 2 + } + } + ] + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 3, + "modifiedCount": 3, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "1": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$set": { + "array.$[i]": 4 + } + }, + "arrayFilters": [ + { + "i": { + "$gte": 2 + } + } + ], + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$set": { + "array.$[i]": 5 + } + }, + "arrayFilters": [ + { + "i": { + "$gte": 2 + } + } + ], + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "array": [ + 1, + 4, + 4 + ] + }, + { + "_id": 2, + "array": [ + 1, + 5, + 5 + ] + }, + { + "_id": 3, + "array": [ + 1, + 5, + 5 + ] + }, + { + "_id": 4, + "array": [ + 1, + 2, + 3 + ] + } + ] + } + ] + }, + { + "description": "client bulkWrite update with collation", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "collation": { + "locale": "simple" + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$set": { + "array": [ + 1, + 2, + 5 + ] + } + }, + "collation": { + "locale": "simple" + } + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "array": [ + 1, + 2, + 6 + ] + }, + "collation": { + "locale": "simple" + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "1": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "collation": { + "locale": "simple" + }, + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$set": { + "array": [ + 1, + 2, + 5 + ] + } + }, + "collation": { + "locale": "simple" + }, + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "array": [ + 1, + 2, + 6 + ] + }, + "collation": { + "locale": "simple" + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "array": [ + 1, + 2, + 4 + ] + }, + { + "_id": 2, + "array": [ + 1, + 2, + 5 + ] + }, + { + "_id": 3, + "array": [ + 1, + 2, + 5 + ] + }, + { + "_id": 4, + "array": [ + 1, + 2, + 6 + ] + } + ] + } + ] + }, + { + "description": "client bulkWrite update with hint", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "hint": "_id_" + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$set": { + "array": [ + 1, + 2, + 5 + ] + } + }, + "hint": "_id_" + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "array": [ + 1, + 2, + 6 + ] + }, + "hint": "_id_" + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "1": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "hint": "_id_", + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$set": { + "array": [ + 1, + 2, + 5 + ] + } + }, + "hint": "_id_", + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "array": [ + 1, + 2, + 6 + ] + }, + "hint": "_id_", + "multi": false + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "array": [ + 1, + 2, + 4 + ] + }, + { + "_id": 2, + "array": [ + 1, + 2, + 5 + ] + }, + { + "_id": 3, + "array": [ + 1, + 2, + 5 + ] + }, + { + "_id": 4, + "array": [ + 1, + 2, + 6 + ] + } + ] + } + ] + }, + { + "description": "client bulkWrite update with upsert", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 5 + }, + "update": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "upsert": true + } + }, + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 6 + }, + "replacement": { + "array": [ + 1, + 2, + 6 + ] + }, + "upsert": true + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedId": 5 + }, + "1": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedId": 6 + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 5 + }, + "updateMods": { + "$set": { + "array": [ + 1, + 2, + 4 + ] + } + }, + "upsert": true, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 6 + }, + "updateMods": { + "array": [ + 1, + 2, + 6 + ] + }, + "upsert": true, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 2, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 3, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 4, + "array": [ + 1, + 2, + 3 + ] + }, + { + "_id": 5, + "array": [ + 1, + 2, + 4 + ] + }, + { + "_id": 6, + "array": [ + 1, + 2, + 6 + ] + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-update-options.yml b/source/crud/tests/unified/client-bulkWrite-update-options.yml new file mode 100644 index 0000000000..c5cc20d480 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-options.yml @@ -0,0 +1,338 @@ +description: "client bulkWrite update options" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, array: [ 1, 2, 3 ] } + - { _id: 2, array: [ 1, 2, 3 ] } + - { _id: 3, array: [ 1, 2, 3 ] } + - { _id: 4, array: [ 1, 2, 3 ] } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + collation: &collation { "locale": "simple" } + hint: &hint _id_ + +tests: + - description: "client bulkWrite update with arrayFilters" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + $set: + array.$[i]: 4 + arrayFilters: [ i: { $gte: 2 } ] + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: + $set: + array.$[i]: 5 + arrayFilters: [ i: { $gte: 2 } ] + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 3 + modifiedCount: 3 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 1: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { $$exists: false } + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + $set: + array.$[i]: 4 + arrayFilters: [ i: { $gte: 2 } ] + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: + $set: + array.$[i]: 5 + arrayFilters: [ i: { $gte: 2 } ] + multi: true + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, array: [ 1, 4, 4 ] } + - { _id: 2, array: [ 1, 5, 5 ] } + - { _id: 3, array: [ 1, 5, 5 ] } + - { _id: 4, array: [ 1, 2, 3 ] } + - description: "client bulkWrite update with collation" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $set: { array: [ 1, 2, 4 ] } } + collation: *collation + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $set: { array: [ 1, 2, 5 ] } } + collation: *collation + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { array: [ 1, 2, 6 ] } + collation: *collation + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 1: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: { $set: { array: [ 1, 2, 4 ] } } + collation: *collation + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $set: { array: [ 1, 2, 5 ] } } + collation: *collation + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { array: [ 1, 2, 6 ] } + collation: *collation + multi: false + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, array: [ 1, 2, 4 ] } + - { _id: 2, array: [ 1, 2, 5 ] } + - { _id: 3, array: [ 1, 2, 5 ] } + - { _id: 4, array: [ 1, 2, 6 ] } + - description: "client bulkWrite update with hint" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $set: { array: [ 1, 2, 4 ] } } + hint: *hint + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $set: { array: [ 1, 2, 5 ] } } + hint: *hint + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { array: [ 1, 2, 6 ] } + hint: *hint + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 1: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: { $set: { array: [ 1, 2, 4 ] } } + hint: *hint + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $set: { array: [ 1, 2, 5 ] } } + hint: *hint + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { array: [ 1, 2, 6 ] } + hint: *hint + multi: false + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, array: [ 1, 2, 4 ] } + - { _id: 2, array: [ 1, 2, 5 ] } + - { _id: 3, array: [ 1, 2, 5 ] } + - { _id: 4, array: [ 1, 2, 6 ] } + - description: "client bulkWrite update with upsert" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 5 } + update: { $set: { array: [ 1, 2, 4 ] } } + upsert: true + - replaceOne: + namespace: *namespace + filter: { _id: 6 } + replacement: { array: [ 1, 2, 6 ] } + upsert: true + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 2 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 0 + upsertedId: 5 + 1: + matchedCount: 1 + modifiedCount: 0 + upsertedId: 6 + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { _id: 5 } + updateMods: { $set: { array: [ 1, 2, 4 ] } } + upsert: true + multi: false + - update: 0 + filter: { _id: 6 } + updateMods: { array: [ 1, 2, 6 ] } + upsert: true + multi: false + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, array: [ 1, 2, 3 ] } + - { _id: 2, array: [ 1, 2, 3 ] } + - { _id: 3, array: [ 1, 2, 3 ] } + - { _id: 4, array: [ 1, 2, 3 ] } + - { _id: 5, array: [ 1, 2, 4 ] } + - { _id: 6, array: [ 1, 2, 6 ] } diff --git a/source/crud/tests/unified/client-bulkWrite-update-pipeline.json b/source/crud/tests/unified/client-bulkWrite-update-pipeline.json new file mode 100644 index 0000000000..9dba5ee6c5 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-pipeline.json @@ -0,0 +1,258 @@ +{ + "description": "client bulkWrite update pipeline", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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": 1 + }, + { + "_id": 2, + "x": 2 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite updateOne with pipeline", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": [ + { + "$addFields": { + "foo": 1 + } + } + ] + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": [ + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": false + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "x": 1, + "foo": 1 + }, + { + "_id": 2, + "x": 2 + } + ] + } + ] + }, + { + "description": "client bulkWrite updateMany with pipeline", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": [ + { + "$addFields": { + "foo": 1 + } + } + ] + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 0, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 0, + "insertResults": {}, + "updateResults": { + "0": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": {}, + "updateMods": [ + { + "$addFields": { + "foo": 1 + } + } + ], + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "databaseName": "crud-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "x": 1, + "foo": 1 + }, + { + "_id": 2, + "x": 2, + "foo": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-update-pipeline.yml b/source/crud/tests/unified/client-bulkWrite-update-pipeline.yml new file mode 100644 index 0000000000..c90e93b47c --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-pipeline.yml @@ -0,0 +1,133 @@ +description: "client bulkWrite update pipeline" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - {_id: 1, x: 1} + - {_id: 2, x: 2} + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite updateOne with pipeline" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + - $addFields: + foo: 1 + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { "$$exists": false } + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + - $addFields: + foo: 1 + multi: false + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - {_id: 1, x: 1, foo: 1} + - {_id: 2, x: 2 } + + - description: "client bulkWrite updateMany with pipeline" + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateMany: + namespace: *namespace + filter: {} + update: + - $addFields: + foo: 1 + verboseResults: true + expectResult: + insertedCount: 0 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 0 + insertResults: {} + updateResults: + 0: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { "$$exists": false } + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - update: 0 + filter: { } + updateMods: + - $addFields: + foo: 1 + multi: true + nsInfo: + - ns: *namespace + outcome: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - {_id: 1, x: 1, foo: 1} + - {_id: 2, x: 2, foo: 1} diff --git a/source/crud/tests/unified/client-bulkWrite-update-rawdata.json b/source/crud/tests/unified/client-bulkWrite-update-rawdata.json new file mode 100644 index 0000000000..63f86f19d9 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-rawdata.json @@ -0,0 +1,198 @@ +{ + "description": "client bulkWrite update-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": { + "$set": {} + } + }, + "tests": [ + { + "description": "client bulk write update with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": { + "$set": {} + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": { + "$set": {} + } + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": {}, + "updateMods": { + "$set": {} + }, + "multi": false + }, + { + "update": 0, + "filter": {}, + "updateMods": { + "$set": {} + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "client bulk write update with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "maxServerVersion": "8.1.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": { + "$set": {} + } + } + }, + { + "updateMany": { + "namespace": "crud-tests.coll0", + "filter": {}, + "update": { + "$set": {} + } + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": {}, + "updateMods": { + "$set": {} + }, + "multi": false + }, + { + "update": 0, + "filter": {}, + "updateMods": { + "$set": {} + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-update-rawdata.yml b/source/crud/tests/unified/client-bulkWrite-update-rawdata.yml new file mode 100644 index 0000000000..f53f7c958c --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-rawdata.yml @@ -0,0 +1,103 @@ +description: "client bulkWrite update-rawData" +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + filter: &filter {} + update: &update + $set: {} + +tests: + - description: "client bulk write update with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: *filter + update: *update + - updateMany: + namespace: *namespace + filter: *filter + update: *update + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ordered: true + ops: + - update: 0 + filter: *filter + updateMods: *update + multi: false + - update: 0 + filter: *filter + updateMods: *update + multi: true + nsInfo: + - ns: *namespace + rawData: *rawdata + - description: "client bulk write update with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "8.0" + maxServerVersion: "8.1.99" + serverless: forbid + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: *filter + update: *update + - updateMany: + namespace: *namespace + filter: *filter + update: *update + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ordered: true + ops: + - update: 0 + filter: *filter + updateMods: *update + multi: false + - update: 0 + filter: *filter + updateMods: *update + multi: true + nsInfo: + - ns: *namespace + rawData: + $$exists: false diff --git a/source/crud/tests/unified/client-bulkWrite-update-validation.json b/source/crud/tests/unified/client-bulkWrite-update-validation.json new file mode 100644 index 0000000000..617e711338 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-validation.json @@ -0,0 +1,216 @@ +{ + "description": "client-bulkWrite-update-validation", + "schemaVersion": "1.1", + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite replaceOne prohibits atomic modifiers", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "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 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "client bulkWrite updateOne requires atomic modifiers", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "x": 22 + } + } + } + ] + }, + "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 + } + ] + } + ] + }, + { + "description": "client bulkWrite updateMany requires atomic modifiers", + "operations": [ + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "crud-tests.coll0", + "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/source/crud/tests/unified/client-bulkWrite-update-validation.yml b/source/crud/tests/unified/client-bulkWrite-update-validation.yml new file mode 100644 index 0000000000..478554c322 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-update-validation.yml @@ -0,0 +1,79 @@ +description: "client-bulkWrite-update-validation" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: "client bulkWrite replaceOne prohibits atomic modifiers" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: + - replaceOne: + namespace: *namespace + filter: { _id: 1 } + replacement: { $set: { x: 22 } } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData + + - description: "client bulkWrite updateOne requires atomic modifiers" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { x: 22 } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData + + - description: "client bulkWrite updateMany requires atomic modifiers" + operations: + - name: clientBulkWrite + object: *client0 + arguments: + models: + - updateMany: + namespace: *namespace + filter: { _id: { $gt: 1 } } + update: { x: 44 } + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: *initialData diff --git a/source/crud/tests/unified/client-bulkWrite-updateOne-sort.json b/source/crud/tests/unified/client-bulkWrite-updateOne-sort.json new file mode 100644 index 0000000000..ef75dcb374 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-updateOne-sort.json @@ -0,0 +1,167 @@ +{ + "description": "client bulkWrite updateOne-sort", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite updateOne with sort option", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ops": [ + { + "update": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "nErrors": 0, + "nMatched": 1, + "nModified": 1 + }, + "commandName": "bulkWrite" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/client-bulkWrite-updateOne-sort.yml b/source/crud/tests/unified/client-bulkWrite-updateOne-sort.yml new file mode 100644 index 0000000000..73a265d6b8 --- /dev/null +++ b/source/crud/tests/unified/client-bulkWrite-updateOne-sort.yml @@ -0,0 +1,77 @@ +description: client bulkWrite updateOne-sort + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid # Serverless does not support bulkWrite: CLOUDP-256344. + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "crud-tests.coll0" + +tests: + - description: client bulkWrite updateOne with sort option + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateOne: + namespace: *namespace + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: { $inc: { x: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + ops: + - update: 0 + filter: { _id: { $gt: 1 } } + updateMods: { $inc: { x: 1 } } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + nsInfo: + - ns: *namespace + - commandSucceededEvent: + reply: + ok: 1 + nErrors: 0 + nMatched: 1 + nModified: 1 + commandName: bulkWrite + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } diff --git a/source/crud/tests/unified/count-collation.json b/source/crud/tests/unified/count-collation.json new file mode 100644 index 0000000000..eef65e0880 --- /dev/null +++ b/source/crud/tests/unified/count-collation.json @@ -0,0 +1,83 @@ +{ + "description": "count-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "Count documents with collation", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "x": "ping" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "Deprecated count with collation", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": { + "x": "ping" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": 1 + } + ] + } + ] +} diff --git a/source/crud/tests/unified/count-collation.yml b/source/crud/tests/unified/count-collation.yml new file mode 100644 index 0000000000..692a3c6988 --- /dev/null +++ b/source/crud/tests/unified/count-collation.yml @@ -0,0 +1,57 @@ +description: count-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: ping } + +tests: + - + description: 'Count documents with collation' + operations: + - + object: *collection0 + name: countDocuments + arguments: + filter: { x: ping } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: 1 + - + description: 'Deprecated count with collation' + operations: + - + object: *collection0 + name: count + arguments: + filter: { x: ping } + collation: + locale: en_US + strength: 2 + expectResult: 1 diff --git a/source/crud/tests/unified/count-empty.json b/source/crud/tests/unified/count-empty.json new file mode 100644 index 0000000000..29d8d76f67 --- /dev/null +++ b/source/crud/tests/unified/count-empty.json @@ -0,0 +1,71 @@ +{ + "description": "count-empty", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [] + } + ], + "tests": [ + { + "description": "Estimated document count with empty collection", + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": {}, + "expectResult": 0 + } + ] + }, + { + "description": "Count documents with empty collection", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 0 + } + ] + }, + { + "description": "Deprecated count with empty collection", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 0 + } + ] + } + ] +} diff --git a/source/crud/tests/unified/count-empty.yml b/source/crud/tests/unified/count-empty.yml new file mode 100644 index 0000000000..a5d5ae35b7 --- /dev/null +++ b/source/crud/tests/unified/count-empty.yml @@ -0,0 +1,52 @@ +description: count-empty + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'Estimated document count with empty collection' + operations: + - + object: *collection0 + name: estimatedDocumentCount + arguments: { } + expectResult: 0 + - + description: 'Count documents with empty collection' + operations: + - + object: *collection0 + name: countDocuments + arguments: + filter: { } + expectResult: 0 + - + description: 'Deprecated count with empty collection' + operations: + - + object: *collection0 + name: count + arguments: + filter: { } + expectResult: 0 diff --git a/source/crud/tests/unified/count-rawdata.json b/source/crud/tests/unified/count-rawdata.json new file mode 100644 index 0000000000..44772d40fb --- /dev/null +++ b/source/crud/tests/unified/count-rawdata.json @@ -0,0 +1,100 @@ +{ + "description": "count-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Deprecated count with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "rawData": true + }, + "databaseName": "crud-tests" + } + } + ] + } + ] + }, + { + "description": "Deprecated count with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "rawData": { + "$$exists": false + } + }, + "databaseName": "crud-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/count-rawdata.yml b/source/crud/tests/unified/count-rawdata.yml new file mode 100644 index 0000000000..845606c9dd --- /dev/null +++ b/source/crud/tests/unified/count-rawdata.yml @@ -0,0 +1,53 @@ +description: "count-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Deprecated count with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: count + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + rawData: *rawdata + databaseName: *database0Name + - description: "Deprecated count with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: count + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + rawData: + $$exists: false + databaseName: *database0Name diff --git a/source/crud/tests/unified/count.json b/source/crud/tests/unified/count.json new file mode 100644 index 0000000000..80fff5a30c --- /dev/null +++ b/source/crud/tests/unified/count.json @@ -0,0 +1,148 @@ +{ + "description": "count", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Estimated document count", + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": {}, + "expectResult": 3 + } + ] + }, + { + "description": "Count documents without a filter", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 3 + } + ] + }, + { + "description": "Count documents with a filter", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": 2 + } + ] + }, + { + "description": "Count documents with skip and limit", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {}, + "skip": 1, + "limit": 3 + }, + "expectResult": 2 + } + ] + }, + { + "description": "Deprecated count without a filter", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 3 + } + ] + }, + { + "description": "Deprecated count with a filter", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": 2 + } + ] + }, + { + "description": "Deprecated count with skip and limit", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {}, + "skip": 1, + "limit": 3 + }, + "expectResult": 2 + } + ] + } + ] +} diff --git a/source/crud/tests/unified/count.yml b/source/crud/tests/unified/count.yml new file mode 100644 index 0000000000..5f9fbb7633 --- /dev/null +++ b/source/crud/tests/unified/count.yml @@ -0,0 +1,95 @@ +description: count + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'Estimated document count' + operations: + - + object: *collection0 + name: estimatedDocumentCount + arguments: { } + expectResult: 3 + - + description: 'Count documents without a filter' + operations: + - + object: *collection0 + name: countDocuments + arguments: + filter: { } + expectResult: 3 + - + description: 'Count documents with a filter' + operations: + - + object: *collection0 + name: countDocuments + arguments: + filter: { _id: { $gt: 1 } } + expectResult: 2 + - + description: 'Count documents with skip and limit' + operations: + - + object: *collection0 + name: countDocuments + arguments: + filter: { } + skip: 1 + limit: 3 + expectResult: 2 + - + description: 'Deprecated count without a filter' + operations: + - + object: *collection0 + name: count + arguments: + filter: { } + expectResult: 3 + - + description: 'Deprecated count with a filter' + operations: + - + object: *collection0 + name: count + arguments: + filter: { _id: { $gt: 1 } } + expectResult: 2 + - + description: 'Deprecated count with skip and limit' + operations: + - + object: *collection0 + name: count + arguments: + filter: { } + skip: 1 + limit: 3 + expectResult: 2 diff --git a/source/crud/tests/unified/countDocuments-comment.json b/source/crud/tests/unified/countDocuments-comment.json new file mode 100644 index 0000000000..e6c7ae8170 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/countDocuments-comment.yml b/source/crud/tests/unified/countDocuments-comment.yml new file mode 100644 index 0000000000..8f1f7bc4b4 --- /dev/null +++ b/source/crud/tests/unified/countDocuments-comment.yml @@ -0,0 +1,92 @@ +description: "countDocuments-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name countDocuments-comments-test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &documentComment { key: "value" } + expectResult: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: &pipeline + - $match: {} + - $group: { _id: 1, n: { $sum: 1 } } + comment: *documentComment + commandName: aggregate + databaseName: *database0Name + + - description: "countDocuments with string comment" + runOnRequirements: + - minServerVersion: 3.6.0 + operations: + - name: countDocuments + object: *collection0 + arguments: + filter: {} + comment: &stringComment "comment" + expectResult: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline + comment: *stringComment + commandName: aggregate + databaseName: *database0Name + + - 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: *documentComment + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: *pipeline + comment: *documentComment + commandName: aggregate + databaseName: *database0Name diff --git a/source/crud/tests/unified/countDocuments-rawdata.json b/source/crud/tests/unified/countDocuments-rawdata.json new file mode 100644 index 0000000000..1651a7fe2d --- /dev/null +++ b/source/crud/tests/unified/countDocuments-rawdata.json @@ -0,0 +1,128 @@ +{ + "description": "countDocuments-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Count documents with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "rawData": true + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ] + }, + { + "description": "Count documents with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "rawData": { + "$$exists": false + } + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/countDocuments-rawdata.yml b/source/crud/tests/unified/countDocuments-rawdata.yml new file mode 100644 index 0000000000..b0a7408267 --- /dev/null +++ b/source/crud/tests/unified/countDocuments-rawdata.yml @@ -0,0 +1,61 @@ +description: "countDocuments-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Count documents with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: countDocuments + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $match: {} + - $group: { _id: 1, n: { $sum: 1 } } + rawData: *rawdata + commandName: aggregate + databaseName: *database0Name + - description: "Count documents with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: countDocuments + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: + - $match: {} + - $group: { _id: 1, n: { $sum: 1 } } + rawData: + $$exists: false + commandName: aggregate + databaseName: *database0Name diff --git a/source/crud/tests/unified/create-null-ids.json b/source/crud/tests/unified/create-null-ids.json new file mode 100644 index 0000000000..8e0c3ac5d1 --- /dev/null +++ b/source/crud/tests/unified/create-null-ids.json @@ -0,0 +1,253 @@ +{ + "description": "create-null-ids", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "crud_id" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "type_tests" + } + } + ], + "initialData": [ + { + "collectionName": "type_tests", + "databaseName": "crud_id", + "documents": [] + } + ], + "tests": [ + { + "description": "inserting _id with type null via insertOne", + "operations": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": null + } + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via insertMany", + "operations": [ + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": null + } + ] + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via updateOne", + "operations": [ + { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "_id": null + }, + "update": { + "$unset": { + "a": "" + } + }, + "upsert": true + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via updateMany", + "operations": [ + { + "name": "updateMany", + "object": "collection", + "arguments": { + "filter": { + "_id": null + }, + "update": { + "$unset": { + "a": "" + } + }, + "upsert": true + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via replaceOne", + "operations": [ + { + "name": "replaceOne", + "object": "collection", + "arguments": { + "filter": {}, + "replacement": { + "_id": null + }, + "upsert": true + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via bulkWrite", + "operations": [ + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": null + } + } + } + ] + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + }, + { + "description": "inserting _id with type null via clientBulkWrite", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "crud_id.type_tests", + "document": { + "_id": null + } + } + } + ] + } + }, + { + "name": "countDocuments", + "object": "collection", + "arguments": { + "filter": { + "_id": { + "$type": "null" + } + } + }, + "expectResult": 1 + } + ] + } + ] +} diff --git a/source/crud/tests/unified/create-null-ids.yml b/source/crud/tests/unified/create-null-ids.yml new file mode 100644 index 0000000000..7511f04010 --- /dev/null +++ b/source/crud/tests/unified/create-null-ids.yml @@ -0,0 +1,97 @@ +description: create-null-ids +schemaVersion: '1.0' + +createEntities: + - client: + id: client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: client + databaseName: crud_id + - collection: + id: &collection collection + database: *database + collectionName: type_tests + +initialData: + - collectionName: type_tests + databaseName: crud_id + documents: [] + +tests: + + - description: inserting _id with type null via insertOne + operations: + - name: insertOne + object: *collection + arguments: {document: &null_id {_id: null}} + # We use countDocuments with a $type query to verify the insert of the correct BSON type + # this is to avoid client side type conversions (potentially common: undefined -> null) + - name: countDocuments + object: *collection + arguments: {filter: &null_id_filter {_id: {$type: 'null'}}} + expectResult: 1 + + - description: inserting _id with type null via insertMany + operations: + - name: insertMany + object: *collection + arguments: {documents: [*null_id]} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 + + - description: inserting _id with type null via updateOne + operations: + - name: updateOne + object: *collection + arguments: {filter: *null_id, update: {$unset: {a: ''}}, upsert: true} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 + + - description: inserting _id with type null via updateMany + operations: + - name: updateMany + object: *collection + arguments: {filter: *null_id, update: {$unset: {a: ''}}, upsert: true} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 + + - description: inserting _id with type null via replaceOne + operations: + - name: replaceOne + object: *collection + arguments: {filter: {}, replacement: *null_id, upsert: true} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 + + - description: inserting _id with type null via bulkWrite + operations: + - name: bulkWrite + object: *collection + arguments: {requests: [{insertOne: {document: *null_id}}]} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 + + - description: inserting _id with type null via clientBulkWrite + runOnRequirements: + - minServerVersion: '8.0' + operations: + - name: clientBulkWrite + object: client + arguments: {models: [{insertOne: {namespace: crud_id.type_tests, document: *null_id}}]} + - name: countDocuments + object: *collection + arguments: {filter: *null_id_filter} + expectResult: 1 diff --git a/source/crud/tests/unified/db-aggregate-rawdata.json b/source/crud/tests/unified/db-aggregate-rawdata.json new file mode 100644 index 0000000000..61e22b9fbe --- /dev/null +++ b/source/crud/tests/unified/db-aggregate-rawdata.json @@ -0,0 +1,177 @@ +{ + "description": "db-aggregate-rawdata", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "crud-v2" + } + } + ], + "tests": [ + { + "description": "Aggregate with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0", + "topologies": [ + "replicaset" + ], + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "Aggregate with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99", + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/db-aggregate-rawdata.yml b/source/crud/tests/unified/db-aggregate-rawdata.yml new file mode 100644 index 0000000000..1a6b21ca29 --- /dev/null +++ b/source/crud/tests/unified/db-aggregate-rawdata.yml @@ -0,0 +1,64 @@ +description: "db-aggregate-rawdata" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name admin + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName crud-v2 + +tests: + - description: "Aggregate with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + topologies: [ replicaset ] + serverless: forbid + operations: + - object: *database0 + name: aggregate + arguments: + pipeline: &pipeline + - { $listLocalSessions: {} } + - { $limit: 1 } + - { $addFields: { dummy: 'dummy field' } } + - { $project: { _id: 0, dummy: 1 } } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *pipeline + rawData: *rawdata + - description: "Aggregate with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + serverless: forbid + operations: + - object: *database0 + name: aggregate + arguments: + pipeline: &pipeline + - { $listLocalSessions: {} } + - { $limit: 1 } + - { $addFields: { dummy: 'dummy field' } } + - { $project: { _id: 0, dummy: 1 } } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *pipeline + rawData: + $$exists: false diff --git a/source/crud/tests/unified/db-aggregate-write-readPreference.json b/source/crud/tests/unified/db-aggregate-write-readPreference.json new file mode 100644 index 0000000000..b6460f001f --- /dev/null +++ b/source/crud/tests/unified/db-aggregate-write-readPreference.json @@ -0,0 +1,395 @@ +{ + "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" + } + } + ], + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + }, + { + "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 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/db-aggregate-write-readPreference.yml b/source/crud/tests/unified/db-aggregate-write-readPreference.yml new file mode 100644 index 0000000000..03fcd35aa3 --- /dev/null +++ b/source/crud/tests/unified/db-aggregate-write-readPreference.yml @@ -0,0 +1,138 @@ +description: db-aggregate-write-readPreference + +schemaVersion: '1.4' + +runOnRequirements: + # 3.6+ non-standalone is needed to utilize $readPreference in OP_MSG. + # Serverless does not support $listLocalSessions and $currentOp stages, and + # mongos does not allow combining them with $out or $merge. + - minServerVersion: "3.6" + topologies: [ replicaset ] + serverless: forbid + +_yamlAnchors: + readConcern: &readConcern + level: &readConcernLevel "local" + writeConcern: &writeConcern + w: &writeConcernW 1 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + # Used to test that read and write concerns are still inherited + uriOptions: + readConcernLevel: *readConcernLevel + w: *writeConcernW + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + databaseOptions: + readPreference: &readPreference + # secondaryPreferred is specified for compatibility with clusters that + # may not have a secondary (e.g. each shard is only a primary). + mode: secondaryPreferred + # maxStalenessSeconds is specified to ensure that drivers forward the + # read preference to mongos or a load balancer. That would not be the + # case with only secondaryPreferred. + maxStalenessSeconds: 600 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +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: &outPipeline + - { $listLocalSessions: {} } + - { $limit: 1 } + - { $addFields: { _id: 1 } } + - { $project: { _id: 1 } } + - { $out: *collection0Name } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *outPipeline + $readPreference: *readPreference + readConcern: *readConcern + writeConcern: *writeConcern + + - description: "Database-level aggregate with $out omits read preference for pre-5.0 server" + runOnRequirements: + # MongoDB 4.2 introduced support for read concerns and write stages. + # Pre-4.2 servers may allow a "local" read concern anyway, but some + # drivers may avoid inheriting a client-level read concern for pre-4.2. + - minServerVersion: "4.2" + maxServerVersion: "4.4.99" + serverless: "forbid" + operations: + - object: *database0 + name: aggregate + arguments: + pipeline: *outPipeline + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *outPipeline + $readPreference: { $$exists: false } + readConcern: *readConcern + writeConcern: *writeConcern + + - description: "Database-level aggregate with $merge includes read preference for 5.0+ server" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - object: *database0 + name: aggregate + arguments: + pipeline: &mergePipeline + - { $listLocalSessions: {} } + - { $limit: 1 } + - { $addFields: { _id: 1 } } + - { $project: { _id: 1 } } + - { $merge: { into: *collection0Name } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *mergePipeline + $readPreference: *readPreference + readConcern: *readConcern + writeConcern: *writeConcern + + - 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: *mergePipeline + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *mergePipeline + $readPreference: { $$exists: false } + readConcern: *readConcern + writeConcern: *writeConcern diff --git a/source/crud/tests/unified/db-aggregate.json b/source/crud/tests/unified/db-aggregate.json new file mode 100644 index 0000000000..5015405bfc --- /dev/null +++ b/source/crud/tests/unified/db-aggregate.json @@ -0,0 +1,107 @@ +{ + "description": "db-aggregate", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "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" + } + } + ], + "tests": [ + { + "description": "Aggregate with $listLocalSessions", + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ] + }, + "expectResult": [ + { + "dummy": "dummy field" + } + ] + } + ] + }, + { + "description": "Aggregate with $listLocalSessions and allowDiskUse", + "operations": [ + { + "object": "database0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ], + "allowDiskUse": true + }, + "expectResult": [ + { + "dummy": "dummy field" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/db-aggregate.yml b/source/crud/tests/unified/db-aggregate.yml new file mode 100644 index 0000000000..df82c32b05 --- /dev/null +++ b/source/crud/tests/unified/db-aggregate.yml @@ -0,0 +1,70 @@ +description: db-aggregate +schemaVersion: '1.4' +runOnRequirements: + - + minServerVersion: 3.6.0 + # serverless does not support either of the current database-level aggregation stages ($listLocalSessions and + # $currentOp) + serverless: forbid +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name admin + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name crud-v2 +tests: + - + description: 'Aggregate with $listLocalSessions' + operations: + - + object: *database0 + name: aggregate + arguments: + pipeline: + - + $listLocalSessions: { } + - + $limit: 1 + - + $addFields: + dummy: 'dummy field' + - + $project: + _id: 0 + dummy: 1 + expectResult: + - + dummy: 'dummy field' + - + description: 'Aggregate with $listLocalSessions and allowDiskUse' + operations: + - + object: *database0 + name: aggregate + arguments: + pipeline: + - + $listLocalSessions: { } + - + $limit: 1 + - + $addFields: + dummy: 'dummy field' + - + $project: + _id: 0 + dummy: 1 + allowDiskUse: true + expectResult: + - + dummy: 'dummy field' diff --git a/source/crud/tests/unified/deleteMany-collation.json b/source/crud/tests/unified/deleteMany-collation.json new file mode 100644 index 0000000000..23d2f037cb --- /dev/null +++ b/source/crud/tests/unified/deleteMany-collation.json @@ -0,0 +1,86 @@ +{ + "description": "deleteMany-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany when many documents match with collation", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "x": "PING" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteMany-collation.yml b/source/crud/tests/unified/deleteMany-collation.yml new file mode 100644 index 0000000000..b7caab67dd --- /dev/null +++ b/source/crud/tests/unified/deleteMany-collation.yml @@ -0,0 +1,54 @@ +description: deleteMany-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + +tests: + - + description: 'DeleteMany when many documents match with collation' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: { x: PING } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + deletedCount: 2 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } diff --git a/source/crud/tests/unified/deleteMany-comment.json b/source/crud/tests/unified/deleteMany-comment.json new file mode 100644 index 0000000000..6abc5fd58a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-comment.yml b/source/crud/tests/unified/deleteMany-comment.yml new file mode 100644 index 0000000000..8f1b3522a7 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-comment.yml @@ -0,0 +1,97 @@ +description: "deleteMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter { _id: { $gt: 1 } } + comment: "comment" + expectResult: &expect_result + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + + - description: "deleteMany with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value" } + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: *comment + outcome: *outcome + + - description: "deleteMany with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/deleteMany-hint-clientError.json b/source/crud/tests/unified/deleteMany-hint-clientError.json new file mode 100644 index 0000000000..66320122b5 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-hint-clientError.yml b/source/crud/tests/unified/deleteMany-hint-clientError.yml new file mode 100644 index 0000000000..c04d7507e3 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-hint-clientError.yml @@ -0,0 +1,84 @@ +description: deleteMany-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteMany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/deleteMany-hint-serverError.json b/source/crud/tests/unified/deleteMany-hint-serverError.json new file mode 100644 index 0000000000..88d4a65576 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-hint-serverError.yml b/source/crud/tests/unified/deleteMany-hint-serverError.yml new file mode 100644 index 0000000000..24260e2611 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-hint-serverError.yml @@ -0,0 +1,104 @@ +description: deleteMany-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.3.3 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteMany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: _id_ + limit: 0 + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: + _id: 1 + limit: 0 + outcome: *outcome diff --git a/source/crud/tests/unified/deleteMany-hint-unacknowledged.json b/source/crud/tests/unified/deleteMany-hint-unacknowledged.json new file mode 100644 index 0000000000..ab7e9c7c09 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-hint-unacknowledged.yml b/source/crud/tests/unified/deleteMany-hint-unacknowledged.yml new file mode 100644 index 0000000000..827618d174 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-hint-unacknowledged.yml @@ -0,0 +1,90 @@ +description: deleteMany-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged deleteMany with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: deleteMany + arguments: + filter: *filter + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + 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: *filter + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/deleteMany-hint.json b/source/crud/tests/unified/deleteMany-hint.json new file mode 100644 index 0000000000..59d903d201 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-hint.yml b/source/crud/tests/unified/deleteMany-hint.yml new file mode 100644 index 0000000000..ce5f0a058d --- /dev/null +++ b/source/crud/tests/unified/deleteMany-hint.yml @@ -0,0 +1,96 @@ +description: deleteMany-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.4 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteMany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + hint: _id_ + expectResult: &result + deletedCount: 2 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: _id_ + limit: 0 + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + description: 'DeleteMany with hint document' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: *filter + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: + _id: 1 + limit: 0 + outcome: *outcome diff --git a/source/crud/tests/unified/deleteMany-let.json b/source/crud/tests/unified/deleteMany-let.json new file mode 100644 index 0000000000..71bf26a013 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteMany-let.yml b/source/crud/tests/unified/deleteMany-let.yml new file mode 100644 index 0000000000..723985aa8c --- /dev/null +++ b/source/crud/tests/unified/deleteMany-let.yml @@ -0,0 +1,93 @@ +description: "deleteMany-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$name", "$$name" ] + let: &let0 + name: "name" + expectResult: + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - + q: *filter + limit: 0 + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + $expr: + $eq: [ "$name", "$$name" ] + let: &let1 + name: "name" + expectError: + errorContains: "'delete.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - + q: *filter1 + limit: 0 + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2, name: "name" } + - { _id: 3, name: "name" } diff --git a/source/crud/tests/unified/deleteMany-rawdata.json b/source/crud/tests/unified/deleteMany-rawdata.json new file mode 100644 index 0000000000..fbdc852708 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-rawdata.json @@ -0,0 +1,110 @@ +{ + "description": "deleteMany-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "deleteMany with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "deleteMany with with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteMany-rawdata.yml b/source/crud/tests/unified/deleteMany-rawdata.yml new file mode 100644 index 0000000000..d6c1f33478 --- /dev/null +++ b/source/crud/tests/unified/deleteMany-rawdata.yml @@ -0,0 +1,57 @@ +description: "deleteMany-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "deleteMany with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: {} + limit: 0 + rawData: *rawdata + - description: "deleteMany with with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: {} + limit: 0 + rawData: + $$exists: false diff --git a/source/crud/tests/unified/deleteMany.json b/source/crud/tests/unified/deleteMany.json new file mode 100644 index 0000000000..36cdff8dc0 --- /dev/null +++ b/source/crud/tests/unified/deleteMany.json @@ -0,0 +1,115 @@ +{ + "description": "deleteMany", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "DeleteMany when many documents match", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "DeleteMany when no document matches", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": 4 + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteMany.yml b/source/crud/tests/unified/deleteMany.yml new file mode 100644 index 0000000000..832201ed51 --- /dev/null +++ b/source/crud/tests/unified/deleteMany.yml @@ -0,0 +1,63 @@ +description: deleteMany + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'DeleteMany when many documents match' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: { _id: { $gt: 1 } } + expectResult: + deletedCount: 2 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'DeleteMany when no document matches' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: { _id: 4 } + expectResult: + deletedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/deleteOne-collation.json b/source/crud/tests/unified/deleteOne-collation.json new file mode 100644 index 0000000000..44bab6e120 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-collation.json @@ -0,0 +1,90 @@ +{ + "description": "deleteOne-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne when many documents matches with collation", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "x": "PING" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteOne-collation.yml b/source/crud/tests/unified/deleteOne-collation.yml new file mode 100644 index 0000000000..2ec971550a --- /dev/null +++ b/source/crud/tests/unified/deleteOne-collation.yml @@ -0,0 +1,55 @@ +description: deleteOne-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + +tests: + - + description: 'DeleteOne when many documents matches with collation' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: { x: PING } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: pINg } diff --git a/source/crud/tests/unified/deleteOne-comment.json b/source/crud/tests/unified/deleteOne-comment.json new file mode 100644 index 0000000000..0f42b086a3 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-comment.yml b/source/crud/tests/unified/deleteOne-comment.yml new file mode 100644 index 0000000000..1e52298f39 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-comment.yml @@ -0,0 +1,98 @@ +description: "deleteOne-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter { _id: 1 } + comment: "comment" + expectResult: &expect_result + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + comment: &comment { key: "value" } + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: *comment + outcome: *outcome + + - description: "deleteOne with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/deleteOne-errorResponse.json b/source/crud/tests/unified/deleteOne-errorResponse.json new file mode 100644 index 0000000000..1f3a266f1e --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-errorResponse.yml b/source/crud/tests/unified/deleteOne-errorResponse.yml new file mode 100644 index 0000000000..dcf013060e --- /dev/null +++ b/source/crud/tests/unified/deleteOne-errorResponse.yml @@ -0,0 +1,46 @@ +description: "deleteOne-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + # Some drivers may still need to skip this test because the CRUD spec does not + # prescribe how drivers should formulate a WriteException beyond collecting a + # write or write concern error. + - 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: &errorCode 8 # UnknownError + - name: deleteOne + object: *collection0 + arguments: + filter: { _id: 1 } + expectError: + errorCode: *errorCode + errorResponse: + code: *errorCode diff --git a/source/crud/tests/unified/deleteOne-hint-clientError.json b/source/crud/tests/unified/deleteOne-hint-clientError.json new file mode 100644 index 0000000000..cf629f59e0 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-hint-clientError.yml b/source/crud/tests/unified/deleteOne-hint-clientError.yml new file mode 100644 index 0000000000..7ff6f8d961 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-hint-clientError.yml @@ -0,0 +1,77 @@ +description: deleteOne-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteOne_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/deleteOne-hint-serverError.json b/source/crud/tests/unified/deleteOne-hint-serverError.json new file mode 100644 index 0000000000..15541ed857 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-hint-serverError.yml b/source/crud/tests/unified/deleteOne-hint-serverError.yml new file mode 100644 index 0000000000..191d96aeaf --- /dev/null +++ b/source/crud/tests/unified/deleteOne-hint-serverError.yml @@ -0,0 +1,97 @@ +description: deleteOne-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.3.3 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteOne_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: _id_ + limit: 1 + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: + _id: 1 + limit: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/deleteOne-hint-unacknowledged.json b/source/crud/tests/unified/deleteOne-hint-unacknowledged.json new file mode 100644 index 0000000000..1782f0f525 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-hint-unacknowledged.yml b/source/crud/tests/unified/deleteOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..4ccbe13d60 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-hint-unacknowledged.yml @@ -0,0 +1,89 @@ +description: deleteOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged deleteOne with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: deleteOne + arguments: + filter: *filter + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + 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: *filter + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/deleteOne-hint.json b/source/crud/tests/unified/deleteOne-hint.json new file mode 100644 index 0000000000..bcc4bc2347 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-hint.yml b/source/crud/tests/unified/deleteOne-hint.yml new file mode 100644 index 0000000000..ce23b8a987 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-hint.yml @@ -0,0 +1,92 @@ +description: deleteOne-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.4 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name DeleteOne_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'DeleteOne with hint string' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: &filter + _id: 1 + hint: _id_ + expectResult: &result + deletedCount: 1 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: _id_ + limit: 1 + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 2 + x: 22 + - + description: 'deleteOne with hint document' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: *filter + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: *filter + hint: + _id: 1 + limit: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/deleteOne-let.json b/source/crud/tests/unified/deleteOne-let.json new file mode 100644 index 0000000000..9718682235 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/deleteOne-let.yml b/source/crud/tests/unified/deleteOne-let.yml new file mode 100644 index 0000000000..c52d98b99d --- /dev/null +++ b/source/crud/tests/unified/deleteOne-let.yml @@ -0,0 +1,91 @@ +description: "deleteOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "deleteOne with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + let: &let0 + id: 1 + expectResult: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - + q: *filter + limit: 1 + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + $expr: + $eq: [ "$_id", "$$id" ] + let: &let1 + id: 1 + expectError: + errorContains: "'delete.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - + q: *filter1 + limit: 1 + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/deleteOne-rawdata.json b/source/crud/tests/unified/deleteOne-rawdata.json new file mode 100644 index 0000000000..4347548583 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-rawdata.json @@ -0,0 +1,110 @@ +{ + "description": "deleteOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "deleteOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "deleteOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteOne-rawdata.yml b/source/crud/tests/unified/deleteOne-rawdata.yml new file mode 100644 index 0000000000..6d9eb72f82 --- /dev/null +++ b/source/crud/tests/unified/deleteOne-rawdata.yml @@ -0,0 +1,57 @@ +description: "deleteOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "deleteOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: {} + limit: 1 + rawData: *rawdata + - description: "deleteOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: {} + limit: 1 + rawData: + $$exists: false diff --git a/source/crud/tests/unified/deleteOne.json b/source/crud/tests/unified/deleteOne.json new file mode 100644 index 0000000000..8177b2fb6b --- /dev/null +++ b/source/crud/tests/unified/deleteOne.json @@ -0,0 +1,136 @@ +{ + "description": "deleteOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne when many documents match", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ] + }, + { + "description": "DeleteOne when one document matches", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 2 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "DeleteOne when no documents match", + "operations": [ + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 4 + } + }, + "expectResult": { + "deletedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/deleteOne.yml b/source/crud/tests/unified/deleteOne.yml new file mode 100644 index 0000000000..6c7dc95191 --- /dev/null +++ b/source/crud/tests/unified/deleteOne.yml @@ -0,0 +1,76 @@ +description: deleteOne + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + # This test doesn't verify the output collection because we cannot assume + # which document gets deleted. + description: 'DeleteOne when many documents match' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: { $gt: 1 } } + expectResult: + deletedCount: 1 + - + description: 'DeleteOne when one document matches' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 2 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: 33 } + - + description: 'DeleteOne when no documents match' + operations: + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 4 } + expectResult: + deletedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/distinct-collation.json b/source/crud/tests/unified/distinct-collation.json new file mode 100644 index 0000000000..e40cb0b2cf --- /dev/null +++ b/source/crud/tests/unified/distinct-collation.json @@ -0,0 +1,69 @@ +{ + "description": "distinct-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "string": "PING" + }, + { + "_id": 2, + "string": "ping" + } + ] + } + ], + "tests": [ + { + "description": "Distinct with a collation", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "string", + "filter": {}, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": [ + "PING" + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/distinct-collation.yml b/source/crud/tests/unified/distinct-collation.yml new file mode 100644 index 0000000000..74cf1bae35 --- /dev/null +++ b/source/crud/tests/unified/distinct-collation.yml @@ -0,0 +1,48 @@ +description: distinct-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - {_id: 1, string: 'PING'} + - {_id: 2, string: 'ping'} + +tests: + - + description: 'Distinct with a collation' + operations: + - + object: *collection0 + name: distinct + arguments: + fieldName: string + filter: { } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + - PING diff --git a/source/crud/tests/unified/distinct-comment.json b/source/crud/tests/unified/distinct-comment.json new file mode 100644 index 0000000000..11bce9ac9d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/distinct-comment.yml b/source/crud/tests/unified/distinct-comment.yml new file mode 100644 index 0000000000..c082009943 --- /dev/null +++ b/source/crud/tests/unified/distinct-comment.yml @@ -0,0 +1,98 @@ +description: "distinct-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name distinct-comment-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "distinct with document comment" + runOnRequirements: + # https://jira.mongodb.org/browse/SERVER-44847 + # Server supports distinct with comment of any type for comment starting from 4.4.14. + - minServerVersion: "4.4.14" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: &fieldName x + filter: &filter {} + comment: &documentComment { key: "value"} + expectResult: [ 11, 22, 33 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + comment: *documentComment + commandName: distinct + databaseName: *database0Name + + - description: "distinct with string comment" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: *fieldName + filter: *filter + comment: &stringComment "comment" + expectResult: [ 11, 22, 33 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + comment: *stringComment + commandName: distinct + databaseName: *database0Name + + - 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: *fieldName + filter: *filter + comment: *documentComment + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + comment: *documentComment + commandName: distinct + databaseName: *database0Name diff --git a/source/crud/tests/unified/distinct-hint.json b/source/crud/tests/unified/distinct-hint.json new file mode 100644 index 0000000000..2a6869cbe0 --- /dev/null +++ b/source/crud/tests/unified/distinct-hint.json @@ -0,0 +1,139 @@ +{ + "description": "distinct-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "7.1.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "distinct-hint-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "distinct-hint-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "distinct with hint string", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectResult": [ + 11 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": { + "_id": 1 + }, + "hint": "_id_" + }, + "commandName": "distinct", + "databaseName": "distinct-hint-tests" + } + } + ] + } + ] + }, + { + "description": "distinct with hint document", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": [ + 11 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "commandName": "distinct", + "databaseName": "distinct-hint-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/distinct-hint.yml b/source/crud/tests/unified/distinct-hint.yml new file mode 100644 index 0000000000..9d277616d3 --- /dev/null +++ b/source/crud/tests/unified/distinct-hint.yml @@ -0,0 +1,73 @@ +description: "distinct-hint" + +schemaVersion: "1.0" +runOnRequirements: + # https://jira.mongodb.org/browse/SERVER-14227 + # Server supports distinct with hint starting from 7.1.0. + - minServerVersion: "7.1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name distinct-hint-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "distinct with hint string" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: &fieldName x + filter: &filter { _id: 1 } + hint: _id_ + expectResult: [ 11 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + hint: _id_ + commandName: distinct + databaseName: *database0Name + + - description: "distinct with hint document" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: *fieldName + filter: *filter + hint: + _id: 1 + expectResult: [ 11 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + hint: + _id: 1 + commandName: distinct + databaseName: *database0Name diff --git a/source/crud/tests/unified/distinct-rawdata.json b/source/crud/tests/unified/distinct-rawdata.json new file mode 100644 index 0000000000..d1a4abc7f7 --- /dev/null +++ b/source/crud/tests/unified/distinct-rawdata.json @@ -0,0 +1,104 @@ +{ + "description": "distinct-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "distinct with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": {}, + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "distinct with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": {}, + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/distinct-rawdata.yml b/source/crud/tests/unified/distinct-rawdata.yml new file mode 100644 index 0000000000..b6eeab9552 --- /dev/null +++ b/source/crud/tests/unified/distinct-rawdata.yml @@ -0,0 +1,57 @@ +description: "distinct-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "distinct with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: &fieldName x + filter: &filter {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + rawData: *rawdata + - description: "distinct with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: &fieldName x + filter: &filter {} + rawData: true + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + rawData: + $$exists: false diff --git a/source/crud/tests/unified/distinct.json b/source/crud/tests/unified/distinct.json new file mode 100644 index 0000000000..9accffabc9 --- /dev/null +++ b/source/crud/tests/unified/distinct.json @@ -0,0 +1,86 @@ +{ + "description": "distinct", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Distinct without a filter", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": {} + }, + "expectResult": [ + 11, + 22, + 33 + ] + } + ] + }, + { + "description": "Distinct with a filter", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/distinct.yml b/source/crud/tests/unified/distinct.yml new file mode 100644 index 0000000000..c554a7671b --- /dev/null +++ b/source/crud/tests/unified/distinct.yml @@ -0,0 +1,54 @@ +description: distinct + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'Distinct without a filter' + operations: + - + object: *collection0 + name: distinct + arguments: + fieldName: x + filter: { } + expectResult: + - 11 + - 22 + - 33 + - + description: 'Distinct with a filter' + operations: + - + object: *collection0 + name: distinct + arguments: + fieldName: x + filter: { _id: { $gt: 1 } } + expectResult: + - 22 + - 33 diff --git a/source/crud/tests/unified/estimatedDocumentCount-comment.json b/source/crud/tests/unified/estimatedDocumentCount-comment.json new file mode 100644 index 0000000000..6c0adacc8f --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/estimatedDocumentCount-comment.yml b/source/crud/tests/unified/estimatedDocumentCount-comment.yml new file mode 100644 index 0000000000..b785315104 --- /dev/null +++ b/source/crud/tests/unified/estimatedDocumentCount-comment.yml @@ -0,0 +1,95 @@ +description: "estimatedDocumentCount-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name edc-comment-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "estimatedDocumentCount with document comment" + runOnRequirements: + # https://jira.mongodb.org/browse/SERVER-63315 + # Server supports count with comment of any type for comment starting from 4.4.14. + - minServerVersion: "4.4.14" + operations: + - name: estimatedDocumentCount + object: *collection0 + arguments: + comment: &documentComment { key: "value"} + expectResult: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + comment: *documentComment + commandName: count + databaseName: *database0Name + + - description: "estimatedDocumentCount with string comment" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: estimatedDocumentCount + object: *collection0 + arguments: + comment: &stringComment "comment" + expectResult: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + comment: *stringComment + commandName: count + databaseName: *database0Name + + - description: "estimatedDocumentCount with document comment - pre 4.4.14, server error" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.4.13" + # Server does not raise an error if topology is sharded. + # https://jira.mongodb.org/browse/SERVER-65954 + topologies: [ single, replicaset ] + operations: + - name: estimatedDocumentCount + object: *collection0 + arguments: + # Even though according to the docs count command does not support any + # comment for server version less than 4.4, no error is raised by such + # servers. Therefore, we have only one test with a document comment + # to test server errors. + # https://jira.mongodb.org/browse/SERVER-63315 + # Server supports count with comment of any type for comment starting from 4.4.14. + comment: *documentComment + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + comment: *documentComment + commandName: count + databaseName: *database0Name diff --git a/source/crud/tests/unified/estimatedDocumentCount-rawdata.json b/source/crud/tests/unified/estimatedDocumentCount-rawdata.json new file mode 100644 index 0000000000..4c79863e98 --- /dev/null +++ b/source/crud/tests/unified/estimatedDocumentCount-rawdata.json @@ -0,0 +1,100 @@ +{ + "description": "estimatedDocumentCount-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Estimated document count with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": { + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "rawData": true + }, + "commandName": "count", + "databaseName": "crud-tests" + } + } + ] + } + ] + }, + { + "description": "Estimated document count with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": { + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll0", + "rawData": { + "$$exists": false + } + }, + "commandName": "count", + "databaseName": "crud-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/estimatedDocumentCount-rawdata.yml b/source/crud/tests/unified/estimatedDocumentCount-rawdata.yml new file mode 100644 index 0000000000..7c07a56990 --- /dev/null +++ b/source/crud/tests/unified/estimatedDocumentCount-rawdata.yml @@ -0,0 +1,53 @@ +description: "estimatedDocumentCount-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Estimated document count with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: estimatedDocumentCount + arguments: + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + rawData: *rawdata + commandName: count + databaseName: *database0Name + - description: "Estimated document count with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: estimatedDocumentCount + arguments: + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + rawData: + $$exists: false + commandName: count + databaseName: *database0Name diff --git a/source/crud/tests/unified/estimatedDocumentCount.json b/source/crud/tests/unified/estimatedDocumentCount.json new file mode 100644 index 0000000000..3577d9006b --- /dev/null +++ b/source/crud/tests/unified/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": { + "isClientError": 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/source/crud/tests/unified/estimatedDocumentCount.yml b/source/crud/tests/unified/estimatedDocumentCount.yml new file mode 100644 index 0000000000..22e1d3587f --- /dev/null +++ b/source/crud/tests/unified/estimatedDocumentCount.yml @@ -0,0 +1,181 @@ +description: "estimatedDocumentCount" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false # Avoid setting fail points with multiple mongoses + uriOptions: { retryReads: false } # Avoid retrying fail points with closeConnection + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name edc-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + # Nonexistent collection intentionally omitted from initialData + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + - collection: + id: &collection0View collection0View + database: *database0 + collectionName: &collection0ViewName coll0view + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *collection0Name + commandName: count + databaseName: *database0Name + + - description: "estimatedDocumentCount with maxTimeMS" + operations: + - name: estimatedDocumentCount + object: *collection0 + arguments: + maxTimeMS: 6000 + expectResult: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + maxTimeMS: 6000 + commandName: count + databaseName: *database0Name + + - description: "estimatedDocumentCount on non-existent collection" + operations: + - name: estimatedDocumentCount + object: *collection1 + expectResult: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection1Name + commandName: count + databaseName: *database0Name + + - 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 # UnknownError + - name: estimatedDocumentCount + object: *collection0 + expectError: + errorCode: 8 # UnknownError + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + commandName: count + databaseName: *database0Name + + - 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: + isClientError: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + count: *collection0Name + commandName: count + databaseName: *database0Name + + - description: "estimatedDocumentCount works correctly on views" + # viewOn option was added to the create command in 3.4 + runOnRequirements: + - minServerVersion: "3.4.0" + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0ViewName + - name: createCollection + object: *database0 + arguments: + collection: *collection0ViewName + viewOn: *collection0Name + pipeline: &pipeline + - { $match: { _id: { $gt: 1 } } } + - name: estimatedDocumentCount + object: *collection0View + expectResult: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0ViewName + commandName: drop + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0ViewName + viewOn: *collection0Name + pipeline: *pipeline + commandName: create + databaseName: *database0Name + - commandStartedEvent: + command: + count: *collection0ViewName + commandName: count + databaseName: *database0Name diff --git a/source/crud/tests/unified/find-allowdiskuse-clientError.json b/source/crud/tests/unified/find-allowdiskuse-clientError.json new file mode 100644 index 0000000000..5bd954e79d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/find-allowdiskuse-clientError.yml b/source/crud/tests/unified/find-allowdiskuse-clientError.yml new file mode 100644 index 0000000000..da7f6365f1 --- /dev/null +++ b/source/crud/tests/unified/find-allowdiskuse-clientError.yml @@ -0,0 +1,52 @@ +description: find-allowdiskuse-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.0.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name 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/source/crud/tests/unified/find-allowdiskuse-serverError.json b/source/crud/tests/unified/find-allowdiskuse-serverError.json new file mode 100644 index 0000000000..dc58f8f0e3 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/find-allowdiskuse-serverError.yml b/source/crud/tests/unified/find-allowdiskuse-serverError.yml new file mode 100644 index 0000000000..86f48d18e5 --- /dev/null +++ b/source/crud/tests/unified/find-allowdiskuse-serverError.yml @@ -0,0 +1,65 @@ +description: find-allowdiskuse-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: '3.2' + maxServerVersion: 4.3.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &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: *collection0 + name: find + arguments: + filter: &filter { } + allowDiskUse: true + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + filter: *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: *filter + allowDiskUse: false + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + filter: *filter + allowDiskUse: false diff --git a/source/crud/tests/unified/find-allowdiskuse.json b/source/crud/tests/unified/find-allowdiskuse.json new file mode 100644 index 0000000000..eb238ab93a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/find-allowdiskuse.yml b/source/crud/tests/unified/find-allowdiskuse.yml new file mode 100644 index 0000000000..2f4190b9c3 --- /dev/null +++ b/source/crud/tests/unified/find-allowdiskuse.yml @@ -0,0 +1,76 @@ +description: find-allowdiskuse +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.1 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name 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: *collection_name + 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: *collection_name + 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: *collection_name + allowDiskUse: true diff --git a/source/crud/tests/unified/find-collation.json b/source/crud/tests/unified/find-collation.json new file mode 100644 index 0000000000..13b105ad5a --- /dev/null +++ b/source/crud/tests/unified/find-collation.json @@ -0,0 +1,69 @@ +{ + "description": "find-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "Find with a collation", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "x": "PING" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": [ + { + "_id": 1, + "x": "ping" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/find-collation.yml b/source/crud/tests/unified/find-collation.yml new file mode 100644 index 0000000000..9810b60970 --- /dev/null +++ b/source/crud/tests/unified/find-collation.yml @@ -0,0 +1,46 @@ +description: find-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: ping } + +tests: + - + description: 'Find with a collation' + operations: + - + object: *collection0 + name: find + arguments: + filter: { x: PING } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + - { _id: 1, x: ping } diff --git a/source/crud/tests/unified/find-comment.json b/source/crud/tests/unified/find-comment.json new file mode 100644 index 0000000000..600a3723f1 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/find-comment.yml b/source/crud/tests/unified/find-comment.yml new file mode 100644 index 0000000000..905241ad0e --- /dev/null +++ b/source/crud/tests/unified/find-comment.yml @@ -0,0 +1,166 @@ +description: "find-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + _id: 1 + comment: "comment" + expectResult: &expect_result + - { _id: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: "comment" + + - description: "find with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: find + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value"} + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: *comment + + - description: "find with document comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + minServerVersion: "3.6" + operations: + - name: find + object: *collection0 + arguments: + filter: *filter + comment: *comment + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: *comment + + - description: "find with comment sets comment on getMore" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: find + object: *collection0 + arguments: + filter: &filter_get_more { _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: *collection0Name + filter: { _id: { $gt: 1 } } + batchSize: 2 + comment: *comment + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment + + - 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: &filter_get_more { _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: *collection0Name + filter: { _id: { $gt: 1 } } + batchSize: 2 + comment: "comment" + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } diff --git a/source/crud/tests/unified/find-let.json b/source/crud/tests/unified/find-let.json new file mode 100644 index 0000000000..4e9c9c99f4 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/find-let.yml b/source/crud/tests/unified/find-let.yml new file mode 100644 index 0000000000..20afe85c87 --- /dev/null +++ b/source/crud/tests/unified/find-let.yml @@ -0,0 +1,71 @@ +description: "find-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "Find with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: find + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + let: &let0 + id: 1 + expectResult: + - { _id: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + let: *let0 + + - 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: &filter1 + _id: 1 + let: &let1 + x: 1 + expectError: + errorContains: "Unrecognized field 'let'" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter1 + let: *let1 diff --git a/source/crud/tests/unified/find-rawdata.json b/source/crud/tests/unified/find-rawdata.json new file mode 100644 index 0000000000..778e289880 --- /dev/null +++ b/source/crud/tests/unified/find-rawdata.json @@ -0,0 +1,100 @@ +{ + "description": "find-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Find with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "Find with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {}, + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/find-rawdata.yml b/source/crud/tests/unified/find-rawdata.yml new file mode 100644 index 0000000000..f260d33943 --- /dev/null +++ b/source/crud/tests/unified/find-rawdata.yml @@ -0,0 +1,53 @@ +description: "find-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Find with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: find + object: *collection0 + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: {} + rawData: *rawdata + - description: "Find with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: find + object: *collection0 + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: {} + rawData: + $$exists: false diff --git a/source/crud/tests/unified/find.json b/source/crud/tests/unified/find.json new file mode 100644 index 0000000000..325cd96c21 --- /dev/null +++ b/source/crud/tests/unified/find.json @@ -0,0 +1,304 @@ +{ + "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" + } + } + ] + } + ] + }, + { + "description": "Find with filter", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "Find with filter, sort, skip, and limit", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": { + "$gt": 2 + } + }, + "sort": { + "_id": 1 + }, + "skip": 2, + "limit": 2 + }, + "expectResult": [ + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ] + }, + { + "description": "Find with limit, sort, and batchsize", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4, + "batchSize": 2 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "Find with batchSize equal to limit", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": 1 + }, + "limit": 4, + "batchSize": 4 + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "limit": 4, + "batchSize": 5 + }, + "commandName": "find", + "databaseName": "find-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/find.yml b/source/crud/tests/unified/find.yml new file mode 100644 index 0000000000..3a09c4d830 --- /dev/null +++ b/source/crud/tests/unified/find.yml @@ -0,0 +1,135 @@ +description: "find" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true # ensure cursors pin to a single server + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name find-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: *collection0Name + filter: { _id: { $gt: 1 } } + batchSize: 2 + commandName: find + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + commandName: getMore + databaseName: *database0Name + - + description: 'Find with filter' + operations: + - + object: *collection0 + name: find + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + - + description: 'Find with filter, sort, skip, and limit' + operations: + - + object: *collection0 + name: find + arguments: + filter: { _id: { $gt: 2 } } + sort: { _id: 1 } + skip: 2 + limit: 2 + expectResult: + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + - + description: 'Find with limit, sort, and batchsize' + operations: + - + object: *collection0 + name: find + arguments: + filter: { } + sort: { _id: 1 } + limit: 4 + batchSize: 2 + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'Find with batchSize equal to limit' + operations: + - + object: *collection0 + name: find + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: 1 } + limit: 4 + batchSize: 4 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + limit: 4 + # Drivers use limit + 1 for batchSize to ensure the server closes the cursor + batchSize: 5 + commandName: find + databaseName: *database0Name diff --git a/source/crud/tests/unified/findOne.json b/source/crud/tests/unified/findOne.json new file mode 100644 index 0000000000..826c0f5dfd --- /dev/null +++ b/source/crud/tests/unified/findOne.json @@ -0,0 +1,158 @@ +{ + "description": "findOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "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": "FindOne with filter", + "operations": [ + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "batchSize": { + "$$exists": false + }, + "limit": 1, + "singleBatch": true + }, + "commandName": "find", + "databaseName": "find-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne with filter, sort, and skip", + "operations": [ + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": { + "$gt": 2 + } + }, + "sort": { + "_id": 1 + }, + "skip": 2 + }, + "expectResult": { + "_id": 5, + "x": 55 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 2 + } + }, + "sort": { + "_id": 1 + }, + "skip": 2, + "batchSize": { + "$$exists": false + }, + "limit": 1, + "singleBatch": true + }, + "commandName": "find", + "databaseName": "find-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOne.yml b/source/crud/tests/unified/findOne.yml new file mode 100644 index 0000000000..ed74124bf3 --- /dev/null +++ b/source/crud/tests/unified/findOne.yml @@ -0,0 +1,75 @@ +description: "findOne" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name find-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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: 'FindOne with filter' + operations: + - + object: *collection0 + name: findOne + arguments: + filter: { _id: 1 } + expectResult: { _id: 1, x: 11 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: 1 } + batchSize: { $$exists: false } + limit: 1 + singleBatch: true + commandName: find + databaseName: *database0Name + - + description: 'FindOne with filter, sort, and skip' + operations: + - + object: *collection0 + name: findOne + arguments: + filter: { _id: { $gt: 2 } } + sort: { _id: 1 } + skip: 2 + expectResult: { _id: 5, x: 55 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 2 } } + sort: { _id: 1 } + skip: 2 + batchSize: { $$exists: false } + limit: 1 + singleBatch: true + commandName: find + databaseName: *database0Name diff --git a/source/crud/tests/unified/findOneAndDelete-collation.json b/source/crud/tests/unified/findOneAndDelete-collation.json new file mode 100644 index 0000000000..a0452876a3 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-collation.json @@ -0,0 +1,98 @@ +{ + "description": "findOneAndDelete-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete when one document matches with collation", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 2, + "x": "PING" + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "x": "ping" + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndDelete-collation.yml b/source/crud/tests/unified/findOneAndDelete-collation.yml new file mode 100644 index 0000000000..2e6de9b08e --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-collation.yml @@ -0,0 +1,56 @@ +description: findOneAndDelete-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + +tests: + - + description: 'FindOneAndDelete when one document matches with collation' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { _id: 2, x: PING } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: { x: ping } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: pINg } diff --git a/source/crud/tests/unified/findOneAndDelete-comment.json b/source/crud/tests/unified/findOneAndDelete-comment.json new file mode 100644 index 0000000000..6853b9cc2d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-comment.yml b/source/crud/tests/unified/findOneAndDelete-comment.yml new file mode 100644 index 0000000000..dfa7ff2ce0 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-comment.yml @@ -0,0 +1,96 @@ +description: "findOneAndDelete-comment" +schemaVersion: "1.0" + + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndDelete with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: &filter + _id: 1 + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 2 } + + - description: "findOneAndDelete with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: *comment + outcome: *outcome + + - description: "findOneAndDelete with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/findOneAndDelete-hint-clientError.json b/source/crud/tests/unified/findOneAndDelete-hint-clientError.json new file mode 100644 index 0000000000..c6ff467866 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-hint-clientError.yml b/source/crud/tests/unified/findOneAndDelete-hint-clientError.yml new file mode 100644 index 0000000000..46c7521ad8 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-hint-clientError.yml @@ -0,0 +1,88 @@ +description: findOneAndDelete-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 4.0.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndDelete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + description: 'FindOneAndDelete with hint document' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: &filter + _id: 1 + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 diff --git a/source/crud/tests/unified/findOneAndDelete-hint-serverError.json b/source/crud/tests/unified/findOneAndDelete-hint-serverError.json new file mode 100644 index 0000000000..b874102728 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-hint-serverError.yml b/source/crud/tests/unified/findOneAndDelete-hint-serverError.yml new file mode 100644 index 0000000000..db75131545 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-hint-serverError.yml @@ -0,0 +1,104 @@ +description: findOneAndDelete-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 + maxServerVersion: 4.3.3 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndDelete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + hint: _id_ + remove: true + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + hint: + _id: 1 + remove: true + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 diff --git a/source/crud/tests/unified/findOneAndDelete-hint-unacknowledged.json b/source/crud/tests/unified/findOneAndDelete-hint-unacknowledged.json new file mode 100644 index 0000000000..077f9892b9 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-hint-unacknowledged.yml b/source/crud/tests/unified/findOneAndDelete-hint-unacknowledged.yml new file mode 100644 index 0000000000..2b29a4923e --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-hint-unacknowledged.yml @@ -0,0 +1,88 @@ +description: findOneAndDelete-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged findOneAndDelete with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: findOneAndDelete + arguments: + filter: *filter + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: null } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + 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: *filter + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/findOneAndDelete-hint.json b/source/crud/tests/unified/findOneAndDelete-hint.json new file mode 100644 index 0000000000..8b53f2bd3f --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-hint.yml b/source/crud/tests/unified/findOneAndDelete-hint.yml new file mode 100644 index 0000000000..9090779831 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-hint.yml @@ -0,0 +1,99 @@ +description: findOneAndDelete-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.4 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndDelete_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'FindOneAndDelete with hint string' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: &filter + _id: 1 + hint: _id_ + expectResult: &result + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + hint: _id_ + remove: true + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 2 + x: 22 + - + description: 'FindOneAndDelete with hint document' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: &filter + _id: 1 + hint: + _id: 1 + expectResult: &result + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + hint: + _id: 1 + remove: true + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 2 + x: 22 diff --git a/source/crud/tests/unified/findOneAndDelete-let.json b/source/crud/tests/unified/findOneAndDelete-let.json new file mode 100644 index 0000000000..ba8e681c0e --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndDelete-let.yml b/source/crud/tests/unified/findOneAndDelete-let.yml new file mode 100644 index 0000000000..0174af0a2b --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-let.yml @@ -0,0 +1,86 @@ +description: "findOneAndDelete-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndDelete with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + let: &let0 + id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + let: *let0 + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + $expr: + $eq: [ "$_id", "$$id" ] + let: &let1 + id: 1 + expectError: + # This error message is consistent between 4.2.x and 4.4.x servers. + # Older servers return a different error message. + errorContains: "field 'let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter1 + remove: true + let: *let1 + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + diff --git a/source/crud/tests/unified/findOneAndDelete-rawdata.json b/source/crud/tests/unified/findOneAndDelete-rawdata.json new file mode 100644 index 0000000000..1ace780b74 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-rawdata.json @@ -0,0 +1,102 @@ +{ + "description": "findOneAndDelete-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "findOneAndDelete with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "remove": true, + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndDelete with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": {}, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "remove": true, + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndDelete-rawdata.yml b/source/crud/tests/unified/findOneAndDelete-rawdata.yml new file mode 100644 index 0000000000..27f52321ed --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete-rawdata.yml @@ -0,0 +1,55 @@ +description: "findOneAndDelete-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "findOneAndDelete with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: {} + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + remove: true + rawData: *rawdata + - description: "findOneAndDelete with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: {} + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + remove: true + rawData: + $$exists: false diff --git a/source/crud/tests/unified/findOneAndDelete.json b/source/crud/tests/unified/findOneAndDelete.json new file mode 100644 index 0000000000..e434b3b740 --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete.json @@ -0,0 +1,171 @@ +{ + "description": "findOneAndDelete", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndDelete when many documents match", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete when one document matches", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 2 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete when no documents match", + "operations": [ + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "_id": 4 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndDelete.yml b/source/crud/tests/unified/findOneAndDelete.yml new file mode 100644 index 0000000000..6bb3a1ccca --- /dev/null +++ b/source/crud/tests/unified/findOneAndDelete.yml @@ -0,0 +1,84 @@ +description: findOneAndDelete + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'FindOneAndDelete when many documents match' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { _id: { $gt: 1 } } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndDelete when one document matches' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { _id: 2 } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndDelete when no documents match' + operations: + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { _id: 4 } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/findOneAndReplace-collation.json b/source/crud/tests/unified/findOneAndReplace-collation.json new file mode 100644 index 0000000000..0d60d54164 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-collation.json @@ -0,0 +1,97 @@ +{ + "description": "findOneAndReplace-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace when one document matches with collation returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "x": "PING" + }, + "replacement": { + "x": "pong" + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "x": "pong" + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "pong" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndReplace-collation.yml b/source/crud/tests/unified/findOneAndReplace-collation.yml new file mode 100644 index 0000000000..c3d8a8e78f --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-collation.yml @@ -0,0 +1,57 @@ +description: findOneAndReplace-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + +tests: + - + description: 'FindOneAndReplace when one document matches with collation returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { x: PING } + replacement: { x: pong } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: { x: pong } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: pong } diff --git a/source/crud/tests/unified/findOneAndReplace-comment.json b/source/crud/tests/unified/findOneAndReplace-comment.json new file mode 100644 index 0000000000..f817bb6937 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-comment.yml b/source/crud/tests/unified/findOneAndReplace-comment.yml new file mode 100644 index 0000000000..2c6aaef07b --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-comment.yml @@ -0,0 +1,101 @@ +description: "findOneAndReplace-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndReplace with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: &filter + _id: 1 + replacement: &replacement + x: 5 + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 5 } + - { _id: 2 } + + - description: "findOneAndReplace with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: *comment + outcome: *outcome + + + - description: "findOneAndReplace with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/findOneAndReplace-dots_and_dollars.json b/source/crud/tests/unified/findOneAndReplace-dots_and_dollars.json new file mode 100644 index 0000000000..19ac447f84 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml b/source/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml new file mode 100644 index 0000000000..0823824cca --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-dots_and_dollars.yml @@ -0,0 +1,140 @@ +description: "findOneAndReplace-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - &initialDocument { _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: &dottedKey { _id: 1, a.b: 1 } + expectResult: *initialDocument + expectEvents: &expectEventsDottedKey + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dottedKey + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - 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: *dottedKey + expectError: + isClientError: false + expectEvents: *expectEventsDottedKey + outcome: *initialData + + - 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: &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *initialDocument + expectEvents: &expectEventsDollarPrefixedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dollarPrefixedKeyInEmbedded + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - 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: *dollarPrefixedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKeyInEmbedded + outcome: *initialData + + - 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: &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *initialDocument + expectEvents: &expectEventsDottedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dottedKeyInEmbedded + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded + + - 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: *dottedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDottedKeyInEmbedded + outcome: *initialData diff --git a/source/crud/tests/unified/findOneAndReplace-hint-clientError.json b/source/crud/tests/unified/findOneAndReplace-hint-clientError.json new file mode 100644 index 0000000000..6b07eb1f4d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-hint-clientError.yml b/source/crud/tests/unified/findOneAndReplace-hint-clientError.yml new file mode 100644 index 0000000000..a0a8f94beb --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-hint-clientError.yml @@ -0,0 +1,80 @@ +description: findOneAndReplace-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 4.0.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndReplace_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + replacement: &replacement + x: 33 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + replacement: *replacement + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndReplace-hint-serverError.json b/source/crud/tests/unified/findOneAndReplace-hint-serverError.json new file mode 100644 index 0000000000..7fbf5a0ea3 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-hint-serverError.yml b/source/crud/tests/unified/findOneAndReplace-hint-serverError.yml new file mode 100644 index 0000000000..1fea6f364b --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-hint-serverError.yml @@ -0,0 +1,96 @@ +description: findOneAndReplace-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 + maxServerVersion: 4.3.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndReplace_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + replacement: &replacement + x: 33 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *replacement + hint: _id_ + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + replacement: *replacement + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *replacement + hint: + _id: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndReplace-hint-unacknowledged.json b/source/crud/tests/unified/findOneAndReplace-hint-unacknowledged.json new file mode 100644 index 0000000000..8228d8a2aa --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-hint-unacknowledged.yml b/source/crud/tests/unified/findOneAndReplace-hint-unacknowledged.yml new file mode 100644 index 0000000000..20a2737de2 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-hint-unacknowledged.yml @@ -0,0 +1,96 @@ +description: findOneAndReplace-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: *collection0Name + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + replacement: &replacement { x: 111 } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged findOneAndReplace with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: findOneAndReplace + arguments: + filter: *filter + replacement: *replacement + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: null } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/findOneAndReplace-hint.json b/source/crud/tests/unified/findOneAndReplace-hint.json new file mode 100644 index 0000000000..d07c5921a7 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-hint.yml b/source/crud/tests/unified/findOneAndReplace-hint.yml new file mode 100644 index 0000000000..67fbf2cb2b --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-hint.yml @@ -0,0 +1,95 @@ +description: findOneAndReplace-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.1 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndReplace_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'FindOneAndReplace with hint string' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: &filter + _id: 1 + replacement: &replacement + x: 33 + hint: _id_ + expectResult: &result + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *replacement + hint: _id_ + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 33 + - + _id: 2 + x: 22 + - + description: 'FindOneAndReplace with hint document' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: *filter + replacement: *replacement + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *replacement + hint: + _id: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndReplace-let.json b/source/crud/tests/unified/findOneAndReplace-let.json new file mode 100644 index 0000000000..5e5de44b31 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndReplace-let.yml b/source/crud/tests/unified/findOneAndReplace-let.yml new file mode 100644 index 0000000000..3ee1b7b672 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-let.yml @@ -0,0 +1,94 @@ +description: "findOneAndReplace-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndReplace with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + replacement: &replacement + x: "x" + let: &let0 + id: 1 + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + $expr: + $eq: [ "$_id", "$$id" ] + replacement: &replacement1 + x: "x" + let: &let1 + id: 1 + expectError: + # This error message is consistent between 4.2.x and 4.4.x servers. + # Older servers return a different error message. + errorContains: "field 'let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter1 + update: *replacement1 + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/findOneAndReplace-rawdata.json b/source/crud/tests/unified/findOneAndReplace-rawdata.json new file mode 100644 index 0000000000..b48a2a4210 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-rawdata.json @@ -0,0 +1,112 @@ +{ + "description": "findOneAndReplace-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "findOneAndReplace with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": {}, + "replacement": { + "x": "x" + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "update": { + "x": "x" + }, + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndReplace with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": {}, + "replacement": { + "x": "x" + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "update": { + "x": "x" + }, + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndReplace-rawdata.yml b/source/crud/tests/unified/findOneAndReplace-rawdata.yml new file mode 100644 index 0000000000..4007a0fe53 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-rawdata.yml @@ -0,0 +1,57 @@ +description: "findOneAndReplace-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "findOneAndReplace with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: {} + replacement: &replacement { x: x } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + update: *replacement + rawData: *rawdata + - description: "findOneAndReplace with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: {} + replacement: &replacement { x: x } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + update: *replacement + rawData: + $$exists: false diff --git a/source/crud/tests/unified/findOneAndReplace-upsert.json b/source/crud/tests/unified/findOneAndReplace-upsert.json new file mode 100644 index 0000000000..f1f18996c8 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-upsert.json @@ -0,0 +1,254 @@ +{ + "description": "findOneAndReplace-upsert", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "upsert": true + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + }, + "upsert": true + }, + "expectResult": { + "x": 44 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "upsert": true + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + }, + "upsert": true + }, + "expectResult": { + "x": 44 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndReplace-upsert.yml b/source/crud/tests/unified/findOneAndReplace-upsert.yml new file mode 100644 index 0000000000..c52fdf11b1 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace-upsert.yml @@ -0,0 +1,127 @@ +description: findOneAndReplace-upsert + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '2.6' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'FindOneAndReplace when no documents match without id specified with upsert returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { x: 44 } + projection: { x: 1, _id: 0 } + # Omit the sort option as it has no effect when no documents match and + # would only cause an inconsistent return value on pre-3.0 servers + # when combined with returnDocument "before" (see: SERVER-17650). + upsert: true + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'FindOneAndReplace when no documents match without id specified with upsert returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { x: 44 } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + upsert: true + expectResult: { x: 44 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'FindOneAndReplace when no documents match with id specified with upsert returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { _id: 4, x: 44 } + projection: { x: 1, _id: 0 } + # Omit the sort option as it has no effect when no documents match and + # would only cause an inconsistent return value on pre-3.0 servers + # when combined with returnDocument "before" (see: SERVER-17650). + upsert: true + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - + description: 'FindOneAndReplace when no documents match with id specified with upsert returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { _id: 4, x: 44 } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + upsert: true + expectResult: { x: 44 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } diff --git a/source/crud/tests/unified/findOneAndReplace.json b/source/crud/tests/unified/findOneAndReplace.json new file mode 100644 index 0000000000..a4731602c4 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace.json @@ -0,0 +1,332 @@ +{ + "description": "findOneAndReplace", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndReplace when many documents match returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 32 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 32 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when many documents match returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 32 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 32 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 32 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when one document matches returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 2 + }, + "replacement": { + "x": 32 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 32 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when one document matches returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 2 + }, + "replacement": { + "x": 32 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 32 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 32 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when no documents match returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace when no documents match returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndReplace.yml b/source/crud/tests/unified/findOneAndReplace.yml new file mode 100644 index 0000000000..ddfd533558 --- /dev/null +++ b/source/crud/tests/unified/findOneAndReplace.yml @@ -0,0 +1,152 @@ +description: findOneAndReplace + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'FindOneAndReplace when many documents match returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: { $gt: 1 } } + replacement: { x: 32 } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 32 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndReplace when many documents match returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: { $gt: 1 } } + replacement: { x: 32 } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: { x: 32 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 32 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndReplace when one document matches returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 2 } + replacement: { x: 32 } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 32 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndReplace when one document matches returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 2 } + replacement: { x: 32 } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: { x: 32 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 32 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndReplace when no documents match returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { x: 44 } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndReplace when no documents match returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 4 } + replacement: { x: 44 } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/findOneAndUpdate-arrayFilters.json b/source/crud/tests/unified/findOneAndUpdate-arrayFilters.json new file mode 100644 index 0000000000..6c99e4ff66 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-arrayFilters.json @@ -0,0 +1,251 @@ +{ + "description": "findOneAndUpdate-arrayFilters", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.5.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate when no document matches arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 4 + } + ] + }, + "expectResult": { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when one document matches arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + }, + "expectResult": { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 2 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when multiple documents match arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + }, + "expectResult": { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 2 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndUpdate-arrayFilters.yml b/source/crud/tests/unified/findOneAndUpdate-arrayFilters.yml new file mode 100644 index 0000000000..42e392c2c5 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-arrayFilters.yml @@ -0,0 +1,89 @@ +description: findOneAndUpdate-arrayFilters + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: 3.5.6 + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + +tests: + - + description: 'FindOneAndUpdate when no document matches arrayFilters' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 4 } + expectResult: { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - + description: 'FindOneAndUpdate when one document matches arrayFilters' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 3 } + expectResult: { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 2 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - + description: 'FindOneAndUpdate when multiple documents match arrayFilters' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 1 } + expectResult: { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 2 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } diff --git a/source/crud/tests/unified/findOneAndUpdate-collation.json b/source/crud/tests/unified/findOneAndUpdate-collation.json new file mode 100644 index 0000000000..7a49347a3a --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-collation.json @@ -0,0 +1,106 @@ +{ + "description": "findOneAndUpdate-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate when many documents match with collation returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "x": "PING" + }, + "update": { + "$set": { + "x": "pong" + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "_id": 1 + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "x": "ping" + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "pong" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndUpdate-collation.yml b/source/crud/tests/unified/findOneAndUpdate-collation.yml new file mode 100644 index 0000000000..8b2048bc82 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-collation.yml @@ -0,0 +1,58 @@ +description: findOneAndUpdate-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + +tests: + - + description: 'FindOneAndUpdate when many documents match with collation returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { x: PING } + update: { $set: { x: pong } } + projection: { x: 1, _id: 0 } + sort: { _id: 1 } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: { x: ping } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: pong } + - { _id: 3, x: pINg } diff --git a/source/crud/tests/unified/findOneAndUpdate-comment.json b/source/crud/tests/unified/findOneAndUpdate-comment.json new file mode 100644 index 0000000000..6dec5b39ee --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-comment.yml b/source/crud/tests/unified/findOneAndUpdate-comment.yml new file mode 100644 index 0000000000..66d2d099c1 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-comment.yml @@ -0,0 +1,95 @@ +description: "findOneAndUpdate-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndUpdate with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: {x: 5 } + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: "comment" + + - description: "findOneAndUpdate with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: {x: 5 } + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: *comment + + - description: "findOneAndUpdate with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json b/source/crud/tests/unified/findOneAndUpdate-dots_and_dollars.json new file mode 100644 index 0000000000..40eb547392 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml b/source/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml new file mode 100644 index 0000000000..69b46a15bc --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-dots_and_dollars.yml @@ -0,0 +1,127 @@ +description: "findOneAndUpdate-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - &initialDocument { _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: &dollarPrefixedKey + - { $replaceWith: { $setField: { field: { $literal: $a }, value: 1, input: $$ROOT } } } + expectResult: *initialDocument + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dollarPrefixedKey + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey + - { $replaceWith: { $setField: { field: { $literal: a.b }, value: 1, input: $$ROOT } } } + expectResult: *initialDocument + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dottedKey + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: $a }, value: 1, input: $foo } } } } + expectResult: *initialDocument + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dollarPrefixedKeyInEmbedded + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: a.b }, value: 1, input: $foo } } } } + expectResult: *initialDocument + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: *dottedKeyInEmbedded + new: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, foo: { a.b: 1 } } diff --git a/source/crud/tests/unified/findOneAndUpdate-errorResponse.json b/source/crud/tests/unified/findOneAndUpdate-errorResponse.json new file mode 100644 index 0000000000..5023a450f3 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-errorResponse.yml b/source/crud/tests/unified/findOneAndUpdate-errorResponse.yml new file mode 100644 index 0000000000..8faed76809 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-errorResponse.yml @@ -0,0 +1,69 @@ +description: "findOneAndUpdate-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: "foo" } + +tests: + - description: "findOneAndUpdate DuplicateKey error is accessible" + runOnRequirements: + - minServerVersion: "4.2" # SERVER-37124 + 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 # DuplicateKey + 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: *collection0Name + validator: + x: { $type: "string" } + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 } } + expectError: + errorCode: 121 # DocumentValidationFailure + errorResponse: + # Avoid asserting the exact contents of errInfo as it may vary by + # server version. Likewise, this is why drivers do not model the + # document. The following is sufficient to test that validation + # details are accessible. See SERVER-20547 for more context. + errInfo: + failingDocumentId: 1 + details: { $$type: "object" } diff --git a/source/crud/tests/unified/findOneAndUpdate-hint-clientError.json b/source/crud/tests/unified/findOneAndUpdate-hint-clientError.json new file mode 100644 index 0000000000..d0b51313c9 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-hint-clientError.yml b/source/crud/tests/unified/findOneAndUpdate-hint-clientError.yml new file mode 100644 index 0000000000..77b41a165c --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-hint-clientError.yml @@ -0,0 +1,81 @@ +description: findOneAndUpdate-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 4.0.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndUpdate_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndUpdate-hint-serverError.json b/source/crud/tests/unified/findOneAndUpdate-hint-serverError.json new file mode 100644 index 0000000000..99fd9938f8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-hint-serverError.yml b/source/crud/tests/unified/findOneAndUpdate-hint-serverError.yml new file mode 100644 index 0000000000..13abba5f5c --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-hint-serverError.yml @@ -0,0 +1,97 @@ +description: findOneAndUpdate-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 + maxServerVersion: 4.3.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndUpdate_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *update + hint: _id_ + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *update + hint: + _id: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.json b/source/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.json new file mode 100644 index 0000000000..d116a06d0d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.yml b/source/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.yml new file mode 100644 index 0000000000..ec576375a6 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.yml @@ -0,0 +1,92 @@ +description: findOneAndUpdate-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + update: &update { $inc: { x: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + update: *update + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged findOneAndUpdate with hint string on 4.4+ server" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - object: *collection0 + name: findOneAndUpdate + arguments: + filter: *filter + update: *update + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: null } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + 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: *filter + update: *update + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/findOneAndUpdate-hint.json b/source/crud/tests/unified/findOneAndUpdate-hint.json new file mode 100644 index 0000000000..5be6d2b3e8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-hint.yml b/source/crud/tests/unified/findOneAndUpdate-hint.yml new file mode 100644 index 0000000000..f7a3c33f08 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-hint.yml @@ -0,0 +1,96 @@ +description: findOneAndUpdate-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.3.1 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name findOneAndUpdate_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'FindOneAndUpdate with hint string' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: &filter + _id: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectResult: &result + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *update + hint: _id_ + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 12 + - + _id: 2 + x: 22 + - + description: 'FindOneAndUpdate with hint document' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: *filter + update: *update + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: *filter + update: *update + hint: + _id: 1 + outcome: *outcome diff --git a/source/crud/tests/unified/findOneAndUpdate-let.json b/source/crud/tests/unified/findOneAndUpdate-let.json new file mode 100644 index 0000000000..74d7d0e58b --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/findOneAndUpdate-let.yml b/source/crud/tests/unified/findOneAndUpdate-let.yml new file mode 100644 index 0000000000..761001db9e --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-let.yml @@ -0,0 +1,96 @@ +description: "findOneAndUpdate-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndUpdate with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + update: &update + - $set: {x: "$$x" } + let: &let0 + id: 1 + x: "foo" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + $expr: + $eq: [ "$_id", "$$id" ] + update: &update1 + - $set: {x: "$$x"} + let: &let1 + id: 1 + x: "foo" + expectError: + # This error message is consistent between 4.2.x and 4.4.x servers. + # Older servers return a different error message. + errorContains: "field 'let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter1 + update: *update1 + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/findOneAndUpdate-pipeline.json b/source/crud/tests/unified/findOneAndUpdate-pipeline.json new file mode 100644 index 0000000000..81dba9ae93 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-pipeline.json @@ -0,0 +1,130 @@ +{ + "description": "findOneAndUpdate-pipeline", + "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": "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 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndUpdate-pipeline.yml b/source/crud/tests/unified/findOneAndUpdate-pipeline.yml new file mode 100644 index 0000000000..6a8dfed6c5 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-pipeline.yml @@ -0,0 +1,56 @@ +description: findOneAndUpdate-pipeline + +schemaVersion: '1.0' + +runOnRequirements: + - minServerVersion: 4.1.11 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, y: 1, t: { u: { v: 1 } } } + - { _id: 2, x: 2, y: 1 } + +tests: + - + 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: *collection_name + update: + - { $project: { x: 1 } } + - { $addFields: { foo: 1 } } + commandName: findAndModify + databaseName: *database_name + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, foo: 1 } + - { _id: 2, x: 2, y: 1 } diff --git a/source/crud/tests/unified/findOneAndUpdate-rawdata.json b/source/crud/tests/unified/findOneAndUpdate-rawdata.json new file mode 100644 index 0000000000..a689e73f7b --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-rawdata.json @@ -0,0 +1,129 @@ +{ + "description": "findOneAndUpdate-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "findOneAndUpdate with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": {}, + "update": [ + { + "$set": { + "x": 1 + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndUpdate-rawdata.yml b/source/crud/tests/unified/findOneAndUpdate-rawdata.yml new file mode 100644 index 0000000000..fb99905e2d --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate-rawdata.yml @@ -0,0 +1,57 @@ +description: "findOneAndUpdate-rawData" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "findOneAndUpdate with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: {} + update: &update [ $set: { x: 1 } ] + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + update: *update + rawData: *rawdata + - description: "findOneAndUpdate with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - minServerVersion: "4.2.0" + maxServerVersion: "8.1.99" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: {} + update: &update [ $set: { x: 1 } ] + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: {} + update: *update + rawData: + $$exists: false diff --git a/source/crud/tests/unified/findOneAndUpdate.json b/source/crud/tests/unified/findOneAndUpdate.json new file mode 100644 index 0000000000..d79cf8ac5b --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate.json @@ -0,0 +1,448 @@ +{ + "description": "findOneAndUpdate", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate when many documents match returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when many documents match returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 23 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when one document matches returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 22 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when one document matches returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": { + "x": 23 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when no documents match returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "sort": { + "x": 1 + } + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when no documents match with upsert returning the document before modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "upsert": true + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when no documents match returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + } + }, + "expectResult": null + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate when no documents match with upsert returning the document after modification", + "operations": [ + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "projection": { + "x": 1, + "_id": 0 + }, + "returnDocument": "After", + "sort": { + "x": 1 + }, + "upsert": true + }, + "expectResult": { + "x": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/findOneAndUpdate.yml b/source/crud/tests/unified/findOneAndUpdate.yml new file mode 100644 index 0000000000..181c6b8b54 --- /dev/null +++ b/source/crud/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,199 @@ +description: findOneAndUpdate + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'FindOneAndUpdate when many documents match returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when many documents match returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: { x: 23 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when one document matches returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: { x: 22 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when one document matches returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: { x: 23 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when no documents match returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + sort: { x: 1 } + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when no documents match with upsert returning the document before modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + # Omit the sort option as it has no effect when no documents match and + # would only cause an inconsistent return value on pre-3.0 servers + # when combined with returnDocument "before" (see: SERVER-17650). + upsert: true + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } + - + description: 'FindOneAndUpdate when no documents match returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + expectResult: null + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'FindOneAndUpdate when no documents match with upsert returning the document after modification' + operations: + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + projection: { x: 1, _id: 0 } + returnDocument: After + sort: { x: 1 } + upsert: true + expectResult: { x: 1 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } diff --git a/source/crud/tests/unified/insertMany-comment.json b/source/crud/tests/unified/insertMany-comment.json new file mode 100644 index 0000000000..2b4c80b3f0 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/insertMany-comment.yml b/source/crud/tests/unified/insertMany-comment.yml new file mode 100644 index 0000000000..4b51584df2 --- /dev/null +++ b/source/crud/tests/unified/insertMany-comment.yml @@ -0,0 +1,93 @@ +description: "insertMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "insertMany with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - &document { _id: 2, x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + 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: + - *document + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: *comment + outcome: *outcome + + - description: "insertMany with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - *document + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/insertMany-dots_and_dollars.json b/source/crud/tests/unified/insertMany-dots_and_dollars.json new file mode 100644 index 0000000000..eed8997df9 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/insertMany-dots_and_dollars.yml b/source/crud/tests/unified/insertMany-dots_and_dollars.yml new file mode 100644 index 0000000000..913a55e4c0 --- /dev/null +++ b/source/crud/tests/unified/insertMany-dots_and_dollars.yml @@ -0,0 +1,128 @@ +description: "insertMany-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: + - &dollarPrefixedKey { _id: 1, $a: 1 } + expectResult: &insertResult + # InsertManyResult is optional because all of its fields are optional + $$unsetOrMatches: { insertedIds: { $$unsetOrMatches: { 0: 1 } } } + expectEvents: &expectEventsDollarPrefixedKey + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKey + + - 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: + - *dollarPrefixedKey + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKey + outcome: *initialData + + - description: "Inserting document with top-level dotted key" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - &dottedKey { _id: 1, a.b: 1 } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - description: "Inserting document with dollar-prefixed key in embedded doc" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - description: "Inserting document with dotted key in embedded doc" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded diff --git a/source/crud/tests/unified/insertMany-rawdata.json b/source/crud/tests/unified/insertMany-rawdata.json new file mode 100644 index 0000000000..50852592df --- /dev/null +++ b/source/crud/tests/unified/insertMany-rawdata.json @@ -0,0 +1,120 @@ +{ + "description": "insertMany-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "insertMany with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "insertMany with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/insertMany-rawdata.yml b/source/crud/tests/unified/insertMany-rawdata.yml new file mode 100644 index 0000000000..562be344d8 --- /dev/null +++ b/source/crud/tests/unified/insertMany-rawdata.yml @@ -0,0 +1,53 @@ +description: "insertMany-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "insertMany with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: &documents [ { _id: 2, x: 22 } ] + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: *documents + rawData: *rawdata + - description: "insertMany with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: &documents [ { _id: 2, x: 22 } ] + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: *documents + rawData: + $$exists: false diff --git a/source/crud/tests/unified/insertMany.json b/source/crud/tests/unified/insertMany.json new file mode 100644 index 0000000000..643b7f44de --- /dev/null +++ b/source/crud/tests/unified/insertMany.json @@ -0,0 +1,205 @@ +{ + "description": "insertMany", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany with non-existing documents", + "operations": [ + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany continue-on-error behavior with unordered (preexisting duplicate key)", + "operations": [ + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": false + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany continue-on-error behavior with unordered (duplicate key in requests)", + "operations": [ + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": false + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/insertMany.yml b/source/crud/tests/unified/insertMany.yml new file mode 100644 index 0000000000..ac4e868cc1 --- /dev/null +++ b/source/crud/tests/unified/insertMany.yml @@ -0,0 +1,118 @@ +description: insertMany + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany with non-existing documents' + operations: + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany continue-on-error behavior with unordered (preexisting duplicate key)' + operations: + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: false + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 2 + # Since the map of insertedIds is generated before execution it + # could indicate inserts that did not actually succeed. We omit this + # field rather than expect drivers to provide an accurate map + # filtered by write errors. + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany continue-on-error behavior with unordered (duplicate key in requests)' + operations: + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: false + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 2 + # Since the map of insertedIds is generated before execution it + # could indicate inserts that did not actually succeed. We omit this + # field rather than expect drivers to provide an accurate map + # filtered by write errors. + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/insertOne-comment.json b/source/crud/tests/unified/insertOne-comment.json new file mode 100644 index 0000000000..dbd83d9f64 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/insertOne-comment.yml b/source/crud/tests/unified/insertOne-comment.yml new file mode 100644 index 0000000000..f8cb60ce85 --- /dev/null +++ b/source/crud/tests/unified/insertOne-comment.yml @@ -0,0 +1,91 @@ +description: "insertOne-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "insertOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &document { _id: 2, x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + 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: *document + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: *comment + outcome: *outcome + + - description: "insertOne with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: insertOne + object: *collection0 + arguments: + document: *document + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/insertOne-dots_and_dollars.json b/source/crud/tests/unified/insertOne-dots_and_dollars.json new file mode 100644 index 0000000000..fdc17af2e8 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/insertOne-dots_and_dollars.yml b/source/crud/tests/unified/insertOne-dots_and_dollars.yml new file mode 100644 index 0000000000..fcfcfc71c6 --- /dev/null +++ b/source/crud/tests/unified/insertOne-dots_and_dollars.yml @@ -0,0 +1,238 @@ +description: "insertOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + collectionOptions: + writeConcern: { w: 0 } + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey { _id: 1, $a: 1 } + expectResult: &insertResult + # InsertOneResult is optional because all of its fields are optional + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: &expectEventsDollarPrefixedKey + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKey + + - 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: *dollarPrefixedKey + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKey + outcome: *initialData + + - description: "Inserting document with top-level dotted key" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &dottedKey { _id: 1, a.b: 1 } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - description: "Inserting document with dollar-prefixed key in embedded doc" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - description: "Inserting document with dotted key in embedded doc" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKeyInEmbedded + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded + + - description: "Inserting document with dollar-prefixed key in _id yields server-side error" + # Note: 5.0+ did not remove restrictions on dollar-prefixed keys in _id documents + operations: + - name: insertOne + object: *collection0 + arguments: + document: &dollarPrefixedKeyInId { _id: { $a: 1 } } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dollarPrefixedKeyInId + outcome: *initialData + + - description: "Inserting document with dotted key in _id on 3.6+ server" + runOnRequirements: + - minServerVersion: "3.6" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &dottedKeyInId { _id: { a.b: 1 } } + expectResult: + # InsertOneResult is optional because all of its fields are optional + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: { a.b: 1 } } } + expectEvents: &expectEventsDottedKeyInId + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dottedKeyInId + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInId + + - 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: *dottedKeyInId + expectError: + isClientError: false + expectEvents: *expectEventsDottedKeyInId + outcome: *initialData + + - description: "Inserting document with DBRef-like keys" + operations: + - name: insertOne + object: *collection0 + arguments: + # Note: an incomplete DBRef document may cause issues loading the test + # file with an Extended JSON parser, since the presence of one DBRef + # key may cause the parser to require others and/or enforce expected + # types (e.g. $ref and $db must be strings). + # + # Using "$db" here works for libmongoc so long as it's a string type; + # however, neither $ref nor $id would be accepted on their own. + # + # See https://github.com/mongodb/specifications/blob/master/source/extended-json.md#parsers + document: &dbrefLikeKey { _id: 1, a: { $db: "foo" } } + expectResult: *insertResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *dbrefLikeKey + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dbrefLikeKey + + - 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: *dollarPrefixedKeyInId + expectResult: + # InsertOneResult is optional because all of its fields are optional + $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection1Name + documents: + - *dollarPrefixedKeyInId + writeConcern: { w: 0 } + outcome: *initialData diff --git a/source/crud/tests/unified/insertOne-errorResponse.json b/source/crud/tests/unified/insertOne-errorResponse.json new file mode 100644 index 0000000000..04ea6a7451 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/insertOne-errorResponse.yml b/source/crud/tests/unified/insertOne-errorResponse.yml new file mode 100644 index 0000000000..b14caa1737 --- /dev/null +++ b/source/crud/tests/unified/insertOne-errorResponse.yml @@ -0,0 +1,46 @@ +description: "insertOne-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + # Some drivers may still need to skip this test because the CRUD spec does not + # prescribe how drivers should formulate a WriteException beyond collecting a + # write or write concern error. + - 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: &errorCode 8 # UnknownError + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1 } + expectError: + errorCode: *errorCode + errorResponse: + code: *errorCode diff --git a/source/crud/tests/unified/insertOne-rawdata.json b/source/crud/tests/unified/insertOne-rawdata.json new file mode 100644 index 0000000000..d010c2daac --- /dev/null +++ b/source/crud/tests/unified/insertOne-rawdata.json @@ -0,0 +1,116 @@ +{ + "description": "insertOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "insertOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "insertOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/insertOne-rawdata.yml b/source/crud/tests/unified/insertOne-rawdata.yml new file mode 100644 index 0000000000..7b14fd6e28 --- /dev/null +++ b/source/crud/tests/unified/insertOne-rawdata.yml @@ -0,0 +1,53 @@ +description: "insertOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "insertOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &document { _id: 2, x: 22 } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: [ *document ] + rawData: *rawdata + - description: "insertOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &document { _id: 2, x: 22 } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: [ *document ] + rawData: + $$exists: false diff --git a/source/crud/tests/unified/insertOne.json b/source/crud/tests/unified/insertOne.json new file mode 100644 index 0000000000..1a90913476 --- /dev/null +++ b/source/crud/tests/unified/insertOne.json @@ -0,0 +1,77 @@ +{ + "description": "insertOne", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "InsertOne with a non-existing document", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 2, + "x": 22 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/insertOne.yml b/source/crud/tests/unified/insertOne.yml new file mode 100644 index 0000000000..85ab7fe9e0 --- /dev/null +++ b/source/crud/tests/unified/insertOne.yml @@ -0,0 +1,44 @@ +description: insertOne + +schemaVersion: '1.0' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertOne with a non-existing document' + operations: + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 2, x: 22 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/crud/tests/unified/replaceOne-collation.json b/source/crud/tests/unified/replaceOne-collation.json new file mode 100644 index 0000000000..dd76b9d616 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-collation.json @@ -0,0 +1,92 @@ +{ + "description": "replaceOne-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne when one document matches with collation", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "x": "PING" + }, + "replacement": { + "_id": 2, + "x": "pong" + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "pong" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/replaceOne-collation.yml b/source/crud/tests/unified/replaceOne-collation.yml new file mode 100644 index 0000000000..68eddcff91 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-collation.yml @@ -0,0 +1,57 @@ +description: replaceOne-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + +tests: + - + description: 'ReplaceOne when one document matches with collation' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { x: PING } + replacement: { _id: 2, x: pong } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: pong } diff --git a/source/crud/tests/unified/replaceOne-comment.json b/source/crud/tests/unified/replaceOne-comment.json new file mode 100644 index 0000000000..88bee5d7b7 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-comment.yml b/source/crud/tests/unified/replaceOne-comment.yml new file mode 100644 index 0000000000..2c22b28757 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-comment.yml @@ -0,0 +1,105 @@ +description: "replaceOne-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "ReplaceOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: &filter { _id: 1 } + replacement: &replacement { x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "ReplaceOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: *comment + outcome: *outcome + + - description: "ReplaceOne with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/replaceOne-dots_and_dollars.json b/source/crud/tests/unified/replaceOne-dots_and_dollars.json new file mode 100644 index 0000000000..d5003dc5ea --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-dots_and_dollars.yml b/source/crud/tests/unified/replaceOne-dots_and_dollars.yml new file mode 100644 index 0000000000..b834e22205 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-dots_and_dollars.yml @@ -0,0 +1,180 @@ +description: "replaceOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + collectionOptions: + writeConcern: { w: 0 } + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey { _id: 1, a.b: 1 } + expectResult: &replaceResult + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: &expectEventsDottedKey + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKey + + - 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: *dottedKey + expectError: + isClientError: false + expectEvents: *expectEventsDottedKey + outcome: *initialData + + - 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: &dollarPrefixedKeyInEmbedded { _id: 1, a: { $b: 1 } } + expectResult: *replaceResult + expectEvents: &expectEventsDollarPrefixedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dollarPrefixedKeyInEmbedded + + - 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: *dollarPrefixedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDollarPrefixedKeyInEmbedded + outcome: *initialData + + - 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: &dottedKeyInEmbedded { _id: 1, a: { b.c: 1 } } + expectResult: *replaceResult + expectEvents: &expectEventsDottedKeyInEmbedded + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - *dottedKeyInEmbedded + + - 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: *dottedKeyInEmbedded + expectError: + isClientError: false + expectEvents: *expectEventsDottedKeyInEmbedded + outcome: *initialData + + - 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: *dollarPrefixedKeyInEmbedded + expectResult: + acknowledged: { $$unsetOrMatches: false } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection1Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: 0 } + outcome: *initialData diff --git a/source/crud/tests/unified/replaceOne-hint-unacknowledged.json b/source/crud/tests/unified/replaceOne-hint-unacknowledged.json new file mode 100644 index 0000000000..5c5dec64f6 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-hint-unacknowledged.yml b/source/crud/tests/unified/replaceOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..debb99b22f --- /dev/null +++ b/source/crud/tests/unified/replaceOne-hint-unacknowledged.yml @@ -0,0 +1,95 @@ +description: replaceOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + replacement: &replacement { x: 111 } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged replaceOne with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: replaceOne + arguments: + filter: *filter + replacement: *replacement + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *replacement + 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: *filter + replacement: *replacement + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/replaceOne-hint.json b/source/crud/tests/unified/replaceOne-hint.json new file mode 100644 index 0000000000..6926e9d8df --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-hint.yml b/source/crud/tests/unified/replaceOne-hint.yml new file mode 100644 index 0000000000..263e372170 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-hint.yml @@ -0,0 +1,105 @@ +description: replaceOne-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_replaceone_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'ReplaceOne with hint string' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: &filter + _id: + $gt: 1 + replacement: &replacement + x: 111 + hint: _id_ + expectResult: &result + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *replacement + hint: _id_ + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 111 + - + description: 'ReplaceOne with hint document' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: *filter + replacement: *replacement + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *replacement + hint: + _id: 1 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: *outcome diff --git a/source/crud/tests/unified/replaceOne-let.json b/source/crud/tests/unified/replaceOne-let.json new file mode 100644 index 0000000000..e7a7ee65a5 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-let.yml b/source/crud/tests/unified/replaceOne-let.yml new file mode 100644 index 0000000000..fda5bb6744 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-let.yml @@ -0,0 +1,98 @@ +description: "replaceOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "ReplaceOne with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + replacement: &replacement + x: "foo" + let: &let + id: 1 + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: *filter + replacement: *replacement + let: *let + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/replaceOne-rawdata.json b/source/crud/tests/unified/replaceOne-rawdata.json new file mode 100644 index 0000000000..632404491c --- /dev/null +++ b/source/crud/tests/unified/replaceOne-rawdata.json @@ -0,0 +1,132 @@ +{ + "description": "replaceOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "ReplaceOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "x": 22 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "ReplaceOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": {}, + "replacement": { + "x": 22 + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "x": 22 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/replaceOne-rawdata.yml b/source/crud/tests/unified/replaceOne-rawdata.yml new file mode 100644 index 0000000000..1b81c1f3f6 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-rawdata.yml @@ -0,0 +1,67 @@ +description: "replaceOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "ReplaceOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: replaceOne + arguments: + filter: {} + replacement: &replacement { x: 22 } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: *rawdata + - description: "ReplaceOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: replaceOne + arguments: + filter: {} + replacement: &replacement { x: 22 } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *replacement + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: + $$exists: false diff --git a/source/crud/tests/unified/replaceOne-sort.json b/source/crud/tests/unified/replaceOne-sort.json new file mode 100644 index 0000000000..cf2271dda5 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-sort.json @@ -0,0 +1,232 @@ +{ + "description": "replaceOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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": "ReplaceOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "replaceOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/replaceOne-sort.yml b/source/crud/tests/unified/replaceOne-sort.yml new file mode 100644 index 0000000000..f4b10fbaf9 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-sort.yml @@ -0,0 +1,94 @@ +description: replaceOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: ReplaceOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: replaceOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/replaceOne-validation.json b/source/crud/tests/unified/replaceOne-validation.json new file mode 100644 index 0000000000..6f5b173e02 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/replaceOne-validation.yml b/source/crud/tests/unified/replaceOne-validation.yml new file mode 100644 index 0000000000..db5a2a6666 --- /dev/null +++ b/source/crud/tests/unified/replaceOne-validation.yml @@ -0,0 +1,37 @@ +description: "replaceOne-validation" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: *initialData diff --git a/source/crud/tests/unified/replaceOne.json b/source/crud/tests/unified/replaceOne.json new file mode 100644 index 0000000000..bdb7556f2f --- /dev/null +++ b/source/crud/tests/unified/replaceOne.json @@ -0,0 +1,259 @@ +{ + "description": "replaceOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne when many documents match", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "replacement": { + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ] + }, + { + "description": "ReplaceOne when one document matches", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "ReplaceOne when no documents match", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 1 + } + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "ReplaceOne with upsert when no documents match without an id specified", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "x": 1 + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + }, + { + "description": "ReplaceOne with upsert when no documents match with an id specified", + "operations": [ + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 1 + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/replaceOne.yml b/source/crud/tests/unified/replaceOne.yml new file mode 100644 index 0000000000..ebe0af933e --- /dev/null +++ b/source/crud/tests/unified/replaceOne.yml @@ -0,0 +1,138 @@ +description: replaceOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '2.6' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + # This test doesn't verify the output collection because we cannot assume + # which document gets replaced. + description: 'ReplaceOne when many documents match' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: { $gt: 1 } } + replacement: { x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - + description: 'ReplaceOne when one document matches' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'ReplaceOne when no documents match' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 4 } + replacement: { _id: 4, x: 1 } + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'ReplaceOne with upsert when no documents match without an id specified' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 4 } + replacement: { x: 1 } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } + - + description: 'ReplaceOne with upsert when no documents match with an id specified' + operations: + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 4 } + replacement: { _id: 4, x: 1 } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } diff --git a/source/crud/tests/unified/updateMany-arrayFilters.json b/source/crud/tests/unified/updateMany-arrayFilters.json new file mode 100644 index 0000000000..8730caeb42 --- /dev/null +++ b/source/crud/tests/unified/updateMany-arrayFilters.json @@ -0,0 +1,233 @@ +{ + "description": "updateMany-arrayFilters", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.5.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany when no documents match arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 4 + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + }, + { + "description": "UpdateMany when one document matches arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 2 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + } + ] + } + ] + }, + { + "description": "UpdateMany when multiple documents match arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 2 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 2 + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany-arrayFilters.yml b/source/crud/tests/unified/updateMany-arrayFilters.yml new file mode 100644 index 0000000000..db5ec29c88 --- /dev/null +++ b/source/crud/tests/unified/updateMany-arrayFilters.yml @@ -0,0 +1,98 @@ +description: updateMany-arrayFilters + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: 3.5.6 + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + +tests: + - + description: 'UpdateMany when no documents match arrayFilters' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 4 } + expectResult: + matchedCount: 2 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - + description: 'UpdateMany when one document matches arrayFilters' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 3 } + expectResult: + matchedCount: 2 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 2 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - + description: 'UpdateMany when multiple documents match arrayFilters' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 1 } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 2 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 2 } ] } diff --git a/source/crud/tests/unified/updateMany-collation.json b/source/crud/tests/unified/updateMany-collation.json new file mode 100644 index 0000000000..0c780a3c2d --- /dev/null +++ b/source/crud/tests/unified/updateMany-collation.json @@ -0,0 +1,101 @@ +{ + "description": "updateMany-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + }, + { + "_id": 3, + "x": "pINg" + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany when many documents match with collation", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "x": "ping" + }, + "update": { + "$set": { + "x": "pong" + } + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "pong" + }, + { + "_id": 3, + "x": "pong" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany-collation.yml b/source/crud/tests/unified/updateMany-collation.yml new file mode 100644 index 0000000000..af5221c02e --- /dev/null +++ b/source/crud/tests/unified/updateMany-collation.yml @@ -0,0 +1,59 @@ +description: updateMany-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + - { _id: 3, x: pINg } + +tests: + - + description: 'UpdateMany when many documents match with collation' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { x: ping } + update: { $set: { x: pong } } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: pong } + - { _id: 3, x: pong } diff --git a/source/crud/tests/unified/updateMany-comment.json b/source/crud/tests/unified/updateMany-comment.json new file mode 100644 index 0000000000..88b8b67f5a --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-comment.yml b/source/crud/tests/unified/updateMany-comment.yml new file mode 100644 index 0000000000..4c6fbacd77 --- /dev/null +++ b/source/crud/tests/unified/updateMany-comment.yml @@ -0,0 +1,104 @@ +description: "updateMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "UpdateMany with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: &filter { _id: 1 } + update: &update { $set: {x: 22} } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "UpdateMany with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: *filter + update: *update + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + comment: *comment + outcome: *outcome + + - description: "UpdateMany with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/updateMany-dots_and_dollars.json b/source/crud/tests/unified/updateMany-dots_and_dollars.json new file mode 100644 index 0000000000..5d3b9d0453 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-dots_and_dollars.yml b/source/crud/tests/unified/updateMany-dots_and_dollars.yml new file mode 100644 index 0000000000..6c9da82a5a --- /dev/null +++ b/source/crud/tests/unified/updateMany-dots_and_dollars.yml @@ -0,0 +1,138 @@ +description: "updateMany-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey + - { $replaceWith: { $setField: { field: { $literal: $a }, value: 1, input: $$ROOT } } } + expectResult: &updateResult + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKey + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey + - { $replaceWith: { $setField: { field: { $literal: a.b }, value: 1, input: $$ROOT } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: $a }, value: 1, input: $foo } } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: a.b }, value: 1, input: $foo } } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: true + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, foo: { a.b: 1 } } diff --git a/source/crud/tests/unified/updateMany-hint-clientError.json b/source/crud/tests/unified/updateMany-hint-clientError.json new file mode 100644 index 0000000000..5da878e293 --- /dev/null +++ b/source/crud/tests/unified/updateMany-hint-clientError.json @@ -0,0 +1,159 @@ +{ + "description": "updateMany-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_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 (client-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": [] + } + ], + "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 (client-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": [] + } + ], + "outcome": [ + { + "collectionName": "test_updatemany_hint", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany-hint-clientError.yml b/source/crud/tests/unified/updateMany-hint-clientError.yml new file mode 100644 index 0000000000..163168ecfa --- /dev/null +++ b/source/crud/tests/unified/updateMany-hint-clientError.yml @@ -0,0 +1,88 @@ +description: updateMany-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updatemany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 +tests: + - + description: 'UpdateMany with hint string unsupported (client-side error)' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + description: 'UpdateMany with hint document unsupported (client-side error)' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/updateMany-hint-serverError.json b/source/crud/tests/unified/updateMany-hint-serverError.json new file mode 100644 index 0000000000..c81f36b13c --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-hint-serverError.yml b/source/crud/tests/unified/updateMany-hint-serverError.yml new file mode 100644 index 0000000000..61d7179fc0 --- /dev/null +++ b/source/crud/tests/unified/updateMany-hint-serverError.yml @@ -0,0 +1,112 @@ +description: updateMany-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.1.9 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updatemany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: true + hint: _id_ + upsert: { $$unsetOrMatches: false } + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: true + hint: + _id: 1 + upsert: { $$unsetOrMatches: false } + outcome: *outcome diff --git a/source/crud/tests/unified/updateMany-hint-unacknowledged.json b/source/crud/tests/unified/updateMany-hint-unacknowledged.json new file mode 100644 index 0000000000..e83838aac2 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-hint-unacknowledged.yml b/source/crud/tests/unified/updateMany-hint-unacknowledged.yml new file mode 100644 index 0000000000..9f15297385 --- /dev/null +++ b/source/crud/tests/unified/updateMany-hint-unacknowledged.yml @@ -0,0 +1,96 @@ +description: updateMany-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + update: &update { $inc: { x: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + update: *update + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged updateMany with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: updateMany + arguments: + filter: *filter + update: *update + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + 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: *filter + update: *update + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/updateMany-hint.json b/source/crud/tests/unified/updateMany-hint.json new file mode 100644 index 0000000000..929be52994 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-hint.yml b/source/crud/tests/unified/updateMany-hint.yml new file mode 100644 index 0000000000..e595030923 --- /dev/null +++ b/source/crud/tests/unified/updateMany-hint.yml @@ -0,0 +1,112 @@ +description: updateMany-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updatemany_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectResult: &result + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: true + hint: _id_ + upsert: { $$unsetOrMatches: false } + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + multi: true + hint: + _id: 1 + upsert: { $$unsetOrMatches: false } + outcome: *outcome diff --git a/source/crud/tests/unified/updateMany-let.json b/source/crud/tests/unified/updateMany-let.json new file mode 100644 index 0000000000..cff3bd4c79 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-let.yml b/source/crud/tests/unified/updateMany-let.yml new file mode 100644 index 0000000000..402000967b --- /dev/null +++ b/source/crud/tests/unified/updateMany-let.yml @@ -0,0 +1,107 @@ +description: "updateMany-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &filter + $expr: + $eq: [ "$name", "$$name" ] + update: &update + - $set: {x: "$$x", y: "$$y" } + let: &let0 + name: name + x: foo + y: { $literal: "bar" } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + upsert: { $$unsetOrMatches: false } + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + _id: 1 + update: &update1 + - $set: {x: "$$x"} + let: &let1 + x: foo + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter1 + u: *update1 + multi: true + upsert: { $$unsetOrMatches: false } + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2, name: "name" } + - { _id: 3, name: "name" } diff --git a/source/crud/tests/unified/updateMany-pipeline.json b/source/crud/tests/unified/updateMany-pipeline.json new file mode 100644 index 0000000000..e0f6d9d4a4 --- /dev/null +++ b/source/crud/tests/unified/updateMany-pipeline.json @@ -0,0 +1,142 @@ +{ + "description": "updateMany-pipeline", + "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": "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 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany-pipeline.yml b/source/crud/tests/unified/updateMany-pipeline.yml new file mode 100644 index 0000000000..a177f22efb --- /dev/null +++ b/source/crud/tests/unified/updateMany-pipeline.yml @@ -0,0 +1,64 @@ +description: updateMany-pipeline + +schemaVersion: '1.0' + +runOnRequirements: + - minServerVersion: 4.1.11 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, y: 1, t: { u: { v: 1 } } } + - { _id: 2, x: 2, y: 1 } + +tests: + - + 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: *collection_name + updates: + - q: { } + u: + - { $project: { x: 1 } } + - { $addFields: { foo: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + commandName: update + databaseName: *database_name + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1, foo: 1 } + - { _id: 2, x: 2, foo: 1 } diff --git a/source/crud/tests/unified/updateMany-rawdata.json b/source/crud/tests/unified/updateMany-rawdata.json new file mode 100644 index 0000000000..20417e0770 --- /dev/null +++ b/source/crud/tests/unified/updateMany-rawdata.json @@ -0,0 +1,136 @@ +{ + "description": "updateMany-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "updateMany with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "updateMany with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "x": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany-rawdata.yml b/source/crud/tests/unified/updateMany-rawdata.yml new file mode 100644 index 0000000000..75b2587fa3 --- /dev/null +++ b/source/crud/tests/unified/updateMany-rawdata.yml @@ -0,0 +1,65 @@ +description: "updateMany-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "updateMany with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: {} + update: &update { $set: {x: 1} } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *update + multi: true + upsert: + $$unsetOrMatches: false + rawData: *rawdata + - description: "updateMany with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: {} + update: &update { $set: {x: 1} } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *update + multi: true + upsert: + $$unsetOrMatches: false + rawData: + $$exists: false diff --git a/source/crud/tests/unified/updateMany-validation.json b/source/crud/tests/unified/updateMany-validation.json new file mode 100644 index 0000000000..e3e46a1384 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateMany-validation.yml b/source/crud/tests/unified/updateMany-validation.yml new file mode 100644 index 0000000000..4e86eff1f3 --- /dev/null +++ b/source/crud/tests/unified/updateMany-validation.yml @@ -0,0 +1,39 @@ +description: "updateMany-validation" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: *initialData diff --git a/source/crud/tests/unified/updateMany.json b/source/crud/tests/unified/updateMany.json new file mode 100644 index 0000000000..19b890592b --- /dev/null +++ b/source/crud/tests/unified/updateMany.json @@ -0,0 +1,236 @@ +{ + "description": "updateMany", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany when many documents match", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateMany when one document matches", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "UpdateMany when no documents match", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "UpdateMany with upsert when no documents match", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateMany.yml b/source/crud/tests/unified/updateMany.yml new file mode 100644 index 0000000000..98f29d45e8 --- /dev/null +++ b/source/crud/tests/unified/updateMany.yml @@ -0,0 +1,120 @@ +description: updateMany + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '2.6' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + description: 'UpdateMany when many documents match' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 23 } + - { _id: 3, x: 34 } + - + description: 'UpdateMany when one document matches' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'UpdateMany when no documents match' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'UpdateMany with upsert when no documents match' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } diff --git a/source/crud/tests/unified/updateOne-arrayFilters.json b/source/crud/tests/unified/updateOne-arrayFilters.json new file mode 100644 index 0000000000..be5d05b01e --- /dev/null +++ b/source/crud/tests/unified/updateOne-arrayFilters.json @@ -0,0 +1,453 @@ +{ + "description": "updateOne-arrayFilters", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.5.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 1 + } + ] + } + ] + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne when no document matches arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 4 + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "description": "UpdateOne when one document matches arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 3 + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 2 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "description": "UpdateOne when multiple documents match arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": {}, + "update": { + "$set": { + "y.$[i].b": 2 + } + }, + "arrayFilters": [ + { + "i.b": 1 + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 2 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "description": "UpdateOne when no documents match multiple arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "y.$[i].c.$[j].d": 0 + } + }, + "arrayFilters": [ + { + "i.b": 5 + }, + { + "j.d": 3 + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 1 + } + ] + } + ] + } + ] + } + ] + }, + { + "description": "UpdateOne when one document matches multiple arrayFilters", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3 + }, + "update": { + "$set": { + "y.$[i].c.$[j].d": 0 + } + }, + "arrayFilters": [ + { + "i.b": 5 + }, + { + "j.d": 1 + } + ] + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "y": [ + { + "b": 3 + }, + { + "b": 1 + } + ] + }, + { + "_id": 2, + "y": [ + { + "b": 0 + }, + { + "b": 1 + } + ] + }, + { + "_id": 3, + "y": [ + { + "b": 5, + "c": [ + { + "d": 2 + }, + { + "d": 0 + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne-arrayFilters.yml b/source/crud/tests/unified/updateOne-arrayFilters.yml new file mode 100644 index 0000000000..8d5b238bef --- /dev/null +++ b/source/crud/tests/unified/updateOne-arrayFilters.yml @@ -0,0 +1,150 @@ +description: updateOne-arrayFilters + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: 3.5.6 + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 1 } ] } ] } + +tests: + - + description: 'UpdateOne when no document matches arrayFilters' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 4 } + expectResult: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 1 } ] } ] } + - + description: 'UpdateOne when one document matches arrayFilters' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 3 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 2 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 1 } ] } ] } + - + description: 'UpdateOne when multiple documents match arrayFilters' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { } + update: { $set: { 'y.$[i].b': 2 } } + arrayFilters: + - { i.b: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 2 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 1 } ] } ] } + - + description: 'UpdateOne when no documents match multiple arrayFilters' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3 } + update: { $set: { 'y.$[i].c.$[j].d': 0 } } + arrayFilters: + - { i.b: 5 } + - { j.d: 3 } + expectResult: + matchedCount: 1 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 1 } ] } ] } + - + description: 'UpdateOne when one document matches multiple arrayFilters' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3 } + update: { $set: { 'y.$[i].c.$[j].d': 0 } } + arrayFilters: + - { i.b: 5 } + - { j.d: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: [ { b: 3 }, { b: 1 } ] } + - { _id: 2, y: [ { b: 0 }, { b: 1 } ] } + - { _id: 3, y: [ { b: 5, c: [ { d: 2 }, { d: 0 } ] } ] } diff --git a/source/crud/tests/unified/updateOne-collation.json b/source/crud/tests/unified/updateOne-collation.json new file mode 100644 index 0000000000..a39be46054 --- /dev/null +++ b/source/crud/tests/unified/updateOne-collation.json @@ -0,0 +1,93 @@ +{ + "description": "updateOne-collation", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "ping" + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne when one document matches with collation", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "x": "PING" + }, + "update": { + "$set": { + "x": "pong" + } + }, + "collation": { + "locale": "en_US", + "strength": 2 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": "pong" + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne-collation.yml b/source/crud/tests/unified/updateOne-collation.yml new file mode 100644 index 0000000000..85c2b55295 --- /dev/null +++ b/source/crud/tests/unified/updateOne-collation.yml @@ -0,0 +1,57 @@ +description: updateOne-collation + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '3.4' + serverless: forbid + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: ping } + +tests: + - + description: 'UpdateOne when one document matches with collation' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { x: PING } + update: { $set: { x: pong } } + # https://www.mongodb.com/docs/manual/reference/collation/#collation-document + collation: + locale: en_US + strength: 2 + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: pong } diff --git a/source/crud/tests/unified/updateOne-comment.json b/source/crud/tests/unified/updateOne-comment.json new file mode 100644 index 0000000000..f4ee74db38 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-comment.yml b/source/crud/tests/unified/updateOne-comment.yml new file mode 100644 index 0000000000..a176d620eb --- /dev/null +++ b/source/crud/tests/unified/updateOne-comment.yml @@ -0,0 +1,104 @@ +description: "updateOne-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "UpdateOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: &filter { _id: 1 } + update: &update { $set: {x: 22} } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "UpdateOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: *filter + update: *update + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: *comment + outcome: *outcome + + - description: "UpdateOne with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.4.0" + maxServerVersion: "4.2.99" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + comment: "comment" + outcome: *initialData diff --git a/source/crud/tests/unified/updateOne-dots_and_dollars.json b/source/crud/tests/unified/updateOne-dots_and_dollars.json new file mode 100644 index 0000000000..798d522cba --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-dots_and_dollars.yml b/source/crud/tests/unified/updateOne-dots_and_dollars.yml new file mode 100644 index 0000000000..9ff8a38d73 --- /dev/null +++ b/source/crud/tests/unified/updateOne-dots_and_dollars.yml @@ -0,0 +1,138 @@ +description: "updateOne-dots_and_dollars" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKey + - { $replaceWith: { $setField: { field: { $literal: $a }, value: 1, input: $$ROOT } } } + expectResult: &updateResult + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKey + - { $replaceWith: { $setField: { field: { $literal: a.b }, value: 1, input: $$ROOT } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKey + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dollarPrefixedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: $a }, value: 1, input: $foo } } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dollarPrefixedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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: &dottedKeyInEmbedded + - { $set: { foo: { $setField: { field: { $literal: a.b }, value: 1, input: $foo } } } } + expectResult: *updateResult + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: 1 } + u: *dottedKeyInEmbedded + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, foo: { a.b: 1 } } diff --git a/source/crud/tests/unified/updateOne-errorResponse.json b/source/crud/tests/unified/updateOne-errorResponse.json new file mode 100644 index 0000000000..0ceddbc4fc --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-errorResponse.yml b/source/crud/tests/unified/updateOne-errorResponse.yml new file mode 100644 index 0000000000..6d42195b0b --- /dev/null +++ b/source/crud/tests/unified/updateOne-errorResponse.yml @@ -0,0 +1,47 @@ +description: "updateOne-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +tests: + # Some drivers may still need to skip this test because the CRUD spec does not + # prescribe how drivers should formulate a WriteException beyond collecting a + # write or write concern error. + - 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: &errorCode 8 # UnknownError + - name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 } } + expectError: + errorCode: *errorCode + errorResponse: + code: *errorCode diff --git a/source/crud/tests/unified/updateOne-hint-clientError.json b/source/crud/tests/unified/updateOne-hint-clientError.json new file mode 100644 index 0000000000..d4f1a53430 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-hint-clientError.yml b/source/crud/tests/unified/updateOne-hint-clientError.yml new file mode 100644 index 0000000000..5107b5f4b5 --- /dev/null +++ b/source/crud/tests/unified/updateOne-hint-clientError.yml @@ -0,0 +1,82 @@ +description: updateOne-hint-clientError +schemaVersion: '1.0' +runOnRequirements: + - + maxServerVersion: 3.3.99 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updateone_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: [] + outcome: *outcome diff --git a/source/crud/tests/unified/updateOne-hint-serverError.json b/source/crud/tests/unified/updateOne-hint-serverError.json new file mode 100644 index 0000000000..05fb033319 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-hint-serverError.yml b/source/crud/tests/unified/updateOne-hint-serverError.yml new file mode 100644 index 0000000000..f9681ab825 --- /dev/null +++ b/source/crud/tests/unified/updateOne-hint-serverError.yml @@ -0,0 +1,106 @@ +description: updateOne-hint-serverError +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 3.4.0 + maxServerVersion: 4.1.9 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updateone_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + 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: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + hint: _id_ + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + 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: *filter + update: *update + hint: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + hint: + _id: 1 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: *outcome diff --git a/source/crud/tests/unified/updateOne-hint-unacknowledged.json b/source/crud/tests/unified/updateOne-hint-unacknowledged.json new file mode 100644 index 0000000000..859b0f92f9 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-hint-unacknowledged.yml b/source/crud/tests/unified/updateOne-hint-unacknowledged.yml new file mode 100644 index 0000000000..77802db001 --- /dev/null +++ b/source/crud/tests/unified/updateOne-hint-unacknowledged.yml @@ -0,0 +1,95 @@ +description: updateOne-hint-unacknowledged + +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name db0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: &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: &filter { _id: { $gt: 1 } } + update: &update { $inc: { x: 1 } } + hint: _id_ + expectError: + isClientError: true + expectEvents: &noEvents + - 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: *filter + update: *update + hint: { _id: 1 } + expectError: + isClientError: true + expectEvents: *noEvents + + - description: "Unacknowledged updateOne with hint string on 4.2+ server" + runOnRequirements: + - minServerVersion: "4.2.0" + operations: + - object: *collection0 + name: updateOne + arguments: + filter: *filter + update: *update + hint: _id_ + expectResult: &unacknowledgedResult { $$unsetOrMatches: { acknowledged: { $$unsetOrMatches: false } } } + expectEvents: &events + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: *filter + u: *update + 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: *filter + update: *update + hint: { _id: 1 } + expectResult: *unacknowledgedResult + expectEvents: *events diff --git a/source/crud/tests/unified/updateOne-hint.json b/source/crud/tests/unified/updateOne-hint.json new file mode 100644 index 0000000000..484e00757d --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-hint.yml b/source/crud/tests/unified/updateOne-hint.yml new file mode 100644 index 0000000000..bd60cd7a19 --- /dev/null +++ b/source/crud/tests/unified/updateOne-hint.yml @@ -0,0 +1,106 @@ +description: updateOne-hint +schemaVersion: '1.0' +runOnRequirements: + - + minServerVersion: 4.2.0 +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test_updateone_hint +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'UpdateOne with hint string' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: &filter + _id: + $gt: 1 + update: &update + $inc: + x: 1 + hint: _id_ + expectResult: &result + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + hint: _id_ + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 23 + - + description: 'UpdateOne with hint document' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: *filter + update: *update + hint: + _id: 1 + expectResult: *result + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: *filter + u: *update + hint: + _id: 1 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: *outcome diff --git a/source/crud/tests/unified/updateOne-let.json b/source/crud/tests/unified/updateOne-let.json new file mode 100644 index 0000000000..e43b979358 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-let.yml b/source/crud/tests/unified/updateOne-let.yml new file mode 100644 index 0000000000..0044fdac66 --- /dev/null +++ b/source/crud/tests/unified/updateOne-let.yml @@ -0,0 +1,102 @@ +description: "updateOne-let" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "UpdateOne with let option" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: &filter + $expr: + $eq: [ "$_id", "$$id" ] + update: &update + - $set: {x: "$$x" } + let: &let0 + id: 1 + x: "foo" + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let0 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + 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: &filter1 + _id: 1 + update: &update1 + - $set: {x: "$$x"} + let: &let1 + x: foo + expectError: + errorContains: "'update.let' is an unknown field" + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter1 + u: *update1 + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + let: *let1 + outcome: + - + collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/crud/tests/unified/updateOne-pipeline.json b/source/crud/tests/unified/updateOne-pipeline.json new file mode 100644 index 0000000000..1348c6b53b --- /dev/null +++ b/source/crud/tests/unified/updateOne-pipeline.json @@ -0,0 +1,150 @@ +{ + "description": "updateOne-pipeline", + "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 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne-pipeline.yml b/source/crud/tests/unified/updateOne-pipeline.yml new file mode 100644 index 0000000000..726d93c92c --- /dev/null +++ b/source/crud/tests/unified/updateOne-pipeline.yml @@ -0,0 +1,64 @@ +description: updateOne-pipeline + +schemaVersion: '1.0' + +runOnRequirements: + - minServerVersion: 4.1.11 + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-tests + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name test + +initialData: + - collectionName: *collection_name + databaseName: *database_name + 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: *collection_name + updates: + - q: { _id: 1 } + u: + - { $replaceRoot: { newRoot: $t } } + - { $addFields: { foo: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + commandName: update + databaseName: *database_name + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, u: { v: 1 }, foo: 1 } + - { _id: 2, x: 2, y: 1 } diff --git a/source/crud/tests/unified/updateOne-rawdata.json b/source/crud/tests/unified/updateOne-rawdata.json new file mode 100644 index 0000000000..8f703d9a40 --- /dev/null +++ b/source/crud/tests/unified/updateOne-rawdata.json @@ -0,0 +1,140 @@ +{ + "description": "updateOne-rawData", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "UpdateOne with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "UpdateOne with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + }, + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": {}, + "u": { + "$set": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne-rawdata.yml b/source/crud/tests/unified/updateOne-rawdata.yml new file mode 100644 index 0000000000..a22110d376 --- /dev/null +++ b/source/crud/tests/unified/updateOne-rawdata.yml @@ -0,0 +1,67 @@ +description: "updateOne-rawData" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "UpdateOne with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: {} + update: &update { $set: { x: 1 } } + rawData: &rawdata true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *update + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: *rawdata + - description: "UpdateOne with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: {} + update: &update { $set: { x: 1 } } + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: {} + u: *update + multi: + $$unsetOrMatches: false + upsert: + $$unsetOrMatches: false + rawData: + $$exists: false diff --git a/source/crud/tests/unified/updateOne-sort.json b/source/crud/tests/unified/updateOne-sort.json new file mode 100644 index 0000000000..8fe4f50b94 --- /dev/null +++ b/source/crud/tests/unified/updateOne-sort.json @@ -0,0 +1,240 @@ +{ + "description": "updateOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "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": "UpdateOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "updateOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne-sort.yml b/source/crud/tests/unified/updateOne-sort.yml new file mode 100644 index 0000000000..a14e1df1d2 --- /dev/null +++ b/source/crud/tests/unified/updateOne-sort.yml @@ -0,0 +1,96 @@ +description: updateOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: UpdateOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + + - description: updateOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: { $inc: { x: 1 } } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/crud/tests/unified/updateOne-validation.json b/source/crud/tests/unified/updateOne-validation.json new file mode 100644 index 0000000000..1464642c59 --- /dev/null +++ b/source/crud/tests/unified/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/source/crud/tests/unified/updateOne-validation.yml b/source/crud/tests/unified/updateOne-validation.yml new file mode 100644 index 0000000000..b6f49a6568 --- /dev/null +++ b/source/crud/tests/unified/updateOne-validation.yml @@ -0,0 +1,37 @@ +description: "updateOne-validation" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + 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: *initialData diff --git a/source/crud/tests/unified/updateOne.json b/source/crud/tests/unified/updateOne.json new file mode 100644 index 0000000000..a3f559673e --- /dev/null +++ b/source/crud/tests/unified/updateOne.json @@ -0,0 +1,216 @@ +{ + "description": "updateOne", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne when many documents match", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ] + }, + { + "description": "UpdateOne when one document matches", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "UpdateOne when no documents match", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert when no documents match", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "crud-v1", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/crud/tests/unified/updateOne.yml b/source/crud/tests/unified/updateOne.yml new file mode 100644 index 0000000000..2f8f6a9181 --- /dev/null +++ b/source/crud/tests/unified/updateOne.yml @@ -0,0 +1,114 @@ +description: updateOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '2.6' + +createEntities: + - + client: + id: &client0 client0 + - + database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v1 + - + collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - + # This test doesn't verify the output collection because we cannot assume + # which document gets updated. + description: 'UpdateOne when many documents match' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - + description: 'UpdateOne when one document matches' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'UpdateOne when no documents match' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'UpdateOne with upsert when no documents match' + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 4 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 1 } diff --git a/source/crud/tests/write/bulkWrite-arrayFilters.json b/source/crud/tests/write/bulkWrite-arrayFilters.json deleted file mode 100644 index 99e73f5d75..0000000000 --- a/source/crud/tests/write/bulkWrite-arrayFilters.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "BulkWrite with arrayFilters", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 3, - "modifiedCount": 3, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/bulkWrite-arrayFilters.yml b/source/crud/tests/write/bulkWrite-arrayFilters.yml deleted file mode 100644 index 1089f08c09..0000000000 --- a/source/crud/tests/write/bulkWrite-arrayFilters.yml +++ /dev/null @@ -1,45 +0,0 @@ -data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - -minServerVersion: '3.5.6' - -tests: - - - description: "BulkWrite with arrayFilters" - operation: - name: "bulkWrite" - arguments: - requests: - - - # UpdateOne when one document matches arrayFilters - name: "updateOne" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 3} - - - # UpdateMany when multiple documents match arrayFilters - name: "updateMany" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 1} - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 0 - insertedIds: {} - matchedCount: 3 - modifiedCount: 3 - upsertedCount: 0 - upsertedIds: {} - collection: - data: - - {_id: 1, y: [{b: 2}, {b: 2}]} - - {_id: 2, y: [{b: 0}, {b: 2}]} diff --git a/source/crud/tests/write/deleteMany-collation.json b/source/crud/tests/write/deleteMany-collation.json deleted file mode 100644 index d17bf3bcb9..0000000000 --- a/source/crud/tests/write/deleteMany-collation.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "DeleteMany when many documents match with collation", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/deleteMany-collation.yml b/source/crud/tests/write/deleteMany-collation.yml deleted file mode 100644 index daca5dd4c2..0000000000 --- a/source/crud/tests/write/deleteMany-collation.yml +++ /dev/null @@ -1,22 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} - - {_id: 3, x: 'pINg'} -minServerVersion: '3.4' - -tests: - - - description: "DeleteMany when many documents match with collation" - operation: - name: "deleteMany" - arguments: - filter: - x: 'PING' - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - deletedCount: 2 - collection: - data: - - {_id: 1, x: 11} diff --git a/source/crud/tests/write/deleteMany.json b/source/crud/tests/write/deleteMany.json deleted file mode 100644 index 7eee85e77f..0000000000 --- a/source/crud/tests/write/deleteMany.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "DeleteMany when many documents match", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "DeleteMany when no document matches", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - "outcome": { - "result": { - "deletedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/deleteMany.yml b/source/crud/tests/write/deleteMany.yml deleted file mode 100644 index e776c82f9a..0000000000 --- a/source/crud/tests/write/deleteMany.yml +++ /dev/null @@ -1,35 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "DeleteMany when many documents match" - operation: - name: "deleteMany" - arguments: - filter: - _id: {$gt: 1} - - outcome: - result: - deletedCount: 2 - collection: - data: - - {_id: 1, x: 11} - - - description: "DeleteMany when no document matches" - operation: - name: "deleteMany" - arguments: - filter: {_id: 4} - - outcome: - result: - deletedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} diff --git a/source/crud/tests/write/deleteOne-collation.json b/source/crud/tests/write/deleteOne-collation.json deleted file mode 100644 index 2f7f921130..0000000000 --- a/source/crud/tests/write/deleteOne-collation.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "DeleteOne when many documents matches with collation", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/deleteOne-collation.yml b/source/crud/tests/write/deleteOne-collation.yml deleted file mode 100644 index 321a148856..0000000000 --- a/source/crud/tests/write/deleteOne-collation.yml +++ /dev/null @@ -1,22 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} - - {_id: 3, x: 'pINg'} -minServerVersion: '3.4' - -tests: - - - description: "DeleteOne when many documents matches with collation" - operation: - name: "deleteOne" - arguments: - filter: {x: 'PING'} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - deletedCount: 1 - collection: - data: - - {_id: 1, x: 11} - - {_id: 3, x: 'pINg'} diff --git a/source/crud/tests/write/deleteOne.json b/source/crud/tests/write/deleteOne.json deleted file mode 100644 index a1106deae3..0000000000 --- a/source/crud/tests/write/deleteOne.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "DeleteOne when many documents match", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - } - } - }, - { - "description": "DeleteOne when one document matches", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "DeleteOne when no documents match", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - "outcome": { - "result": { - "deletedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/deleteOne.yml b/source/crud/tests/write/deleteOne.yml deleted file mode 100644 index 2d8a90779f..0000000000 --- a/source/crud/tests/write/deleteOne.yml +++ /dev/null @@ -1,48 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "DeleteOne when many documents match" - operation: - name: "deleteOne" - arguments: - filter: - _id: {$gt: 1} - - outcome: - result: - deletedCount: 1 - # can't verify collection because we don't have a way - # of knowing which document gets deleted. - - - description: "DeleteOne when one document matches" - operation: - name: "deleteOne" - arguments: - filter: {_id: 2} - - outcome: - result: - deletedCount: 1 - collection: - data: - - {_id: 1, x: 11} - - {_id: 3, x: 33} - - - description: "DeleteOne when no documents match" - operation: - name: "deleteOne" - arguments: - filter: {_id: 4} - - outcome: - result: - deletedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} diff --git a/source/crud/tests/write/findOneAndDelete-collation.json b/source/crud/tests/write/findOneAndDelete-collation.json deleted file mode 100644 index 1ff37d2e88..0000000000 --- a/source/crud/tests/write/findOneAndDelete-collation.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "FindOneAndDelete when one document matches with collation", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 2, - "x": "PING" - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "ping" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndDelete-collation.yml b/source/crud/tests/write/findOneAndDelete-collation.yml deleted file mode 100644 index d33741f573..0000000000 --- a/source/crud/tests/write/findOneAndDelete-collation.yml +++ /dev/null @@ -1,23 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} - - {_id: 3, x: 'pINg'} -minServerVersion: '3.4' - -tests: - - - description: "FindOneAndDelete when one document matches with collation" - operation: - name: findOneAndDelete - arguments: - filter: {_id: 2, x: 'PING'} - projection: {x: 1, _id: 0} - sort: {x: 1} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: {x: 'ping'} - collection: - data: - - {_id: 1, x: 11} - - {_id: 3, x: 'pINg'} \ No newline at end of file diff --git a/source/crud/tests/write/findOneAndDelete.json b/source/crud/tests/write/findOneAndDelete.json deleted file mode 100644 index e424e2aad1..0000000000 --- a/source/crud/tests/write/findOneAndDelete.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndDelete when many documents match", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndDelete when one document matches", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 2 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndDelete when no documents match", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 4 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndDelete.yml b/source/crud/tests/write/findOneAndDelete.yml deleted file mode 100644 index a485ee46a5..0000000000 --- a/source/crud/tests/write/findOneAndDelete.yml +++ /dev/null @@ -1,53 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "FindOneAndDelete when many documents match" - operation: - name: findOneAndDelete - arguments: - filter: - _id: {$gt: 1} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 3, x: 33} - - - description: "FindOneAndDelete when one document matches" - operation: - name: findOneAndDelete - arguments: - filter: {_id: 2} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 3, x: 33} - - - description: "FindOneAndDelete when no documents match" - operation: - name: findOneAndDelete - arguments: - filter: {_id: 4} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} \ No newline at end of file diff --git a/source/crud/tests/write/findOneAndReplace-collation.json b/source/crud/tests/write/findOneAndReplace-collation.json deleted file mode 100644 index babb2f7c11..0000000000 --- a/source/crud/tests/write/findOneAndReplace-collation.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "FindOneAndReplace when one document matches with collation returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "x": "PING" - }, - "replacement": { - "x": "pong" - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "pong" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndReplace-collation.yml b/source/crud/tests/write/findOneAndReplace-collation.yml deleted file mode 100644 index 040661d752..0000000000 --- a/source/crud/tests/write/findOneAndReplace-collation.yml +++ /dev/null @@ -1,24 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "FindOneAndReplace when one document matches with collation returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: {x: 'PING'} - replacement: {x: 'pong'} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: {x: 'pong'} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 'pong'} diff --git a/source/crud/tests/write/findOneAndReplace-upsert.json b/source/crud/tests/write/findOneAndReplace-upsert.json deleted file mode 100644 index 0f07bf9c18..0000000000 --- a/source/crud/tests/write/findOneAndReplace-upsert.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 44 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 44 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndReplace-upsert.yml b/source/crud/tests/write/findOneAndReplace-upsert.yml deleted file mode 100644 index 2bd64c94ec..0000000000 --- a/source/crud/tests/write/findOneAndReplace-upsert.yml +++ /dev/null @@ -1,91 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} -minServerVersion: '2.6' - -tests: - - - description: "FindOneAndReplace when no documents match without id specified with upsert returning the document before modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {x: 44} - projection: {x: 1, _id: 0} - # Omit the sort option as it has no effect when no documents - # match and would only cause an inconsistent return value on - # pre-3.0 servers when combined with returnDocument "before" - # (see: SERVER-17650). - upsert: true - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} - - - description: "FindOneAndReplace when no documents match without id specified with upsert returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {x: 44} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - upsert: true - - outcome: - result: {x: 44} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} - - - description: "FindOneAndReplace when no documents match with id specified with upsert returning the document before modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {_id: 4, x: 44} - projection: {x: 1, _id: 0} - # Omit the sort option as it has no effect when no documents - # match and would only cause an inconsistent return value on - # pre-3.0 servers when combined with returnDocument "before" - # (see: SERVER-17650). - upsert: true - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} - - - description: "FindOneAndReplace when no documents match with id specified with upsert returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {_id: 4, x: 44} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - upsert: true - - outcome: - result: {x: 44} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 44} diff --git a/source/crud/tests/write/findOneAndReplace.json b/source/crud/tests/write/findOneAndReplace.json deleted file mode 100644 index 70e5c3df4e..0000000000 --- a/source/crud/tests/write/findOneAndReplace.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndReplace when many documents match returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when many documents match returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 32 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when one document matches returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when one document matches returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 32 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndReplace.yml b/source/crud/tests/write/findOneAndReplace.yml deleted file mode 100644 index c121418b78..0000000000 --- a/source/crud/tests/write/findOneAndReplace.yml +++ /dev/null @@ -1,113 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "FindOneAndReplace when many documents match returning the document before modification" - operation: - name: findOneAndReplace - arguments: - filter: - _id: {$gt: 1} - replacement: {x: 32} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 32} - - {_id: 3, x: 33} - - - description: "FindOneAndReplace when many documents match returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: - _id: {$gt: 1} - replacement: {x: 32} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: {x: 32} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 32} - - {_id: 3, x: 33} - - - description: "FindOneAndReplace when one document matches returning the document before modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 2} - replacement: {x: 32} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 32} - - {_id: 3, x: 33} - - - description: "FindOneAndReplace when one document matches returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 2} - replacement: {x: 32} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: {x: 32} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 32} - - {_id: 3, x: 33} - - - description: "FindOneAndReplace when no documents match returning the document before modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {x: 44} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "FindOneAndReplace when no documents match returning the document after modification" - operation: - name: findOneAndReplace - arguments: - filter: {_id: 4} - replacement: {x: 44} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} diff --git a/source/crud/tests/write/findOneAndUpdate-arrayFilters.json b/source/crud/tests/write/findOneAndUpdate-arrayFilters.json deleted file mode 100644 index 1aa13b863e..0000000000 --- a/source/crud/tests/write/findOneAndUpdate-arrayFilters.json +++ /dev/null @@ -1,203 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "FindOneAndUpdate when no document matches arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when multiple documents match arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndUpdate-arrayFilters.yml b/source/crud/tests/write/findOneAndUpdate-arrayFilters.yml deleted file mode 100644 index 7665ad0c59..0000000000 --- a/source/crud/tests/write/findOneAndUpdate-arrayFilters.yml +++ /dev/null @@ -1,69 +0,0 @@ -data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} -minServerVersion: '3.5.6' - -tests: - - - description: "FindOneAndUpdate when no document matches arrayFilters" - operation: - name: findOneAndUpdate - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 4} - - outcome: - result: - _id: 1 - y: - - {b: 3} - - {b: 1} - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - - description: "FindOneAndUpdate when one document matches arrayFilters" - operation: - name: findOneAndUpdate - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 3} - - outcome: - result: - _id: 1 - y: - - {b: 3} - - {b: 1} - collection: - data: - - {_id: 1, y: [{b: 2}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - - description: "FindOneAndUpdate when multiple documents match arrayFilters" - operation: - name: findOneAndUpdate - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 1} - - outcome: - result: - _id: 1 - y: - - {b: 3} - - {b: 1} - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 2}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} diff --git a/source/crud/tests/write/findOneAndUpdate-collation.json b/source/crud/tests/write/findOneAndUpdate-collation.json deleted file mode 100644 index 04c1fe73ec..0000000000 --- a/source/crud/tests/write/findOneAndUpdate-collation.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "FindOneAndUpdate when many documents match with collation returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "x": "PING" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "_id": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "ping" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndUpdate-collation.yml b/source/crud/tests/write/findOneAndUpdate-collation.yml deleted file mode 100644 index 201f2751c3..0000000000 --- a/source/crud/tests/write/findOneAndUpdate-collation.yml +++ /dev/null @@ -1,27 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} - - {_id: 3, x: 'pINg'} -minServerVersion: '3.4' - -tests: - - - description: "FindOneAndUpdate when many documents match with collation returning the document before modification" - operation: - name: findOneAndUpdate - arguments: - filter: - x: 'PING' - update: - $set: {x: 'pong'} - projection: {x: 1, _id: 0} - sort: {_id: 1} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: {x: 'ping'} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 'pong'} - - {_id: 3, x: 'pINg'} \ No newline at end of file diff --git a/source/crud/tests/write/findOneAndUpdate.json b/source/crud/tests/write/findOneAndUpdate.json deleted file mode 100644 index 6da8325273..0000000000 --- a/source/crud/tests/write/findOneAndUpdate.json +++ /dev/null @@ -1,379 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate when many documents match returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when many documents match returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 23 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 23 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match with upsert returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match with upsert returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/findOneAndUpdate.yml b/source/crud/tests/write/findOneAndUpdate.yml deleted file mode 100644 index bed02f146a..0000000000 --- a/source/crud/tests/write/findOneAndUpdate.yml +++ /dev/null @@ -1,163 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - -tests: - - - description: "FindOneAndUpdate when many documents match returning the document before modification" - operation: - name: findOneAndUpdate - arguments: - filter: - _id: {$gt: 1} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 23} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when many documents match returning the document after modification" - operation: - name: findOneAndUpdate - arguments: - filter: - _id: {$gt: 1} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: {x: 23} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 23} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when one document matches returning the document before modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 2} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: {x: 22} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 23} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when one document matches returning the document after modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 2} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: {x: 23} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 23} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when no documents match returning the document before modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - sort: {x: 1} - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when no documents match with upsert returning the document before modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - # Omit the sort option as it has no effect when no documents - # match and would only cause an inconsistent return value on - # pre-3.0 servers when combined with returnDocument "before" - # (see: SERVER-17650). - upsert: true - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} - - - description: "FindOneAndUpdate when no documents match returning the document after modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - - outcome: - result: null - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "FindOneAndUpdate when no documents match with upsert returning the document after modification" - operation: - name: findOneAndUpdate - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - projection: {x: 1, _id: 0} - returnDocument: After - sort: {x: 1} - upsert: true - - outcome: - result: {x: 1} - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} \ No newline at end of file diff --git a/source/crud/tests/write/insertMany.json b/source/crud/tests/write/insertMany.json deleted file mode 100644 index a2356c42df..0000000000 --- a/source/crud/tests/write/insertMany.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany with non-existing documents", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/insertMany.yml b/source/crud/tests/write/insertMany.yml deleted file mode 100644 index 410220aa00..0000000000 --- a/source/crud/tests/write/insertMany.yml +++ /dev/null @@ -1,21 +0,0 @@ -data: - - {_id: 1, x: 11} - -tests: - - - description: "InsertMany with non-existing documents" - operation: - name: "insertMany" - arguments: - documents: - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} \ No newline at end of file diff --git a/source/crud/tests/write/insertOne.json b/source/crud/tests/write/insertOne.json deleted file mode 100644 index 525de75e07..0000000000 --- a/source/crud/tests/write/insertOne.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertOne with a non-existing document", - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - "outcome": { - "result": { - "insertedId": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/insertOne.yml b/source/crud/tests/write/insertOne.yml deleted file mode 100644 index 8d65499d9b..0000000000 --- a/source/crud/tests/write/insertOne.yml +++ /dev/null @@ -1,18 +0,0 @@ -data: - - {_id: 1, x: 11} - -tests: - - - description: "InsertOne with a non-existing document" - operation: - name: "insertOne" - arguments: - document: {_id: 2, x: 22} - - outcome: - result: - insertedId: 2 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} \ No newline at end of file diff --git a/source/crud/tests/write/replaceOne-collation.json b/source/crud/tests/write/replaceOne-collation.json deleted file mode 100644 index a668fe7383..0000000000 --- a/source/crud/tests/write/replaceOne-collation.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "ReplaceOne when one document matches with collation", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "x": "PING" - }, - "replacement": { - "_id": 2, - "x": "pong" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/replaceOne-collation.yml b/source/crud/tests/write/replaceOne-collation.yml deleted file mode 100644 index 6861092bbb..0000000000 --- a/source/crud/tests/write/replaceOne-collation.yml +++ /dev/null @@ -1,24 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "ReplaceOne when one document matches with collation" - operation: - name: "replaceOne" - arguments: - filter: {x: 'PING'} - replacement: {_id: 2, x: 'pong'} - collation: {locale: 'en_US', strength: 2} # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 'pong'} \ No newline at end of file diff --git a/source/crud/tests/write/replaceOne.json b/source/crud/tests/write/replaceOne.json deleted file mode 100644 index 101af25c7c..0000000000 --- a/source/crud/tests/write/replaceOne.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "ReplaceOne when many documents match", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - }, - { - "description": "ReplaceOne when one document matches", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "ReplaceOne when no documents match", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 1 - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "ReplaceOne with upsert when no documents match without an id specified", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "ReplaceOne with upsert when no documents match with an id specified", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/replaceOne.yml b/source/crud/tests/write/replaceOne.yml deleted file mode 100644 index 4bccd36008..0000000000 --- a/source/crud/tests/write/replaceOne.yml +++ /dev/null @@ -1,102 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} -minServerVersion: '2.6' - -tests: - - - description: "ReplaceOne when many documents match" - operation: - name: "replaceOne" - arguments: - filter: - _id: {$gt: 1} - replacement: {x: 111} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - # Can't verify collection data because we don't have a way of - # knowing which document gets updated. - - - description: "ReplaceOne when one document matches" - operation: - name: "replaceOne" - arguments: - filter: {_id: 1} - replacement: {_id: 1, x: 111} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 111} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "ReplaceOne when no documents match" - operation: - name: "replaceOne" - arguments: - filter: {_id: 4} - replacement: {_id: 4, x: 1} - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "ReplaceOne with upsert when no documents match without an id specified" - operation: - name: "replaceOne" - arguments: - filter: {_id: 4} - replacement: {x: 1} - upsert: true - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} - - - - description: "ReplaceOne with upsert when no documents match with an id specified" - operation: - name: "replaceOne" - arguments: - filter: {_id: 4} - replacement: {_id: 4, x: 1} - upsert: true - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} diff --git a/source/crud/tests/write/updateMany-arrayFilters.json b/source/crud/tests/write/updateMany-arrayFilters.json deleted file mode 100644 index ae4c123ea5..0000000000 --- a/source/crud/tests/write/updateMany-arrayFilters.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "UpdateMany when no documents match arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "UpdateMany when one document matches arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "UpdateMany when multiple documents match arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateMany-arrayFilters.yml b/source/crud/tests/write/updateMany-arrayFilters.yml deleted file mode 100644 index 3cfee56924..0000000000 --- a/source/crud/tests/write/updateMany-arrayFilters.yml +++ /dev/null @@ -1,66 +0,0 @@ -data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} -minServerVersion: '3.5.6' - -tests: - - - description: "UpdateMany when no documents match arrayFilters" - operation: - name: "updateMany" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 4} - - outcome: - result: - matchedCount: 2 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - - description: "UpdateMany when one document matches arrayFilters" - operation: - name: "updateMany" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 3} - - outcome: - result: - matchedCount: 2 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 2}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - - description: "UpdateMany when multiple documents match arrayFilters" - operation: - name: "updateMany" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 1} - - outcome: - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 2}]} - - {_id: 2, y: [{b: 0}, {b: 2}]} diff --git a/source/crud/tests/write/updateMany-collation.json b/source/crud/tests/write/updateMany-collation.json deleted file mode 100644 index 3cb49f2298..0000000000 --- a/source/crud/tests/write/updateMany-collation.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "UpdateMany when many documents match with collation", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "x": "ping" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - }, - { - "_id": 3, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateMany-collation.yml b/source/crud/tests/write/updateMany-collation.yml deleted file mode 100644 index 2d48a9dcfe..0000000000 --- a/source/crud/tests/write/updateMany-collation.yml +++ /dev/null @@ -1,28 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} - - {_id: 3, x: 'pINg'} -minServerVersion: '3.4' - -tests: - - - description: "UpdateMany when many documents match with collation" - operation: - name: "updateMany" - arguments: - filter: - x: 'ping' - update: - $set: {x: 'pong'} - collation: { locale: 'en_US', strength: 2 } # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 'pong'} - - {_id: 3, x: 'pong'} diff --git a/source/crud/tests/write/updateMany.json b/source/crud/tests/write/updateMany.json deleted file mode 100644 index a3c339987d..0000000000 --- a/source/crud/tests/write/updateMany.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "UpdateMany when many documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateMany when one document matches", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateMany when no documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateMany with upsert when no documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateMany.yml b/source/crud/tests/write/updateMany.yml deleted file mode 100644 index a0eb62ba83..0000000000 --- a/source/crud/tests/write/updateMany.yml +++ /dev/null @@ -1,87 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} -minServerVersion: '2.6' - -tests: - - - description: "UpdateMany when many documents match" - operation: - name: "updateMany" - arguments: - filter: - _id: {$gt: 1} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 23} - - {_id: 3, x: 34} - - - description: "UpdateMany when one document matches" - operation: - name: "updateMany" - arguments: - filter: {_id: 1} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 12} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "UpdateMany when no documents match" - operation: - name: "updateMany" - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "UpdateMany with upsert when no documents match" - operation: - name: "updateMany" - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - upsert: true - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} diff --git a/source/crud/tests/write/updateOne-arrayFilters.json b/source/crud/tests/write/updateOne-arrayFilters.json deleted file mode 100644 index 087ed4b82f..0000000000 --- a/source/crud/tests/write/updateOne-arrayFilters.json +++ /dev/null @@ -1,395 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "UpdateOne when no document matches arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when one document matches arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when multiple documents match arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when no documents match multiple arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "y.$[i].c.$[j].d": 0 - } - }, - "arrayFilters": [ - { - "i.b": 5 - }, - { - "j.d": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when one document matches multiple arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "y.$[i].c.$[j].d": 0 - } - }, - "arrayFilters": [ - { - "i.b": 5 - }, - { - "j.d": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 0 - } - ] - } - ] - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateOne-arrayFilters.yml b/source/crud/tests/write/updateOne-arrayFilters.yml deleted file mode 100644 index a35e15c687..0000000000 --- a/source/crud/tests/write/updateOne-arrayFilters.yml +++ /dev/null @@ -1,114 +0,0 @@ -data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 1}] }]} -minServerVersion: '3.5.6' - -tests: - - - description: "UpdateOne when no document matches arrayFilters" - operation: - name: "updateOne" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 4} - - outcome: - result: - matchedCount: 1 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 1}] }]} - - - description: "UpdateOne when one document matches arrayFilters" - operation: - name: "updateOne" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 3} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 2}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 1}] }]} - - - description: "UpdateOne when multiple documents match arrayFilters" - operation: - name: "updateOne" - arguments: - filter: {} - update: - $set: {"y.$[i].b": 2} - arrayFilters: - - {i.b: 1} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 2}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 1}] }]} - - - description: "UpdateOne when no documents match multiple arrayFilters" - operation: - name: "updateOne" - arguments: - filter: {_id: 3} - update: - $set: {"y.$[i].c.$[j].d": 0} - arrayFilters: - - {i.b: 5} - - {j.d: 3} - - outcome: - result: - matchedCount: 1 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 1}] }]} - - - description: "UpdateOne when one document matches multiple arrayFilters" - operation: - name: "updateOne" - arguments: - filter: {_id: 3} - update: - $set: {"y.$[i].c.$[j].d": 0} - arrayFilters: - - {i.b: 5} - - {j.d: 1} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, y: [{b: 3}, {b: 1}]} - - {_id: 2, y: [{b: 0}, {b: 1}]} - - {_id: 3, y: [{b: 5, c: [{d: 2}, {d: 0}] }]} diff --git a/source/crud/tests/write/updateOne-collation.json b/source/crud/tests/write/updateOne-collation.json deleted file mode 100644 index c49112d519..0000000000 --- a/source/crud/tests/write/updateOne-collation.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "tests": [ - { - "description": "UpdateOne when one document matches with collation", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "x": "PING" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateOne-collation.yml b/source/crud/tests/write/updateOne-collation.yml deleted file mode 100644 index 468e946742..0000000000 --- a/source/crud/tests/write/updateOne-collation.yml +++ /dev/null @@ -1,25 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 'ping'} -minServerVersion: '3.4' - -tests: - - - description: "UpdateOne when one document matches with collation" - operation: - name: "updateOne" - arguments: - filter: {x: 'PING'} - update: - $set: {x: 'pong'} - collation: { locale: 'en_US', strength: 2} # https://docs.mongodb.com/master/reference/collation/#collation-document - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 'pong'} diff --git a/source/crud/tests/write/updateOne.json b/source/crud/tests/write/updateOne.json deleted file mode 100644 index 76d2086bdb..0000000000 --- a/source/crud/tests/write/updateOne.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "UpdateOne when many documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - }, - { - "description": "UpdateOne when one document matches", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateOne when no documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert when no documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/source/crud/tests/write/updateOne.yml b/source/crud/tests/write/updateOne.yml deleted file mode 100644 index 223532feac..0000000000 --- a/source/crud/tests/write/updateOne.yml +++ /dev/null @@ -1,84 +0,0 @@ -data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} -minServerVersion: '2.6' - -tests: - - - description: "UpdateOne when many documents match" - operation: - name: "updateOne" - arguments: - filter: - _id: {$gt: 1} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - # Can't verify collection data because we don't have a way of - # knowing which document gets updated. - - - description: "UpdateOne when one document matches" - operation: - name: "updateOne" - arguments: - filter: {_id: 1} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 12} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "UpdateOne when no documents match" - operation: - name: "updateOne" - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - - description: "UpdateOne with upsert when no documents match" - operation: - name: "updateOne" - arguments: - filter: {_id: 4} - update: - $inc: {x: 1} - upsert: true - - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - collection: - data: - - {_id: 1, x: 11} - - {_id: 2, x: 22} - - {_id: 3, x: 33} - - {_id: 4, x: 1} diff --git a/source/dbref.rst b/source/dbref.rst new file mode 100644 index 0000000000..a63ea99332 --- /dev/null +++ b/source/dbref.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `dbref.md `_. diff --git a/source/dbref/dbref.md b/source/dbref/dbref.md new file mode 100644 index 0000000000..c132e54876 --- /dev/null +++ b/source/dbref/dbref.md @@ -0,0 +1,282 @@ +# DBRef + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +DBRefs are a convention for expressing a reference to another document as an embedded document (i.e. BSON type 0x03). +Several drivers provide a model class for encoding and/or decoding DBRef documents. This specification will both define +the structure of a DBRef and provide guidance for implementing model classes in drivers that choose to do so. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +This specification presents documents as Extended JSON for readability and expressing special types (e.g. ObjectId). +Although JSON fields are unordered, the order of fields presented herein should be considered pertinent. This is +especially relevant for the [Test Plan](#test-plan). + +## Specification + +### DBRef Structure + +A DBRef is an embedded document with the following fields: + +- `$ref`: required string field. Contains the name of the collection where the referenced document resides. This MUST be + the first field in the DBRef. +- `$id`: required field. Contains the value of the `_id` field of the referenced document. This MUST be the second field + in the DBRef. +- `$db`: optional string field. Contains the name of the database where the referenced document resides. If specified, + this MUST be the third field in the DBRef. If omitted, the referenced document is assumed to reside in the same + database as the DBRef. +- Extra, optional fields may follow after `$id` or `$db` (if specified). There are no inherent restrictions on extra + field names; however, older server versions may impose their own restrictions (e.g. no dots or dollars). + +DBRefs have no relation to the deprecated DBPointer BSON type (i.e. type 0x0C). + +#### Examples of Valid DBRefs + +The following examples are all valid DBRefs: + +```typescript +// Basic DBRef with only $ref and $id fields +{ "$ref": "coll0", "$id": { "$oid": "60a6fe9a54f4180c86309efa" } } + +// DBRef $id is not necessarily an ObjectId +{ "$ref": "coll0", "$id": 1 } + +// DBRef with optional $db field +{ "$ref": "coll0", "$id": 1, "$db": "db0" } + +// DBRef with extra, optional fields (with or without $db) +{ "$ref": "coll0", "$id": 1, "$db": "db0", "foo": "bar" } +{ "$ref": "coll0", "$id": 1, "foo": true } + +// Extra field names have no inherent restrictions +{ "$ref": "coll0", "$id": 1, "$foo": "bar" } +{ "$ref": "coll0", "$id": 1, "foo.bar": 0 } +``` + +#### Examples of Invalid DBRefs + +The following examples are all invalid DBRefs: + +```typescript +// Required fields are omitted +{ "$ref": "coll0" } +{ "$id": { "$oid": "60a6fe9a54f4180c86309efa" } } + +// Invalid types for $ref or $db +{ "$ref": true, "$id": 1 } +{ "$ref": "coll0", "$id": 1, "$db": 1 } + +// Fields are out of order +{ "$id": 1, "$ref": "coll0" } +``` + +### Implementing a DBRef Model + +Drivers MAY provide a model class for encoding and/or decoding DBRef documents. For those drivers that do, this section +defines expected behavior of that class. This section does not prohibit drivers from implementing additional +functionality, provided it does not conflict with any of these guidelines. + +#### Constructing a DBRef model + +Drivers MAY provide an API for constructing a DBRef model directly from its constituent parts. If so: + +- Drivers MUST solicit a string value for `$ref`. +- Drivers MUST solicit an arbitrary value for `$id`. Drivers SHOULD NOT enforce any restrictions on this value; however, + this may be necessary if the driver is unable to differentiate between certain BSON types (e.g. `null`, `undefined`) + and the parameter being unspecified. +- Drivers SHOULD solicit an optional string value for `$db`. +- Drivers MUST require `$ref` and `$db` (if specified) to be strings but MUST NOT enforce any + [naming restrictions](https://www.mongodb.com/docs/manual/reference/limits/#naming-restrictions) on the string + values. +- Drivers MAY solicit extra, optional fields. + +#### Decoding a BSON document to a DBRef model + +Drivers MAY support explicit and/or implicit decoding. An example of explicit decoding might be a DBRef model +constructor that takes a BSON document. An example of implicit decoding might be configuring the driver's BSON codec to +automatically convert embedded documents that comply with the [DBRef Structure](#dbref-structure) into a DBRef model. + +Drivers that provide implicit decoding SHOULD provide some way for applications to opt out and allow DBRefs to be +decoded like any other embedded document. + +When decoding a BSON document to a DBRef model: + +- Drivers MUST require `$ref` and `$id` to be present. +- Drivers MUST require `$ref` and `$db` (if present) to be strings but MUST NOT enforce any + [naming restrictions](https://www.mongodb.com/docs/manual/reference/limits/#naming-restrictions) on the string + values. +- Drivers MUST accept any BSON type for `$id` and MUST NOT enforce any restrictions on its value. +- Drivers MUST preserve extra, optional fields (beyond `$ref`, `$id`, and `$db`) and MUST provide some way to access + those fields via the DBRef model. For example, an accessor method that returns the original BSON document (including + `$ref`, etc.) would fulfill this requirement. + +If a BSON document cannot be implicitly decoded to a DBRef model, it MUST be left as-is (like any other embedded +document). If a BSON document cannot be explicitly decoded to a DBRef model, the driver MUST raise an error. + +Since DBRefs are a special type of embedded document, a DBRef model class used for decoding SHOULD inherit the class +used to represent an embedded document (e.g. Hash in Ruby). This will allow applications to always expect an instance of +a common class when decoding an embedded document (if desired) and should also support the requirement for DBRef models +to provide access to any extra, optional fields. + +#### Encoding a DBRef model to a BSON document + +Drivers MAY support explicit and/or implicit encoding. An example of explicit encoding might be a DBRef method that +returns its corresponding representation as a BSON document. An example of implicit encoding might be configuring the +driver's BSON codec to automatically convert DBRef models to the corresponding BSON document representation as needed. + +If a driver supports implicit decoding of BSON to a DBRef model, it SHOULD also support implicit encoding. Doing so will +allow applications to more easily round-trip DBRefs through the driver. + +When encoding a DBRef model to BSON document: + +- Drivers MUST encode all fields in the order defined in [DBRef Structure](#dbref-structure). +- Drivers MUST encode `$ref` and `$id`. If `$db` was specified, it MUST be encoded after `$id`. If any extra, optional + fields were specified, they MUST be encoded after `$id` or `$db`. +- If the DBRef includes any extra, optional fields after `$id` or `$db`, drivers SHOULD attempt to preserve the original + order of those fields relative to one another. + +## Test Plan + +The test plan consists of a series of prose tests. These tests are only relevant to drivers that provide a DBRef model +class. + +The documents in these tests are presented as Extended JSON for readability; however, readers should consider the field +order pertinent when translating to BSON (or their language equivalent). These tests are not intended to exercise a +driver's Extended JSON parser. Implementations SHOULD construct the documents directly using native BSON types (e.g. +Document, ObjectId). + +### Decoding + +These tests are only relevant to drivers that allow decoding into a DBRef model. Drivers SHOULD implement these tests +for both explicit and implicit decoding code paths as needed. + +1. Valid documents MUST be decoded to a DBRef model. For each of the following: + + 1. `{ "$ref": "coll0", "$id": { "$oid": "60a6fe9a54f4180c86309efa" } }` + 2. `{ "$ref": "coll0", "$id": 1 }` + 3. `{ "$ref": "coll0", "$id": null }` + 4. `{ "$ref": "coll0", "$id": 1, "$db": "db0" }` + + Assert that each document is successfully decoded to a DBRef model. Assert that the `$ref`, `$id`, and `$db` (if + applicable) fields have their expected value. + +2. Valid documents with extra fields MUST be decoded to a DBRef model and the model MUST provide some way to access + those extra fields. For each of the following: + + 1. `{ "$ref": "coll0", "$id": 1, "$db": "db0", "foo": "bar" }` + 2. `{ "$ref": "coll0", "$id": 1, "foo": true, "bar": false }` + 3. `{ "$ref": "coll0", "$id": 1, "meta": { "foo": 1, "bar": 2 } }` + 4. `{ "$ref": "coll0", "$id": 1, "$foo": "bar" }` + 5. `{ "$ref": "coll0", "$id": 1, "foo.bar": 0 }` + + Assert that each document is successfully decoded to a DBRef model. Assert that the `$ref`, `$id`, and `$db` (if + applicable) fields have their expected value. Assert that it is possible to access all extra fields and that those + fields have their expected value. + +3. Documents with out of order fields that are otherwise valid MUST be decoded to a DBRef model. For each of the + following: + + 1. `{ "$id": 1, "$ref": "coll0" }` + 2. `{ "$db": "db0", "$ref": "coll0", "$id": 1 }` + 3. `{ "foo": 1, "$id": 1, "$ref": "coll0" }` + 4. `{ "foo": 1, "$ref": "coll0", "$id": 1, "$db": "db0" }` + 5. `{ "foo": 1, "$ref": "coll0", "$id": 1, "$db": "db0", "bar": 1 }` + + Assert that each document is successfully decoded to a DBRef model. Assert that the `$ref`, `$id`, `$db` (if + applicable), and any extra fields (if applicable) have their expected value. + +4. Documents missing required fields MUST NOT be decoded to a DBRef model. For each of the following: + + 1. `{ "$ref": "coll0" }` + 2. `{ "$id": { "$oid": "60a6fe9a54f4180c86309efa" } }` + 3. `{ "$db": "db0" }` + + Assert that each document is not decoded to a DBRef model. In the context of implicit decoding, the document MUST be + decoded like any other embedded document. In the context of explicit decoding, the DBRef decoding method MUST raise + an error. + +5. Documents with invalid types for `$ref` or `$db` MUST NOT be decoded to a DBRef model. For each of the following: + + 1. `{ "$ref": true, "$id": 1 }` + 2. `{ "$ref": "coll0", "$id": 1, "$db": 1 }` + + Assert that each document is not decoded to a DBRef model. In the context of implicit decoding, the document MUST be + decoded like any other embedded document. In the context of explicit decoding, the DBRef decoding method MUST raise + an error. + +### Encoding + +These tests are only relevant to drivers that allow encoding a DBRef model. Drivers SHOULD implement these tests for +both explicit and implicit encoding code paths as needed. + +Drivers MAY use any method to create the DBRef model for each test (e.g. constructor, explicit decoding method). + +Drivers MAY skip tests that cannot be implemented as written (e.g. DBRef model constructor does not support extra, +optional fields and the driver also does not support explicit/implicit decoding). + +1. Encoding DBRefs with basic fields. + + - For each of the following: + + 1. `{ "$ref": "coll0", "$id": { "$oid": "60a6fe9a54f4180c86309efa" } }` + 2. `{ "$ref": "coll0", "$id": 1 }` + 3. `{ "$ref": "coll0", "$id": null }` + 4. `{ "$ref": "coll0", "$id": 1, "$db": "db0" }` + + - Assert that each DBRef model is successfully encoded to a BSON document. Assert that the `$ref`, `$id`, and `$db` + (if applicable) fields appear in the correct order and have their expected values. + +2. Encoding DBRefs with extra, optional fields. + + - For each of the following: + + 1. `{ "$ref": "coll0", "$id": 1, "$db": "db0", "foo": "bar" }` + 2. `{ "$ref": "coll0", "$id": 1, "foo": true, "bar": false }` + 3. `{ "$ref": "coll0", "$id": 1, "meta": { "foo": 1, "bar": 2 } }` + 4. `{ "$ref": "coll0", "$id": 1, "$foo": "bar" }` + 5. `{ "$ref": "coll0", "$id": 1, "foo.bar": 0 }` + + - Assert that each DBRef model is successfully encoded to a BSON document. Assert that the `$ref`, `$id`, `$db` (if + applicable), and any extra fields appear in the correct order and have their expected values. + +3. Encoding DBRefs re-orders any out of order fields during decoding. This test MUST NOT use a constructor that solicits + fields individually. + + - For each of the following: + + 1. `{ "$id": 1, "$ref": "coll0" }` + 2. `{ "$db": "db0", "$ref": "coll0", "$id": 1 }` + 3. `{ "foo": 1, "$id": 1, "$ref": "coll0" }` + 4. `{ "foo": 1, "$ref": "coll0", "$id": 1, "$db": "db0" }` + 5. `{ "foo": 1, "$ref": "coll0", "$id": 1, "$db": "db0", "bar": 1 }` + + - Assert that each document is successfully decoded to a DBRef model and then successfully encoded back to a BSON + document. Assert that the order of fields in each encoded BSON document matches the following, respectively: + + 1. `{ "$ref": "coll0", "$id": 1 }` + 2. `{ "$ref": "coll0", "$id": 1, "$db": "db0" }` + 3. `{ "$ref": "coll0", "$id": 1, "foo": 1 }` + 4. `{ "$ref": "coll0", "$id": 1, "$db": "db0", "foo": 1}` + 5. `{ "$ref": "coll0", "$id": 1, "$db": "db0", "foo": 1, "bar": 1 }` + +## Design Rationale + +In contrast to always encoding DBRefs with the correct field order, decoding permits fields to be out of order (provided +the document is otherwise valid). This follows the +[robustness principle](https://en.wikipedia.org/wiki/Robustness_principle) in having the driver be liberal in what it +accepts and conservative in what it emits. This does mean that round-tripping an out of order DBRef through a driver +could result in its field order being changed; however, this behavior is consistent with existing behavior in drivers +that model DBRefs (e.g. C#, Java, Node, Python, Ruby) and applications can opt out of implicit decoding if desired. + +## Changelog + +- 2024-02-26: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter. diff --git a/source/driver-bulk-update.rst b/source/driver-bulk-update.rst index cda3bca7d1..e6830c721a 100644 --- a/source/driver-bulk-update.rst +++ b/source/driver-bulk-update.rst @@ -1,41 +1,14 @@ +============= Bulk API Spec ============= -:Authors: Christian Kvalheim :Status: Deprecated -:Type: Standards -:Last Modified: October 9, 2015 +:Minimum Server Version: 2.4 .. contents:: -Changes from previous versions ------------------------------- - -Deprecated in favor of the *Driver CRUD API*. - -v0.7 -~~~~ -* Clarify that "writeConcernErrors" field is plural - -v0.6 -~~~~ -* First public version of the specification. -* Merged in Test Cases from QA tickets -* Specification cleanup and increased precision - -v0.5 -~~~~ -* Specification cleanup and increased precision -* Suggested Error handling for languages using commonly raising exceptions -* Narrowed writeConcern reporting requirement - -v0.4 -~~~~ -* Renamed nUpdated to nMatched as to reflect that it's the number of matched documents not the number of modified documents. - - Bulk Operation Builder ----------------------- +====================== Starting a bulk operation can be done in two ways. @@ -45,7 +18,7 @@ Starting a bulk operation can be done in two ways. initializeOrderedBulkOp() -> Bulk - Initialize an ordered bulk Operations Possible On Bulk Instance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------ Available operations follow the fluent API for insert, update and remove. @@ -118,7 +91,7 @@ remove. bulk.execute(writeConcern); Current shell implementation ----------------------------- +============================ The shell implementation serves as a guide only. One main difference between the shell implementation and a proper driver implementation is that unordered bulk operations are not optimized by re-ordering the writes; only the execution semantics are kept correct. You can find it here: @@ -127,7 +100,7 @@ https://github.com/mongodb/mongo/blob/master/src/mongo/shell/bulk_api.js If you need more information about the actual write command you can find the specification at the following location -https://github.com/mongodb/specifications/blob/master/source/server_write_commands.rst +server_write_commands.md Modes of Execution ------------------ @@ -229,11 +202,11 @@ Internally the driver will execute 3 write commands. One each for the inserts, u Request Size Limits ------------------- -Supporting unlimited batch sizes poses two problems - the BSONObj internal size limit is 16MB + small overhead (for commands), and a small write operation may have a much larger response. In order to ensure a batch can be correctly processed, two limits must be respected. +Supporting unlimited batch sizes poses two problems - the BSONObj internal size limit is 16 MiB + small overhead (for commands), and a small write operation may have a much larger response. In order to ensure a batch can be correctly processed, two limits must be respected. -Both of these limits can be found using isMaster(): +Both of these limits can be found using hello or legacy hello: -* ``maxBsonObjectSize`` : currently 16MB, this is the maximum size of writes (excepting command overhead) +* ``maxBsonObjectSize`` : currently 16 MiB, this is the maximum size of writes (excepting command overhead) that should be sent to the server. Documents to be inserted, query documents for updates and deletes, and update expression documents must be <= this size. @@ -242,7 +215,7 @@ Both of these limits can be found using isMaster(): the command itself is guaranteed not to trigger an error from the server, except in the case of `SERVER-12305 `_. -* ``maxWriteBatchSize`` : currently 1000, this is the maximum number of inserts, updates, or deletes that +* ``maxWriteBatchSize`` : currently 100,000 (as of MongoDB 3.6 via `SERVER-13061 `__), this is the maximum number of inserts, updates, or deletes that can be included in a write batch. If more than this number of writes are included, the server cannot guarantee space in the response document to reply to the batch. @@ -751,12 +724,12 @@ Code Description Thee are also some some errors only detectable by inspecting the **errmsg** field. -=================== ============================================================= -ErrMsg Description -=================== ============================================================= -exceeded time limit Operation exceeded time limit. -execution terminated Operation exceeded time limit. -=================== ============================================================= +==================== ============================================================= +ErrMsg Description +==================== ============================================================= +exceeded time limit Operation exceeded time limit. +execution terminated Operation exceeded time limit. +==================== ============================================================= If an error does not return a code the driver can set the returned value to **8** (unknown error). A BSON serializing error should be marked with **22** (illegal BSON). @@ -800,6 +773,8 @@ The driver algorithm for merging results, when using write commands, in pseudoco "upserted": [], } +:: + for each server response in all bulk operations' responses: if the operation is an update: if the response has a non-NULL nModified: @@ -895,13 +870,7 @@ Test Case 2: Same for initializeOrderedBulkOp(). -Test Case 3: - Key validation, no $-prefixed keys allowed: - batch = initializeUnorderedBulkOp().insert({$key: 1}) - - bulk.execute() throws reasonable error (server does the validation) - - Same for initializeOrderedBulkOp(). +Test Case 3: *Removed* Test Case 4: Inserting a document succeeds and returns 'nInserted’ of 1: @@ -1233,13 +1202,13 @@ Test Case 2: Same for initializeOrderedBulkOp(). - We can upsert() a 16MB document—the driver can make a command document slightly larger than the max document size. + We can upsert() a 16 MiB document—the driver can make a command document slightly larger than the max document size. Empty collection. .. code:: javascript - var bigstring = “string of length 16MB - 30 bytes” + var bigstring = "string of length 16 MiB - 30 bytes" batch = initializeUnorderedBulkOp() batch.find({key: 1}).upsert().update({$set: {x: bigstring}}) batch.execute() succeeds. @@ -1554,21 +1523,21 @@ Empty collection, unique index on 'a’. error['code'] == 11000 error['errmsg'] is a string. error['index'] == 1 - error['op'] == {q: {b: 2}, u: {$set: {a: 1}}, multi: false, upsert: True} + error['op'] == {q: {b: 2}, u: {$set: {a: 1}}, upsert: true} collection.count() == 1 // subsequent inserts weren’t attempted BATCH SPLITTING: maxBsonObjectSize ---------------------------------- -More than 16MB worth of inserts are split into multiple messages, and error indexes are rewritten. An unordered batch continues on error and returns the error after all messages are sent. +More than 16 MiB worth of inserts are split into multiple messages, and error indexes are rewritten. An unordered batch continues on error and returns the error after all messages are sent. Empty collection. .. code:: javascript - // Verify that the driver splits inserts into 16-MB messages: + // Verify that the driver splits inserts into 16-MiB messages: batch = initializeOrderedBulkOp() for (i = 0; i < 6; i++) { - batch.insert({_id: i, a: '4 MB STRING'}); + batch.insert({_id: i, a: '4 MiB STRING'}); } batch.insert({_id: 0}) // will fail @@ -1739,3 +1708,23 @@ sleep 6 seconds client.admin.command({replSetStepDown: 5}) batch = client.db.collection.initializeOrderedBulkOp() batch.insert({_id: 2}).execute() should succeed + +Changelog +========= + +:2023-10-17: Updated ``maxWriteBatchSize`` default to 100,000 from 1000 +:2022-10-05: Remove spec front matter and reformat changelog. Consolidated + changelog entries prior to the first published version of this + document, since exact dates were unavailable. +:2021-05-27: Removed "Test Case 3: Key validation, no $-prefixed keys allowed" + for insert. +:2015-10-23: Clarify that "writeConcernErrors" field is plural +:2015-05-22: * First public version of the specification. + * Deprecated this specification in favor of CRUD API. + * Merged in Test Cases from QA tickets. + * Specification cleanup and increased precision. + * Suggested error handling for languages using commonly raising + exceptions. + * Narrowed writeConcern reporting requirement. + * Renamed nUpdated to nMatched as to reflect that it's the number + of matched documents not the number of modified documents. diff --git a/source/driver-mantras.md b/source/driver-mantras.md new file mode 100644 index 0000000000..bf52c62f0a --- /dev/null +++ b/source/driver-mantras.md @@ -0,0 +1,69 @@ +# Driver Mantras + +When developing specifications -- and the drivers themselves -- we follow the following principles: + +### Strive to be idiomatic, but favor consistency + +Drivers attempt to provide the easiest way to work with MongoDB in a given language ecosystem, while specifications +attempt to provide a consistent behavior and experience across all languages. Drivers should strive to be as idiomatic +as possible while meeting the specification and staying true to the original intent. + +### No Knobs + +Too many choices stress out users. Whenever possible, we aim to minimize the number of configuration options exposed to +users. In particular, if a typical user would have no idea how to choose a correct value, we pick a good default instead +of adding a knob. + +### Topology agnostic + +Users test and deploy against different topologies or might scale up from replica sets to sharded clusters. Applications +should never need to use the driver differently based on topology type. + +### Where possible, depend on server to return errors + +The features available to users depend on a server's version, topology, storage engine and configuration. So that +drivers don't need to code and test all possible variations, and to maximize forward compatibility, always let users +attempt operations and let the server error when it can't comply. Exceptions should be rare: for cases where the server +might not error and correctness is at stake. + +### Minimize administrative helpers + +Administrative helpers are methods for admin tasks, like user creation. These are rarely used and have maintenance costs +as the server changes the administrative API. Don't create administrative helpers; let users rely on "RunCommand" for +administrative commands. + +### Check wire version, not server version + +When determining server capabilities within the driver, rely only on the maxWireVersion in the hello response, not on +the X.Y.Z server version. An exception is testing server development releases, as the server bumps wire version early +and then continues to add features until the GA. + +### When in doubt, use "MUST" not "SHOULD" in specs + +Specs guide our work. While there are occasionally valid technical reasons for drivers to differ in their behavior, +avoid encouraging it with a wishy-washy "SHOULD" instead of a more assertive "MUST". + +### Defy augury + +While we have some idea of what the server will do in the future, don't design features with those expectations in mind. +Design and implement based on what is expected in the next release. + +Case Study: In designing OP_MSG, we held off on designing support for Document Sequences in Replies in drivers until the +server would support it. We subsequently decided not to implement that feature in the server. + +### The best way to see what the server does is to test it + +For any unusual case, relying on documentation or anecdote to anticipate the server's behavior in different +versions/topologies/etc. is error-prone. The best way to check the server's behavior is to use a driver or the shell and +test it directly. + +### Drivers follow semantic versioning + +Drivers should follow X.Y.Z versioning, where breaking API changes require a bump to X. See +[semver.org](https://semver.org/) for more. + +### Backward breaking behavior changes and semver + +Backward breaking behavior changes can be more dangerous and disruptive than backward breaking API changes. When +thinking about the implications of a behavior change, ask yourself what could happen if a user upgraded your library +without carefully reading the changelog and/or adequately testing the change. diff --git a/source/driver-read-preferences.rst b/source/driver-read-preferences.rst deleted file mode 100644 index 29882ecc2c..0000000000 --- a/source/driver-read-preferences.rst +++ /dev/null @@ -1,474 +0,0 @@ -====================================== -Driver Read Preferences: Specification -====================================== - -:author: A\. Jesse Jiryu Davis -:date: Feb 14, 2014 -:version: 4 -:status: Deprecated - -Deprecated in favor of the *Server Selection Spec* and *Server Discovery And -Monitoring Spec*. - -.. contents:: - -The Objective -------------- - -How do drivers and mongos choose which member of a replica set to read from -for queries and read-only commands? - -How do application developers control that behavior? - -Terms ------ - -Connection: - An instance of a "Connection" or "Mongo" class, or whatever class - represents a MongoDB server in each driver. - -socket: - An open socket to the MongoDB Server. - -RS: - A replica set. - -near: - responding promptly to the ``ping`` command. - -far: - not responding promptly to the ``ping`` command. - -Assumptions ------------ - -Unless they explicitly override these priorities, we assume our customers -prefer their applications to be, in order: - -* Consistent -* Available -* Low-latency - -Drivers and mongos know the state of the replica set (how they do RS monitoring is outside this spec): - -* They know which members are up or down, what their tags are, and which is primary -* They know ping times to each available member -* They detect reconfigs and the addition or removal of members - -Read Preferences ----------------- - -A "read preference" determines the candidate replica set members to which a query or command can be sent. It consists of: - -* "mode": Whether to read from primary or secondary -* "tag sets": How to choose a member based on its tags in the replica set configuration -* "acceptable latency": How to choose a member based on ping time - -The mode and tag sets filter out members of the replica set as candidates for a -read operation. These two options stack, or in other words, the mode defines a -subset of nodes to potentially read from and the tags refine that subset even -further. The driver or mongos chooses a member from among the nearest members -that match the mode and tag sets. - -Mode -~~~~ - -Drivers and mongos will support five modes: - -"PRIMARY": -`````````` - -Read from primary only. All operations produce an error (throw an exception -where applicable) if primary is unavailable. Cannot be combined with tags. - -*This is the default.* - -"PRIMARY_PREFERRED": -```````````````````` - -Read from primary if available, otherwise a secondary. - - -"SECONDARY": -```````````` - -Read from secondary if available, otherwise error. - - -"SECONDARY_PREFERRED": -`````````````````````` - -Read from a secondary if available, otherwise read from the primary. - -"NEAREST": -`````````` - -Read from any member. - -*Note on NEAREST*: All modes read from among the nearest candidates, but unlike -other modes, NEAREST will include both the primary and all secondaries in the -random selection. The name NEAREST is chosen to emphasize its use, when -latency is most important. For I/O-bound users who want to distribute reads -across all members evenly regardless of ping time, set -secondaryAcceptableLatencyMS very high. See "Ping Times" below. - -*Note on other member states*: Members can be in several states besides PRIMARY -or SECONDARY, e.g. STARTUP2 or RECOVERING. Such a member must not be used for -any read no matter what. See -`DRIVERS-73 `_. - -Tag Sets -~~~~~~~~ - -Drivers will support the use of tag sets in combination with a mode. This can -be utilized for data center awareness. Tags only filter secondary read -operations. Primary will be read independent of any tags. - -A member matches a tag set if its tags match all the tags in the set. For -example, a member tagged "\{ dc: 'ny', rack: 2, size: 'large' \}" matches the -tag set "\{ dc: 'ny', rack: 2 \}". A member's extra tags don't affect whether -it's a match. - -A read preference optionally includes a list of tag sets. A driver or mongos -searches through the list, from first tag set to last, looking for a tag set -that matches one or more members which also match the mode. - -If no tag is provided it will match any member which matches the mode (PRIMARY, -SECONDARY, etc.). For compatibility reasons the final tag set can be empty -document \{ \}, which has the same behavior as not providing this parameter. - -How Modes Interact With Tag Sets: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -"PRIMARY": -`````````` - -It is an error to specify any tag sets with PRIMARY, see "errors" below. - -"PRIMARY_PREFERRED": -```````````````````` - -If the primary is up, read from it no matter how it's tagged. If the primary is -down, read from a secondary matching the tags provided. If there is none, -error. - -"SECONDARY": -```````````` - -The driver or mongos searches through list of tag sets from first to last. When -it finds a tag set matching any available secondaries, it picks a random -secondary among the nearest of them. If no available secondaries match the -tags, raise an error. - -"SECONDARY_PREFERRED": -`````````````````````` - -The driver or mongos searches through the list of tag sets from first to last. -When it finds a tag set matching any available secondaries, it picks a random -secondary among the nearest of them. If there are no tag sets matching any -secondaries, it reads from primary regardless of any tags provided. - -"NEAREST": -`````````` - -The driver or mongos searches through the list of tag sets from first to last. -When it finds a tag set matching any available members, it picks a random -member among the nearest of them. - -Ping Times -~~~~~~~~~~ - -Once the driver or mongos has found a list of candidate members based on mode -and tag sets, determine the "nearest" member as the one with the quickest -response to a periodic ping command. (The driver already knows ping times to -all members, see "assumptions" above.) Choose a member randomly from those at -most 15ms "farther" than the nearest member. The 15ms cutoff is overridable -with "secondaryAcceptableLatencyMS". - -For mode SECONDARY_PREFERRED, the driver or mongos tries to use a random -secondary member that matches the tag sets and secondaryAcceptableLatencyMS. -Failing that, it uses the primary regardless of its ping time. - -*Note*: This is the Java driver's algorithm for choosing a secondary now, -expanded to include read preferences. - -*Note*: We recommend periodically pinging all members and tracking a moving -average of recent ping times, but that is not required by this spec. - -Requests and Auto-retry ------------------------ - -Requests -~~~~~~~~ - -A driver's association of a socket with an application thread is called a -"request". A thread can be "in a request," meaning some association between the -thread and the request is guaranteed, or "not in a request". Drivers differ in -their request semantics, e.g. PyMongo-based programs can opt out of requests, -while Java driver-based programs must opt in. - -Client connections to mongos are always "in a request" as long as the client -connection lives. - -This section details the relationship of modes, requests, and auto-retry. We -describe how the driver or mongos reacts to a member becoming unavailable, -determined either by a socket error when attempting a read, or by background -monitoring. - -*Goals:* Simple implementation, and consistent behavior among the drivers and -mongos. It is *not* a goal to guarantee that a series of reads always moves -forward in time, nor to guarantee read-your-writes consistency, outside of -mode PRIMARY. - -For all modes, a mongos or driver picks an appropriate replica-set member, from -among those believed to be up, for a thread's first read. If that first read -throws a socket error, the driver or mongos may try up to 2 other members, if -those members match the tags and mode. After a total of 3 failures, or after -running out of appropriate members to try, it throws an error to the -application. Once a read succeeds with some member (and the thread is in a -request, in the case of the drivers), the thread is *pinned* to that member. - -The driver / mongos remembers the read preference (mode, tag sets, and -acceptable latency) that the thread / client used for this first read. As long -as the request lasts and all reads use the same read preference, all reads are -routed to the pinned member. A read that uses a new read preference (different -mode, tags, or acceptable latency) unpins the thread and restarts the -member-selection process from scratch. (See `Note On Pinning`_.) All primary -operations within the request must use the same socket, even if interleaved -with operations on secondaries. (For simplicity, drivers may also use a single -socket on each secondary, but this is not required.) - -Not only a change in the read preference can unpin a member: if the client -detects that a member has switched from primary to secondary or vice versa, -such that the member no longer matches a thread's read preference, the member -is unpinned. Changes in tags or ping time do not unpin a member. In short: when -the client refreshes its view of the set, if there's a new primary the client -discards the pinning state for all threads. Otherwise, it does not discard the -pinning state. - -Auto-retry -~~~~~~~~~~ - -After the first successful read in a request, a thread is pinned to a -replica-set member (a secondary or the primary). If that member goes down, the -driver or mongos will try other members according to the original selection -logic (taking mode, tags, and ping times into account). When it finds an up -member, the client is now pinned to the new member. Only if the driver or -mongos runs out of members to try, or has tried *three* members and found them -to be down, does it return an error to the client. The client's next read will -begin the selection logic from scratch with no pinned member. - -*Note*: If the member was determined to be down because of a socket error, the -Connection should refresh its view of the replica-set state ASAP. - -Commands --------- - -Generic Command Helper -~~~~~~~~~~~~~~~~~~~~~~ - -The driver's generic ``command()`` or ``runCommand()`` API accepts a read -preference, but it only obeys the preference for these commands: - -* group -* inline mapreduce -* aggregate without $out specified -* collStats, dbStats -* count, distinct -* geoNear, geoSearch, geoWalk -* text -* parallelCollectionScan - -For these exceptional commands, the driver or mongos obeys the read preference -the same as for queries. Otherwise, all commands are run on the primary. - -If the driver is directly connected to a member (either the primary or a -secondary) it ignores this list and sends all commands to the member to which -it's connected. (It sets the slaveOkay bit to 1 for any read preference besides -PRIMARY.) Thus we still have a means to run commands like "compact" on -secondaries: via direct connection. - -*Note*: In the future we'll need to add a field to commands in ``listCommands`` -to distinguish new commands that should obey the read preference. The -``slaveOk`` field alone doesn't cover this: e.g., ``reindex`` has ``slaveOk`` -true, but we've decided that ``reindex`` with a read preference of SECONDARY -should *not* reindex a random secondary, it should be run on the primary. - -*Note*: mongos doesn't distribute the "text" command to secondaries, -see `SERVER-10947 `_. - -Command-Specific Helpers -~~~~~~~~~~~~~~~~~~~~~~~~ - -The driver will accept no read preference for any command-specific helper the -driver implements, *unless* the command can run on a secondary (e.g., -``count``). - -If the command can run on a secondary, the helper method can accept and obey a -read preference. If no read preference is specified to the helper, then the -collection's, database's, or connection's read preference is used, same as for -queries. - -mongos accepts a read preferences for a command same as for queries, and obeys -or ignores the preference the same as drivers. - -Driver API Guidelines ---------------------- - -These are suggestions. As always, driver authors should balance cross-language -standardization with backwards compatibility and the idioms of their language. - -Modes -~~~~~ - -Modes (PRIMARY, SECONDARY, ...) are constants declared in whatever way is -idiomatic for the programming language. The constant values may be ints, -strings, or whatever. - -Tags -~~~~ - -Tags can be configured in the driver in whatever way is natural for the -language. The "Communication with mongos" section below may provide inspiration -for formatting tags. - -Read Preference Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Applications may set mode and/or tag sets on a per-operation basis similar to -how ``addSpecial``, ``hint``, or ``batchSize`` are set. E.g., in Python:: - - db.collection.find({}).read_preference(ReadPreference.SECONDARY) - db.collection.find({}).read_preference(ReadPreference.NEAREST, [ {'dc': 'ny'} ]) - -Mode and tag sets can be set on a ``Connection``, ``Database``, or -``Collection`` object with a method named like setReadPreference or -set_read_preference, etc. - -``secondaryAcceptableLatencyMS``, on the other hand, can only be set on the -``Connection`` object. - -Errors -~~~~~~ - -If the driver cannot find an available member that matches the ReadPreference, -the driver should immediately raise an exception without attempting any network -operations or refreshing its view of the replica set. If the driver -distinguishes between configuration and connection errors, this is a connection -error. (Justification: the application may not be misconfigured, the expected -member may just be down.) - -Reading from a direct connection to a secondary raises an exception if -preference is ``PRIMARY``. (Same as if slaveOk were false.) - -Drivers should return useful error messages, as in the following examples, when -there are no members matching the ReadPreference: - -* "PRIMARY cannot be combined with tags" -* "No replica set primary available for query with ReadPreference PRIMARY" -* "No replica set secondary available for query with ReadPreference SECONDARY" -* For NEAREST, PRIMARY_PREFERRED, or SECONDARY_PREFERRED, "No replica set members available for query" -* "No replica set member available for query with ReadPreference " + pref + " and tags " + tags - -slaveOk -~~~~~~~ - -The introduction of ``ReadPreference`` deprecates and totally supersedes -``slaveOk`` as a part of the driver API. ``slaveOk`` is deprecated. Until it's -removed, ``slaveOk=True`` means ``ReadPreference=SECONDARY_PREFERRED``. Passing -a value for both ``slaveOk`` and ``ReadPreference`` is an error: "slaveOk is -deprecated, use ReadPreference." - -``slaveOk`` remains as a bit in the wire protocol and drivers will set this bit -to ``1`` for all reads except with ``PRIMARY``. - -*Note*: Drivers must set the slaveOk bit to 1 with mode PRIMARY_PREFERRED. This -means that a new driver connected to an old mongos will send it a -$readPreference that the mongos will ignore, and reads will be sent to -secondaries. We should tell customers to upgrade mongos along with their -drivers to use read preferences. - -Communication with mongos -------------------------- - -mongos must support read preferences for queries and commands the same as -drivers. Drivers connected to a mongos send the read preference to mongos -formatted like:: - - { - ... usual fields ..., - $readPreference: { - mode: 'secondary', - tags: [ { 'dc': 'ny' } ] - } - } - -"Usual fields" includes $query -for a query, ``count:"collection"`` for a count command, etc. - -mongos validates the ``$readPreference`` document: - -* mode: the mode field must be present exactly once and have a lowercase string - value, a valid mode in camel case ("primary", "secondary", - "primaryPreferred", etc.) -* tags: the tags field must be absent, or be present exactly once and have an - array value containing at least one subdocument. It must contain only - documents, no other type. The field must be absent or contain only \{ \} if - mode is "primary". - -A misformatted ``$readPreference`` causes an error. - -Interaction of read preferences and the slaveOk wire-protocol bit: - -* If slaveOk is set, and no read preference is passed, mongos uses SECONDARY_PREFERRED -* If slaveOk is set, and a read preference is passed, mongos obeys the read preference -* If slaveOk is not set, and a read preference is passed, mongos obeys the read preference -* If slaveOk is not set and no read preference is passed, mongos uses PRIMARY - -mongos reports itself using ``"msg": "isdbgrid"`` in its response to -``isMaster``. Drivers should *not* send $readPreference unless connected to -mongos. - -Note: Drivers must not send $readPreference to mongos for mode PRIMARY (simply -don't set the slaveOk bit), or for mode SECONDARY_PREFERRED without tags -(simply set the slaveOk bit). That way these two read preferences\-\-PRIMARY, -and SECONDARY_PREFERRED without tags\-\-are backwards compatible with all -versions of mongos. - -Reference implementation ------------------------- - -Based on mongos and the basis of PyMongo's implementation: - -https://github.com/10gen/read-prefs-reference - -Note On Pinning ---------------- - -An earlier version of this spec, which was implemented in the versions of the -drivers and mongos released concomitantly with MongoDB 2.2, stated that a -thread / client should remain pinned to an RS member as long as that member -matched the current mode, tags, and acceptable latency. This minimized -time-travel, but had the following surprising consequence: - -1. Thread / client reads with mode SECONDARY or SECONDARY_PREFERRED, gets - pinned to a secondary -2. Thread / client reads with mode PRIMARY_PREFERRED, driver / mongos sees that - the pinned member (a secondary) matches the mode (which *allows* for a - secondary) and reads from secondary, even though the primary is available and - preferable - -The old spec also had the swapped problem, reading from the primary with -SECONDARY_PREFERRED, except for mongos which was changed at the last minute -before release with `SERVER-6565 "Do not use primary if secondaries are -available for slaveOk" `_. - -This left application developers with two problems: - -1. PRIMARY_PREFERRED and SECONDARY_PREFERRED acted surprisingly and - unpredictably within requests -2. There was no way to specify a common need: read from a secondary if possible - with SECONDARY_PREFERRED, then from primary if possible with PRIMARY_PREFERRED, - all within a request. Instead an application developer would have to do the - second read with PRIMARY, which would unpin the thread but risk unavailability - if only secondaries were up. diff --git a/source/enumerate-collections.rst b/source/enumerate-collections.rst index 4ae1ac1a6d..5964cbb3b3 100644 --- a/source/enumerate-collections.rst +++ b/source/enumerate-collections.rst @@ -1,441 +1,4 @@ -.. role:: javascript(code) - :language: javascript -======================= -Enumerating Collections -======================= - -:Spec: 107 -:Spec-ticket: SPEC-54 -:Title: Enumerating Collections -:Authors: Derick Rethans -:Status: Draft -:Type: Standards -:Server Versions: 1.8-2.7.5, 2.8.0-rc3 and later -:Last Modified: July 2, 2018 -:Version: 0.5 - -.. contents:: - --------- - -Abstract -======== - -A driver can contain a feature to enumerate all collections belonging to a -database. This specification defines how collections should be enumerated. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -Terms ------ - -MongoClient - Driver object representing a connection to MongoDB. This is the root object - of a driver’s API and MAY be named differently in some drivers. - -Iterable - An object or data structure that is a sequence of elements that can be - iterated over. This spec is flexible on what that means as different drivers - will have different requirements, types, and idioms. - -The driver needs to implement different methods of enumeration depending on -MongoDB server version. From MongoDB 2.7.6, the server implements a -``listCollections`` command that MUST be used if available. For the non-MMAP -storage engines that come with 2.8, this will be the only way how their -collections can be enumerated. For earlier MongoDB server versions, the driver -SHOULD simply query against the ``systems.namespaces`` collection. - - -Pre MongoDB 2.7.6 versions --------------------------- - -On MongoDB versions prior to 2.7.6, the only way to enumerate collections for -a database is by querying the ``system.namespaces`` collection:: - - db.system.namespaces.find() - -This query returns a list of documents through a normal query cursor, which -always include a ``name`` field and optionally a ``options`` field. - -Each document describes a namespace. Among collections, these namespaces -also include special collections (containing ``.system.`` or ``.oplog.`` in the -name). The returned documents also include indexes (containing a ``$`` in the -name). Index names MUST NOT be returned while enumerating collections through -``db.system.namespaces.find()``. - -When returning information, drivers MUST strip the database name from the -returned namespace, and only leave the collection name. - -MongoDB versions 2.7.6 to 2.8.0-rc2 ------------------------------------ - -MAY be supported, but this is not necessary. These versions allow the -``listCollections`` command, but return results as a single document, but not -as a cursor. This spec does not cover these versions. - -Post MongoDB 2.8.0-rc3 versions -------------------------------- - -In versions 2.8.0-rc3 and later, the ``listCollections`` command returns a -cursor:: - - $ use test - $ db.runCommand( { listCollections: 1 } ); - -The command also accepts options. - -The ``filter`` option, which acts like a query against the returned collection -documents. You can i.e. use the following to only list the collections -beginning with ``test``:: - - $ db.runCommand( { listCollections: 1, filter: { name: /^test/ } } ); - -Or to find all capped collections:: - - $ db.runCommand( { listCollections: 1, filter: { 'options.capped': true } } ); - -The ``cursor.batchSize`` option, which allows you to set how many initial -collections should be returned as part of the cursor specification document -that comes back from the server. This first batch is part of the returned -structure in the ``firstBatch`` key (see more about return types further on). - -The command returns a cursor definition structure:: - - { - cursor: { - id: , - ns: , - firstBatch: [, , ...] - }, - ok: 1 - } - -With the ``cursor.id`` and ``cursor.ns`` fields you can retrieve further -collection information structures. - -The command also returns the field ``ok`` to -signal whether the command was executed successfully. - -This will return the first 25 collection descriptions as part of the returned -document:: - - $ db.runCommand( { listCollections: 1, cursor : { batchSize: 25 } } ); - - -Filters -------- - -Pre MongoDB 2.7.6 servers, which require querying ``system.namespaces``, -return the name of a collection prefixed with the database name. - -Post MongoDB 2.7.6 servers, which have ``listCollections`` implemented, -return the name of a collection **without** the database name prefixed to it. - -Because of this, drivers MUST prefix filters against the ``name`` field with -the database name for pre MongoDB 2.7.6 servers. Because prefixing causes -issues with regular expressions, a driver MUST NOT allow a regular expression -as the match value for the ``name`` field in the filter for pre MongoDB -2.7.6 drivers. - -For example, to list all collections with a "listCollections" method, you -would do the following with the ``filter`` argument:: - - if filter.name is set: - if server version < 2.7.6 - if typeof filter.name != string - - throw "value type not accepted" (as it needs to be a static string) - - else - filter.name = "dbname." + filter.name - -Filtering against the ``options`` field of a collection has no -restrictions. - -Return types -~~~~~~~~~~~~ - -For servers that support the ``listCollections`` command, the return types -differ depending on server version. Versions 2.7.6 to 2.8.0-rc2 return a single -document containing all the results, but versions 2.8.0-rc3 and later return a -cursor description. The format that is returned is the same as for any other command cursor:: - - { - cursor: { - id: , - ns: , - firstBatch: [, , ...] - }, - ok: 1 - } - -The number of objects in the ``firstBatch`` field depends on the -``cursor.batchSize`` option. - - -Drivers MAY expose methods to return collection names as an array. If your -driver already has such a method, its return type MUST NOT be changed in order -to prevent breaking backwards compatibility. - -Drivers SHOULD expose (a) method(s) to return collection information through a -cursor, where the information for each collection is represented by a single -document. - - -Enumeration: getting collection names -------------------------------------- - -With both the pre and post 2.7.6 versions having a different way to enumerate -all collections, drivers SHOULD implement their enumeration according to the -following algorithm (just like the -`shell does `_):: - - run the listCollections command, with the filter if given - if res.code == 59 || res.code == 13390: - fall back to querying system.namespaces. - - if !res.ok: - if res.errmsg && res.errmsg.startsWith( "no such cmd" ): - fall back to querying system.namespaces. - - else: - throw listCollections command failed exception - - loop through res.cursor.firstBatch - - use getmore with res.cursor.id and res.cursor.ns information to loop over - remaing results - -If you need to fall back to querying ``system.namespaces``, then you need to -filter out all return documents that contain a ``$`` in the ``name`` field. - -When falling back to querying ``system.namespaces`` you will also need to filter -out all the documents that contain ``".oplog.$"`` as this collection is used for -master/slave replication within the local database. - -Alternatively, and if a driver already implements checking MongoDB versions, a -driver MAY alternatively implement it as:: - - if server version >= 2.7.6 - run the listCollections command, with the filter if given - if res.code == 59 || res.code == 13390: - throw listCollections command failed exception - - if res.errmsg && res.errmsg.startsWith( "no such cmd" ): - throw listCollections command failed exception - - loop through res.cursor.firstBatch - - use getmore with res.cursor.id and res.cursor.ns information to loop over - remaing results - - else - fall back to querying system.namespaces. - -Driver methods --------------- - -Drivers SHOULD use the method name ``listCollections`` for a method that -returns all collections with a cursor return type. Drivers MAY use an idiomatic -variant that fits the language the driver is for. - -If a driver already has a method to perform one of the listed tasks, -there is no need to change it. Do not break backwards compatibility when -adding new methods. - -All methods: - -- SHOULD be on the database object. -- MUST allow a filter to be passed to include only requested collections. -- MAY allow the ``cursor.batchSize`` option to be passed. -- MUST use the *same* return type (ie, array or cursor) whether either a - pre-2.7.6 server, a post-2.7.6 or a post-2.8.0-rc3 server is being used. - -Getting Collection Names -~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a MongoClient method that returns an Iterable of strings, -where each string corresponds to a collection name. This method SHOULD be named -``listCollections``. - -MongoDB 4.0 introduced a ``nameOnly`` boolean option to the ``listCollections`` -database command, which limits the command result to only include collection -names. NOTE: ``nameOnly`` is applied before any filter is applied. - -Example return:: - - [ - "me", - "oplog.rs", - "replset.minvalid", - "startup_log", - "system.indexes", - "system.replset" - ] - - -Server version between 2.7.6 (inclusive) and 4.0 (exclusive) do not support -the ``nameOnly`` option for the ``listCollections`` command and will ignore it -without raising an error. Therefore, drivers MUST always specify the ``nameOnly`` -option when they only intend to access collection names from the ``listCollections`` -command result, except drivers MUST NOT set ``nameOnly`` if a filter -specifies any keys other than ``name``. - -Getting Full Collection Information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a method to return the full ``name/options`` pairs that -are returned from both ``listCollections`` (in the ``res.cursor.firstBatch`` -field, and subsequent retrieved documents through getmore on the cursor -constructed from ``res.cursor.ns`` and ``res.cursor.id``), and the query -result for ``system.namespaces``. - -The returned result for each variant MUST be equivalent, and each collection -that is returned MUST use the field names ``name`` and ``options``. - -Example return (a cursor which returns documents, not a simple array):: - - { - "name" : "me", "options" : { "flags" : 1 } - }, - { - "name" : "oplog.rs", "options" : { "capped" : true, "size" : 10485760, "autoIndexId" : false } - }, - { - "name" : "replset.minvalid", "options" : { "flags" : 1 } - }, - { - "name" : "startup_log", "options" : { "capped" : true, "size" : 10485760 } - }, - { - "name" : "system.indexes", "options" : { } - }, - { - "name" : "system.replset", "options" : { "flags" : 1 } - } - -When returning this information as a cursor, a driver SHOULD use the -method name ``listCollections`` or an idiomatic variant. - -Drivers MAY allow ``nameOnly`` option to be passed when executing the ``listCollections`` command for this method. - -Returning a List of Collection Objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a method that returns a collection object for each -returned collection, if the driver has such a concept. - -Example return (in PHP, but abbreviated):: - - array(6) { - [0] => class MongoCollection#6 { } - [1] => class MongoCollection#7 { } - [2] => class MongoCollection#8 { } - [3] => class MongoCollection#9 { } - [4] => class MongoCollection#10 { } - [5] => class MongoCollection#11 { } - } - -Drivers MUST specify the ``nameOnly`` option when executing the ``listCollections`` command for this method, -except drivers MUST NOT set ``nameOnly`` if a filter specifies any keys other than ``name``. - -Replicasets -~~~~~~~~~~~ - -- ``listCollections`` can be run on a secondary -- querying ``system.namespaces`` on a secondary requires slaveOkay to be set. -- Drivers MUST run ``listCollections`` on the primary node in "replicaset" - mode, unless directly connected to a secondary node in "standalone" mode. - - -Test Plan -========= - -Configurations --------------- - -- standalone node -- replica set primary node -- replica set secondary node -- mongos node - -Preparation ------------ - -For each of the configurations: - -- Create a (new) database -- Create a collection and a capped collection -- Create an index on each of the two collections -- Insert at least one document in each of the two collections - -Tests ------ - -- Run the driver's method that returns a list of collection names, and: - - - verify that *all* collection names are represented in the result - - verify that there are no duplicate collection names - - there are no returned collections that do not exist - - there are no returned collections containing an '$' - -- Run the driver's method that returns a list of collection names, pass a - filter of ``{ 'options.capped': true }``, and: - - - verify that *only* names of capped collections are represented in the result - - verify that there are no duplicate collection names - - there are no returned collections that do not exist - - there are no returned collections containing an '$' - - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. This SPEC merely deals -with how to enumerate collections in future versions of MongoDB. - - -Reference Implementation -======================== - -The shell implements the first algorithm for falling back if the -``listCollections`` command does not exist -(``_). - - -Version History -=============== -Version 0.5 Changes - - Clarify that ``nameOnly`` must not be used with filters other than ``name``. - -Version 0.4 Changes - - SPEC-1066: Support ``nameOnly`` option in ``listCollections`` command. - -Version 0.3.1 Changes - - - Fix typos. - - Clarify reason for filtering collection names containing '$'. - -Version 0.3 Changes - - - SPEC-121: Clarify trimming of database name - - Put preferred method name for listing collections with a cursor as return - value. - -Version 0.2 Changes - - - Update with the server change to return a cursor for - ``listCollections``. - -Version 0.1 Changes - - - Initial draft +.. note:: + This specification has been converted to Markdown and renamed to + `enumerate-collections.md `_. diff --git a/source/enumerate-collections/enumerate-collections.md b/source/enumerate-collections/enumerate-collections.md new file mode 100644 index 0000000000..246644a92a --- /dev/null +++ b/source/enumerate-collections/enumerate-collections.md @@ -0,0 +1,324 @@ +# Enumerating Collections + +- Status: Accepted +- Minimum Server Version: 1.8 + +______________________________________________________________________ + +## Abstract + +A driver can contain a feature to enumerate all collections belonging to a database. This specification defines how +collections should be enumerated. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**MongoClient** + +Driver object representing a connection to MongoDB. This is the root object of a driver's API and MAY be named +differently in some drivers. + +**Iterable** + +An object or data structure that is a sequence of elements that can be iterated over. This spec is flexible on what that +means as different drivers will have different requirements, types, and idioms. + +### listCollections Database Command + +The `listCollections` command returns a cursor: + +```javascript +db.runCommand( { listCollections: 1 } ); +``` + +The command also accepts options. + +The `filter` option, which acts like a query against the returned collection documents. You can i.e. use the following +to only list the collections beginning with `test`: + +```javascript +db.runCommand( { listCollections: 1, filter: { name: /^test/ } } ); +``` + +Or to find all capped collections: + +```javascript +db.runCommand( { listCollections: 1, filter: { 'options.capped': true } } ); +``` + +The `cursor.batchSize` option, which allows you to set how many initial collections should be returned as part of the +cursor specification document that comes back from the server. This first batch is part of the returned structure in the +`firstBatch` key (see more about return types further on). + +The command returns a cursor definition structure: + +```javascript +{ + cursor: { + id: , + ns: , + firstBatch: [, , ...] + }, + ok: 1 +} +``` + +With the `cursor.id` and `cursor.ns` fields you can retrieve further collection information structures. + +The command also returns the field `ok` to signal whether the command was executed successfully. + +This will return the first 25 collection descriptions as part of the returned document: + +```javascript +db.runCommand( { listCollections: 1, cursor : { batchSize: 25 } } ); +``` + +MongoDB 4.4 introduced a `comment` option to the `listCollections` database command. This option enables users to +specify a comment as an arbitrary BSON type to help trace the operation through the database profiler, currentOp and +logs. The default is to not send a value. If a comment is provided on pre-4.4 servers, the comment should still be +attached and the driver should rely on the server to provide an error to the user. + +Example of usage of the comment option: + +```javascript +db.runCommand({"listCollections": 1, "comment": "hi there"}) +``` + +Any comment set on a `listCollections` command is inherited by any subsequent `getMore` commands run on the same +`cursor.id` returned from the `listCollections` command. Therefore, drivers MUST NOT attach the comment to subsequent +getMore commands on a cursor. + +#### Return types + +The `listCollections` command returns a cursor description. The format that is returned is the same as for any other +command cursor: + +```javascript +{ + cursor: { + id: , + ns: , + firstBatch: [, , ...] + }, + ok: 1 +} +``` + +The number of objects in the `firstBatch` field depends on the `cursor.batchSize` option. + +Drivers MAY expose methods to return collection names as an array. If your driver already has such a method, its return +type MUST NOT be changed in order to prevent breaking backwards compatibility. + +Drivers SHOULD expose (a) method(s) to return collection information through a cursor, where the information for each +collection is represented by a single document. + +### Driver methods + +Drivers SHOULD use the method name `listCollections` for a method that returns all collections with a cursor return +type. Drivers MAY use an idiomatic variant that fits the language the driver is for. + +If a driver already has a method to perform one of the listed tasks, there is no need to change it. Do not break +backwards compatibility when adding new methods. + +All methods: + +- SHOULD be on the database object. +- MUST allow a filter to be passed to include only requested collections. +- MAY allow the `cursor.batchSize` option to be passed. +- SHOULD allow the `comment` option to be passed. +- MUST apply timeouts per the + [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. + +All methods that return cursors MUST support the timeout options documented in +[Client Side Operations Timeout: Cursors](../client-side-operations-timeout/client-side-operations-timeout.md#cursors). + +#### Getting Collection Names + +Drivers MAY implement a MongoClient method that returns an Iterable of strings, where each string corresponds to a +collection name. This method SHOULD be named `listCollectionNames`. + +MongoDB 4.0 introduced a `nameOnly` boolean option to the `listCollections` database command, which limits the command +result to only include collection names. NOTE: `nameOnly` is applied before any filter is applied. + +Example return: + +```javascript +[ + "me", + "oplog.rs", + "replset.minvalid", + "startup_log", + "system.indexes", + "system.replset" +] +``` + +Server version between 2.7.6 (inclusive) and 4.0 (exclusive) do not support the `nameOnly` option for the +`listCollections` command and will ignore it without raising an error. Therefore, drivers MUST always specify the +`nameOnly` option when they only intend to access collection names from the `listCollections` command result, except +drivers MUST NOT set `nameOnly` if a filter specifies any keys other than `name`. + +MongoDB 4.0 also added an `authorizedCollections` boolean option to the `listCollections` command, which can be used to +limit the command result to only include collections the user is authorized to use. Drivers MAY allow users to set the +`authorizedCollections` option on the `listCollectionNames` method. + +#### Getting Full Collection Information + +Drivers MAY implement a method to return the full `name/options` pairs that are returned from both `listCollections` (in +the `res.cursor.firstBatch` field, and subsequent retrieved documents through getmore on the cursor constructed from +`res.cursor.ns` and `res.cursor.id`), and the query result for `system.namespaces`. + +The returned result for each variant MUST be equivalent, and each collection that is returned MUST use the field names +`name` and `options`. + +In MongoDB 4.4, the `ns` field was removed from the index specifications, so the index specification included in the +`idIndex` field of the collection information will no longer contain an `ns` field. + +- For drivers that report those index specifications in the form of documents or dictionaries, no special handling is + necessary, but any documentation of the contents of the documents/dictionaries MUST indicate that the `ns` field + will no longer be present in MongoDB 4.4+. If the contents of the documents/dictionaries are undocumented, then no + special mention of the `ns` field is necessary. +- For drivers that report those index specifications in the form of statically defined models, the driver MUST manually + populate the `ns` field of the models with the appropriate namespace if the server does not report it in the + `listCollections` command response. The `ns` field is not required to be a part of the models, however. + +Example return (a cursor which returns documents, not a simple array): + +```javascript +{ + "name" : "me", "options" : { "flags" : 1 } +}, +{ + "name" : "oplog.rs", "options" : { "capped" : true, "size" : 10485760, "autoIndexId" : false } +}, +{ + "name" : "replset.minvalid", "options" : { "flags" : 1 } +}, +{ + "name" : "startup_log", "options" : { "capped" : true, "size" : 10485760 } +}, +{ + "name" : "system.indexes", "options" : { } +}, +{ + "name" : "system.replset", "options" : { "flags" : 1 } +} +``` + +When returning this information as a cursor, a driver SHOULD use the method name `listCollections` or an idiomatic +variant. + +Drivers MAY allow the `nameOnly` and `authorizedCollections` options to be passed when executing the `listCollections` +command for this method. + +#### Returning a List of Collection Objects + +Drivers MAY implement a method that returns a collection object for each returned collection, if the driver has such a +concept. This method MAY be named `listMongoCollections`. + +Example return (in PHP, but abbreviated): + +```javascript +array(6) { + [0] => class MongoCollection#6 { } + [1] => class MongoCollection#7 { } + [2] => class MongoCollection#8 { } + [3] => class MongoCollection#9 { } + [4] => class MongoCollection#10 { } + [5] => class MongoCollection#11 { } +} +``` + +Drivers MUST specify true for the `nameOnly` option when executing the `listCollections` command for this method, except +drivers MUST NOT set `nameOnly` if a filter specifies any keys other than `name`. + +Drivers MAY allow the `authorizedCollections` option to be passed when executing the `listCollections` command for this +method + +#### Replica Sets + +The server supports running `listCollections` on a secondary node. Drivers MUST however run `listCollections` on the +primary node when in a replica set topology, unless directly connected to a secondary node in Single topology. + +## Test Plan + +### Configurations + +- standalone node +- replica set primary node +- replica set secondary node +- mongos node + +### Preparation + +For each of the configurations: + +- Create a (new) database +- Create a collection and a capped collection +- Create an index on each of the two collections +- Insert at least one document in each of the two collections + +### Tests + +- Run the driver's method that returns a list of collection names (e.g. `listCollectionNames()`): + - verify that *all* collection names are represented in the result + - verify that there are no duplicate collection names + - there are no returned collections that do not exist + - there are no returned collections containing an '$' +- Run the driver's method that returns a list of collection names (e.g. `listCollectionNames()`), pass a filter of + `{ 'options.capped': true }`, and: + - verify that *only* names of capped collections are represented in the result + - verify that there are no duplicate collection names + - there are no returned collections that do not exist + - there are no returned collections containing an '$' + +## Backwards Compatibility + +There should be no backwards compatibility concerns. This SPEC merely deals with how to enumerate collections in future +versions of MongoDB. + +## Reference Implementation + +The shell implements the first algorithm for falling back if the `listCollections` command does not exist +(). + +## Changelog + +- 2026-05-13: Clarify "Replica Sets" section: reword to distinguish server capability from driver routing rule; remove + stale `system.indexes` bullet (2.x behavior). + +- 2024-07-26: Migrated from reStructuredText to Markdown. Drop description of behavior for MongoDB 2.x servers. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-09-15: Clarify the behavior of `comment` on pre-4.4 servers. + +- 2022-02-01: Add `comment` option to `listCollections` command. + +- 2022-01-20: Require that timeouts be applied per the client-side operations timeout spec. + +- 2021-12-17: Support `authorizedCollections` option in `listCollections` command. + +- 2021-04-22: Update to use secondaryOk. + +- 2020-03-18: MongoDB 4.4 no longer includes `ns` field in `idIndex` field for `listCollections` responses. + +- 2019-03-21: The method that returns a list of collection names should be named `listCollectionNames`. The method that + returns a list of collection objects may be named `listMongoCollections`. + +- 2018-07-03: Clarify that `nameOnly` must not be used with filters other than `name`. + +- 2018-05-18: Support `nameOnly` option in `listCollections` command. + +- 2017-09-27: Clarify reason for filtering collection names containing '$'. + +- 2015-01-14: Clarify trimming of database name. Put preferred method name for listing collections with a cursor as + return value. + +- 2014-12-18: Update with the server change to return a cursor for `listCollections`. diff --git a/source/enumerate-databases.rst b/source/enumerate-databases.rst index 71584556ad..bb371ab331 100644 --- a/source/enumerate-databases.rst +++ b/source/enumerate-databases.rst @@ -1,267 +1,4 @@ -===================== -Enumerating Databases -===================== -:Spec Title: Enumerating Databases -:Spec Ticket: SPEC-865 -:Spec Version: 1.0 -:Author: Jeremy Mikola -:Status: Accepted -:Type: Standards -:Minimum Server Version: 3.6 -:Last Modified: 2018-02-02 - -.. contents:: - --------- - -Abstract -======== - -A driver can provide functionality to enumerate all databases on a server. This -specification defines several methods for enumerating databases. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -Terms ------ - -MongoClient - Driver object representing a connection to MongoDB. This is the root object - of a driver’s API and MAY be named differently in some drivers. - -MongoDatabase - Driver object representing a database and the operations that can be - performed on it. MAY be named differently in some drivers. - -Iterable - An object or data structure that is a sequence of elements that can be - iterated over. This spec is flexible on what that means as different drivers - will have different requirements, types, and idioms. - -Document - An object or data structure used by the driver to represent a BSON document. - This spec is flexible on what that means as different drivers will have - different requirements, types, and idioms. - -Naming Deviations ------------------ - -This specification defines names for methods. To the extent possible, drivers -SHOULD use the defined names. However, where a driver or language’s naming -conventions would conflict, drivers SHOULD honor their existing conventions. For -example, a driver may use ``list_databases`` instead of ``listDatabases``. - -Filters -------- -Drivers SHOULD support the ``filter`` option when implementing the `listDatabases`_ -database command. The ``filter`` option is a query predicate that determines which -databases are listed in the command result. You can specify a condition on any of the -database fields returned in the command output: - -.. _listDatabases: https://docs.mongodb.com/manual/reference/command/listDatabases/ - -- ``name`` -- ``sizeOnDisk`` -- ``empty`` -- ``shards`` - - -For example, to list only databases whose names begin with "foo": - -:: - - > db.adminCommand({listDatabases: 1, filter: {name: /^foo/}}); - -Driver Methods --------------- - -If a driver already has a method to perform one of the listed tasks, there is no -need to change it. Do not break backwards compatibility when adding new methods. - -All methods SHOULD be implemented on the MongoClient object. - -Enumerating Full Database Information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `listDatabases`_ database command returns an array of documents, each of -which contains information about a database on the MongoDB server. Additionally, -the command reports the aggregate sum of all database sizes (in bytes). Consider -the following example: - -.. _listDatabases: https://docs.mongodb.com/manual/reference/command/listDatabases/ - -:: - - > db.getSiblingDB("admin").runCommand({listDatabases:1}) - { - "databases" : [ - { - "name" : "admin", - "sizeOnDisk" : 83886080, - "empty" : false - }, - { - "name" : "local", - "sizeOnDisk" : 83886080, - "empty" : false - } - ], - "totalSize" : 167772160, - "ok" : 1 - } - -Drivers SHOULD implement a MongoClient method that returns an Iterable of -database specifications (e.g. model object, document type), each of which -correspond to an element in the databases array of the ``listDatabases`` command -result. This method SHOULD be named ``listDatabases``. - -Drivers MAY report ``totalSize`` (e.g. through an additional output variable on -the ``listDatabases`` method), but this is not necessary. - -Drivers SHOULD support the ``filter`` option when implementing this method. - -Enumerating Database Names -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -MongoDB 3.6 introduced a ``nameOnly`` boolean option to the ``listDatabases`` -database command, which limits the command result to only include database -names. Consider the following example: - -:: - - > db.getSiblingDB("admin").runCommand({listDatabases:1,nameOnly:true}) - { - "databases" : [ - { "name" : "admin" }, - { "name" : "local" } - ], - "ok" : 1 - } - -Drivers MAY implement a MongoClient method that returns an Iterable of strings, -each of which corresponds to a name in the databases array of the -``listDatabases`` command result. This method SHOULD be named -``listDatabaseNames``. - -Older versions of the server that do not support the ``nameOnly`` option for the -``listDatabases`` command will ignore it without raising an error. Therefore, -drivers SHOULD always specify the ``nameOnly`` option when they only intend to -access database names from the ``listDatabases`` command result. - -Drivers SHOULD support the ``filter`` option when implementing this method. - -Enumerating MongoDatabase Objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a MongoClient method that returns an Iterable of -MongoDatabase types, each of which corresponds to a name in the databases array -of the ``listDatabases`` command result. This method MAY be named -``listMongoDatabases``. - -Any MongoDatabase objects returned by this method SHOULD inherit the same -MongoClient options that would otherwise be inherited by selecting an individual -MongoDatabase through MongoClient (e.g. read preference, write concern). - -Drivers SHOULD specify the ``nameOnly`` option when executing the -``listDatabases`` command for this method. - -Drivers SHOULD support the ``filter`` option when implementing this method. - -Replica Sets ------------- - -The ``listDatabases`` command may be run on a secondary. Drivers MUST run the -``listDatabases`` command only on the primary node in “replicaset” mode, unless -directly connected to a secondary node in “standalone” mode. - -Test Plan -========= - -Test Environments ------------------ - -The test plan should be executed against the following servers: - -* Standalone -* Replica set primary -* Replica set secondary -* Sharding router (i.e. mongos) - -Test Cases ----------- - -The following scenarios should be run for each test environment: - -* Execute the method to enumerate full database information (e.g. - ``listDatabases()``) - - Verify that the method returns an Iterable of Document types - - Verify that all databases on the server are present in the result set - - Verify that the result set does not contain duplicates -* Execute the method to enumerate database names (e.g. ``listDatabaseNames()``) - - Verify that the method returns an Iterable of strings - - Verify that all databases on the server are present in the result set - - Verify that the result set does not contain duplicates -* Execute the method to enumerate MongoDatabase objects (e.g. - ``listMongoDatabases()``) - - Verify that the method returns an Iterable of MongoDatabase objects - - Verify that all databases on the server are present in the result set - - Verify that the result set does not contain duplicates - -Motivation for Change -===================== - -Although most drivers provide a ``listDatabases`` command helper in their API, -there was previously no spec for a database enumeration. MongoDB 3.6 introduced -a ``nameOnly`` option to the ``listDatabases`` database command. The driver API -should to be expanded to support this option. - -Design Rationale -================ - -The design of this specification is inspired by the `Collection Enumeration`_ -and `Index Enumeration`_ specifications. Since most drivers already implement a -``listDatabases`` command helper in some fashion, this spec is flexible when it -comes to existing APIs. - -.. _Collection Enumeration: ./enumerate-collections.rst -.. _Index Enumeration: ./enumerate-indexes.rst - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. This specification merely -deals with how to enumerate databases in future versions of MongoDB and allows -flexibility for existing driver APIs. - -Reference Implementation -======================== - -TBD - -Q & A -===== - -Why is reporting the total size of all databases optional? ----------------------------------------------------------- - -Although the ``listDatabases`` command provides two results, a ``databases`` -array and ``totalSize`` integer, the array of database information documents is -the primary result. Returning a tuple or composite result type from a -``listDatabases`` driver method would complicate the general use case, as -opposed to an optional output argument (if supported by the language). -Furthermore, the ``totalSize`` value can be calculated client-side by summing -all ``sizeOnDisk`` fields in the array of database information documents. - -Changes -======= - -2017-10-30 Support filter option in listDatabases command +.. note:: + This specification has been converted to Markdown and renamed to + `enumerate-databases.md `_. diff --git a/source/enumerate-databases/enumerate-databases.md b/source/enumerate-databases/enumerate-databases.md new file mode 100644 index 0000000000..b8e9948c88 --- /dev/null +++ b/source/enumerate-databases/enumerate-databases.md @@ -0,0 +1,252 @@ +# Enumerating Databases + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +A driver can provide functionality to enumerate all databases on a server. This specification defines several methods +for enumerating databases. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**MongoClient** + +Driver object representing a connection to MongoDB. This is the root object of a driver's API and MAY be named +differently in some drivers. + +**MongoDatabase** + +Driver object representing a database and the operations that can be performed on it. MAY be named differently in some +drivers. + +**Iterable** + +An object or data structure that is a sequence of elements that can be iterated over. This spec is flexible on what that +means as different drivers will have different requirements, types, and idioms. + +**Document** + +An object or data structure used by the driver to represent a BSON document. This spec is flexible on what that means as +different drivers will have different requirements, types, and idioms. + +### Naming Deviations + +This specification defines names for methods. To the extent possible, drivers SHOULD use the defined names. However, +where a driver or language's naming conventions would conflict, drivers SHOULD honor their existing conventions. For +example, a driver may use `list_databases` instead of `listDatabases`. + +### Filters + +Drivers SHOULD support the `filter` option when implementing the +[listDatabases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) database command. The `filter` +option is a query predicate that determines which databases are listed in the command result. You can specify a +condition on any of the database fields returned in the command output: + +- `name` +- `sizeOnDisk` +- `empty` +- `shards` + +For example, to list only databases whose names begin with "foo": + +```javascript +db.adminCommand({listDatabases: 1, filter: {name: /^foo/}}); +``` + +### AuthorizedDatabases + +MongoDB 4.0.5 added an `authorizedDatabases` boolean option to the +[listDatabases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) database command, which can be +used to limit the command result to only include databases the user is authorized to use. Drivers SHOULD support the new +`authorizedDatabases` option when implementing the +[listDatabases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) database command. + +The possible values for `authorizedDatabases` are: + +- unspecified (missing entirely from the command document sent to the server) +- `false` +- `true` + +See the server's [listDatabases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) documentation for +an explanation of what each value means. + +### Comment + +MongoDB 4.4 introduced a `comment` option to the `listDatabases` command. This option enables users to specify a comment +as an arbitrary BSON type to help trace the operation through the database profiler, currentOp and logs. The default is +to not send a value. If a comment is provided on pre-4.4 servers, the comment should still be attached and the driver +should rely on the server to provide an error to the user. + +```javascript +db.getSiblingDB("admin").runCommand({listDatabases: 1, comment: "hi there"}) +``` + +### Driver Methods + +If a driver already has a method to perform one of the listed tasks, there is no need to change it. Do not break +backwards compatibility when adding new methods. + +All methods SHOULD be implemented on the MongoClient object. + +All methods MUST apply timeouts per the +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. + +#### Enumerating Full Database Information + +The [listDatabases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) database command returns an +array of documents, each of which contains information about a database on the MongoDB server. Additionally, the command +reports the aggregate sum of all database sizes (in bytes). Consider the following example: + +```javascript +db.getSiblingDB("admin").runCommand({listDatabases:1}) + { + "databases" : [ + { + "name" : "admin", + "sizeOnDisk" : 83886080, + "empty" : false + }, + { + "name" : "local", + "sizeOnDisk" : 83886080, + "empty" : false + } + ], + "totalSize" : 167772160, + "ok" : 1 + } +``` + +Drivers SHOULD implement a MongoClient method that returns an Iterable of database specifications (e.g. model object, +document type), each of which correspond to an element in the databases array of the `listDatabases` command result. +This method SHOULD be named `listDatabases`. + +Drivers MAY report `totalSize` (e.g. through an additional output variable on the `listDatabases` method), but this is +not necessary. + +Drivers SHOULD support the `filter`, `authorizedDatabases` and `comment` options when implementing this method. + +#### Enumerating Database Names + +MongoDB 3.6 introduced a `nameOnly` boolean option to the `listDatabases` database command, which limits the command +result to only include database names. Consider the following example: + +```javascript +> db.getSiblingDB("admin").runCommand({listDatabases:1,nameOnly:true}) +{ + "databases" : [ + { "name" : "admin" }, + { "name" : "local" } + ], + "ok" : 1 +} +``` + +Drivers MAY implement a MongoClient method that returns an Iterable of strings, each of which corresponds to a name in +the databases array of the `listDatabases` command result. This method SHOULD be named `listDatabaseNames`. + +Drivers SHOULD support the `filter`, `authorizedDatabases` and `comment` options when implementing this method. + +#### Enumerating MongoDatabase Objects + +Drivers MAY implement a MongoClient method that returns an Iterable of MongoDatabase types, each of which corresponds to +a name in the databases array of the `listDatabases` command result. This method MAY be named `listMongoDatabases`. + +Any MongoDatabase objects returned by this method SHOULD inherit the same MongoClient options that would otherwise be +inherited by selecting an individual MongoDatabase through MongoClient (e.g. read preference, write concern). + +Drivers SHOULD specify the `nameOnly` option when executing the `listDatabases` command for this method. + +Drivers SHOULD support the `filter`, `authorizedDatabases` and `comment` options when implementing this method. + +### Replica Sets + +The `listDatabases` command may be run on a secondary node. Drivers MUST run the `listDatabases` command only on the +primary node in replica set topology, unless directly connected to a secondary node in Single topology. + +## Test Plan + +### Test Environments + +The test plan should be executed against the following servers: + +- Standalone +- Replica set primary +- Replica set secondary +- Sharding router (i.e. mongos) + +### Test Cases + +The following scenarios should be run for each test environment: + +- Execute the method to enumerate full database information (e.g. `listDatabases()`) + - Verify that the method returns an Iterable of Document types + - Verify that all databases on the server are present in the result set + - Verify that the result set does not contain duplicates +- Execute the method to enumerate database names (e.g. `listDatabaseNames()`) + - Verify that the method returns an Iterable of strings + - Verify that all databases on the server are present in the result set + - Verify that the result set does not contain duplicates +- Execute the method to enumerate MongoDatabase objects (e.g. `listMongoDatabases()`) + - Verify that the method returns an Iterable of MongoDatabase objects + - Verify that all databases on the server are present in the result set + - Verify that the result set does not contain duplicates + +## Motivation for Change + +Although most drivers provide a `listDatabases` command helper in their API, there was previously no spec for a database +enumeration. MongoDB 3.6 introduced a `nameOnly` option to the `listDatabases` database command. The driver API should +to be expanded to support this option. + +## Design Rationale + +The design of this specification is inspired by the +[Collection Enumeration](../enumerate-collections/enumerate-collections.md) and +[Index Management](../index-management/index-management.md) specifications. Since most drivers already implement a +`listDatabases` command helper in some fashion, this spec is flexible when it comes to existing APIs. + +## Backwards Compatibility + +There should be no backwards compatibility concerns. This specification merely deals with how to enumerate databases in +future versions of MongoDB and allows flexibility for existing driver APIs. + +## Reference Implementation + +TBD + +## Q & A + +### Why is reporting the total size of all databases optional? + +Although the `listDatabases` command provides two results, a `databases` array and `totalSize` integer, the array of +database information documents is the primary result. Returning a tuple or composite result type from a `listDatabases` +driver method would complicate the general use case, as opposed to an optional output argument (if supported by the +language). Furthermore, the `totalSize` value can be calculated client-side by summing all `sizeOnDisk` fields in the +array of database information documents. + +## Changelog + +- 2024-07-26: Migrated from reStructuredText to Markdown. Removed note that applied to pre-3.6 servers. + +- 2022-10-05: Remove spec front matter and reformat changelog. Also reverts the minimum server version to 3.6, which is + where `nameOnly` and `filter` options were first introduced for `listDatabases`. + +- 2022-08-17: Clarify the behavior of comment on pre-4.4 servers. + +- 2022-02-01: Support comment option in listDatabases command + +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec. + +- 2019-11-20: Support authorizedDatabases option in listDatabases command + +- 2017-10-30: Support filter option in listDatabases command diff --git a/source/enumerate-indexes.rst b/source/enumerate-indexes.rst deleted file mode 100644 index 643d7db594..0000000000 --- a/source/enumerate-indexes.rst +++ /dev/null @@ -1,398 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -=================== -Enumerating Indexes -=================== - -:Spec: 106 -:Spec-ticket: SPEC-53 -:Title: Enumerating Indexes -:Authors: Derick Rethans -:Status: Draft -:Type: Standards -:Server Versions: 1.8-2.7.5, 2.8.0-rc3 and later -:Last Modified: 2018-04-05 -:Version: 0.4.1 - -.. contents:: - --------- - -Abstract -======== - -A driver can contain a feature to enumerate all indexes belonging to a -collection. This specification defines how indexes should be enumerated. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -The driver needs to implement different methods of enumeration depending on -MongoDB server version. From MongoDB 2.7.6, the server implements a -``listIndexes`` command that MUST be used if available. For the non-MMAP -storage engines that come with 2.8, this will be the only way how their indexes -can be enumerated. For earlier MongoDB server versions, the driver SHOULD -simply query against the ``system.indexes`` collection of a database and only -include indexes for the collection that the method is called on (by specifying -``ns: 'dbname.collection'`` when querying against ``system.indexes``). - - -Pre MongoDB 2.7.6 versions --------------------------- - -On MongoDB versions prior to 2.7.6, the only way to enumerate indexes for -a collection is by querying the ``system.indexes`` collection:: - - $ use test - $ db.system.indexes.find( { 'ns' : 'test.collectionName' } ) - -This query returns a list of documents which always include a ``v`` field, a -``key`` field, a ``name`` field and an ``ns`` field. - -Each returned document describes one index:: - - { - "v" : 1, - "key" : { - "l" : "2dsphere" - }, - "name" : "l_2dsphere", - "ns" : "test.collectionName", - "2dsphereIndexVersion" : 2 - } - -MongoDB versions 2.7.6 to 2.8.0-rc2 ------------------------------------ - -MAY be supported, but this is not necessary. These versions allow the -``listIndexes`` command, but return results as a single document, but not -as a cursor. This spec does not cover these versions. - - -Post MongoDB 2.8.0-rc3 versions -------------------------------- - -In versions 2.8.0-rc3 and later, the ``listIndexes`` command returns a -cursor:: - - $ use test - $ db.runCommand( { listIndexes: 'collectionName' } ); - -The value for the ``listIndexes`` is the collection name. Unlike pre-2.7.6 -versions, this is *not* the full namespace name. - -The command returns a cursor definition structure:: - - { - cursor: { - id: , - ns: , - firstBatch: [, , ...] - }, - ok: 1 - } - -The ``cursor.firstBatch`` field contains an initial list of index information -structures, for example:: - - { - cursor: { - id: , - ns: , - firstBatch: [ - { - "v" : 1, - "key" : { - "x" : 1 - }, - "name" : "x_1", - "ns" : "test.collectionName", - }, - { - "v" : 1, - "key" : { - "l" : "2dsphere" - }, - "name" : "l_2dsphere", - "ns" : "test.collectionName", - "2dsphereIndexVersion" : 2 - }, - ] - }, - ok: 1 - } - -With the ``cursor.id`` and ``cursor.ns`` fields you can retrieve further index -information structures. - -The command also returns the field ``ok`` to -signal whether the command was executed successfully. - -This will return the first 25 index descriptions as part of the returned -document:: - - $ db.runCommand( { listIndexes: 'collectionName', cursor : { batchSize: 25 } } ); - -Return types -~~~~~~~~~~~~ - -For servers that support the ``listIndexes`` command, the return types -differ depending on server version. Versions 2.7.6 to 2.8.0-rc2 return a single -document containing all the results, but versions 2.8.0-rc3 and later return a -cursor description. As per the draft specification__, the format that is -returned is the same as for any other command cursor:: - - { - cursor: { - id: , - ns: , - firstBatch: [, , ...] - }, - ok: 1 - } - -The number of objects in the ``firstBatch`` field depends on the -``cursor.batchSize`` option. - -__ https://docs.google.com/a/10gen.com/document/d/1u6UuUvInvnHeAAeVNfTDBD6rtn_lkb0kCDn6ejK6hDk/edit - -Drivers MAY expose methods to return index information structures as an array. -If your driver already has such a method, its return type MUST NOT be changed -in order to prevent breaking backwards compatibility. - -Drivers SHOULD expose (a) method(s) to return index information through a -cursor, where the information for each index is represented by a single -document. - - -Enumeration: getting index information --------------------------------------- - -With both the pre and post 2.7.6 versions having a different way to enumerate -indexes for a collection, drivers SHOULD implement their enumeration according to the -following algorithm (just like the -`shell does `_):: - - run the listIndexes command while specifying the collection name - if res.code == 59 || res.code == 13390: - fall back to querying system.indexes. - - if res.code == 26 - return an empty array - - if !res.ok: - if res.errmsg && res.errmsg.startsWith( "no such cmd" ): - fall back to querying system.indexes. - - else: - throw listIndexes command failed exception - - loop through res.cursor.firstBatch - - use getmore with res.cursor.id and res.cursor.ns information to loop over - remaing results - -If you need to fall back to querying ``system.indexes``, then you need -specify the full namespaces name (``dbName.collectionName``) as query criterion. - -Alternatively, and if a driver already implements checking MongoDB versions, a -driver MAY alternatively implement it as:: - - if server version >= 2.7.6 - run the listIndexes command, with the given collection name - if res.code == 26 - return an empty array - - if res.code == 59 || res.code == 13390: - throw listIndexes command failed exception - - if res.errmsg && res.errmsg.startsWith( "no such cmd" ): - throw listIndexes command failed exception - - loop through res.cursor.firstBatch - - use getmore with res.cursor.id and res.cursor.ns information to loop over - remaing results - - else - fall back to querying system.indexes, and return each returned document - as an element - -Error code 26 indicates that the collection does not exist, and error codes 59 -and 13390 are used to indicate that the command being called (``listIndexes`` -in this case) does not exist. - -A Client MUST handle error codes 59 and 13390 as shown in the algorithms -above. - -A Client MAY bubble up an exception if code 26 is encountered, or it MAY -swallow the exception to preserve backwards compatibility with an existing -API. - -Driver methods --------------- - -Drivers SHOULD use the method name ``listIndexes`` for a method that returns -all indexes with a cursor return type. Drivers MAY use an idiomatic variant -that fits the language the driver is for. - -If a driver already has a method to perform one of the listed tasks, there is -no need to change a method name. Do not break backwards compatibility when -adding new methods. - -All methods: - -- MUST be on the collection object. -- MAY allow the ``cursor.batchSize`` option to be passed. -- MUST use the *same* return type (ie, array or cursor) whether either a - pre-2.7.6 server, a post-2.7.6 or a post-2.8.0-rc3 server is being used. -- MAY emulate returning a cursor for pre-2.8.0-rc3 servers. - -Getting Index Names -~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a method to enumerate all indexes, and return only -the index names. - -Example:: - - > a = []; - [ ] - > db.runCommand( { listIndexes: 'poiConcat' } ).indexes.forEach(function(i) { a.push(i.name); } ); - > a - [ "_id_", "ty_1", "l_2dsphere", "ts_1" ] - -Getting Full Index Information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MAY implement a method to return the full index specifications that are -returned from both ``listIndexes`` (in the ``res.cursor.firstBatch`` field, and -subsequent retrieved documents through getmore on the cursor constructed from -``res.cursor.ns`` and ``res.cursor.id``), and the query result for -``system.indexes``. - -The returned result for each variant MUST be equivalent, and each index -that is returned MUST use the field names ``v``, ``key``, ``name`` and ``ns``. -If the result contains additional fields, they MUST be included verbatim (like -``2dsphereIndexVersion`` in the example below). - -Example return (a cursor which returns documents, not a simple array):: - - { - "v" : 1, - "key" : { "_id" : 1 }, - "name" : "_id_", - "ns" : "demo.poiConcat" - }, - { - "v" : 1, - "key" : { "ty" : NumberLong(1) }, - "name" : "ty_1", - "ns" : "demo.poiConcat" - }, - { - "v" : 1, - "key" : { "l" : "2dsphere" }, - "name" : "l_2dsphere", - "ns" : "demo.poiConcat", - "2dsphereIndexVersion" : 2 - }, - { - "v" : 1, - "key" : { "ts" : NumberLong(1) }, - "name" : "ts_1", - "ns" : "demo.poiConcat" - } - -When returning this information as a cursor, a driver SHOULD use the -method name ``listIndexes`` or an idiomatic variant. - -Replicasets -~~~~~~~~~~~ - -- ``listIndexes`` can be run on a secondary -- querying ``system.indexes`` on a secondary requires slaveOkay to be set. -- Drivers MUST run ``listIndexes`` on the primary node in "replicaset" - mode, unless directly connected to a secondary node in "standalone" mode. - - -Test Plan -========= - -Configurations --------------- - -- standalone node -- replica set primary node -- replica set secondary node -- mongos node - -Preparation ------------ - -For each of the configurations: - -- Create a (new) database -- Create a collection -- Create a single column index, a compound index, and a unique index -- Insert at least one document containing all the fields that the above - indicated indexes act on - -Tests ------ - -- Run the driver's method that returns a list of index names, and: - - - verify that *all* index names are represented in the result - - verify that there are no duplicate index names - - there are no returned indexes that do not exist - -- Run the driver's method that returns a list of index information records, and: - - - all the indexes are represented in the result - - the "unique" flags show up for the unique index - - there are no duplicates in the returned list - - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. This SPEC merely deals -with how to enumerate indexes in future versions of MongoDB. - - -Reference Implementation -======================== - -The shell implements the first algorithm for falling back if the -``listIndexes`` command does not exist -(``_). - - -Version History -=============== - -0.4.1 - 2018-04-05 - Fix typo. - -0.4 - 2017-09-20 - Allow more leniency for handling error code 26 (collection does not - exist). - -0.3 - 2015-01-14 - Put preferred method name for listing indexes with a cursor as return - value. - -0.2 - Update with the server change to return a cursor for ``listIndexes``. - -0.1 - Initial draft diff --git a/source/etc/generate-backpressure-retryability-tests.py b/source/etc/generate-backpressure-retryability-tests.py new file mode 100644 index 0000000000..342ff80f3e --- /dev/null +++ b/source/etc/generate-backpressure-retryability-tests.py @@ -0,0 +1,125 @@ +from collections import namedtuple +from jinja2 import Template +import os + +Operation = namedtuple( + 'Operation', ['operation_name', 'command_name', 'object', 'arguments', 'operation_type']) + +CLIENT_BULK_WRITE_ARGUMENTS = '''models: + - insertOne: + namespace: *client_bulk_write_ns + document: { _id: 8, x: 88 }''' + +CLIENT_OPERATIONS = [ + Operation('listDatabases', 'listDatabases', 'client', ['filter: {}'], 'read'), + Operation('listDatabaseNames', 'listDatabases', 'client', [], 'read'), + Operation('createChangeStream', 'aggregate', 'client', ['pipeline: []'], 'read'), + Operation('clientBulkWrite', 'bulkWrite', 'client', [CLIENT_BULK_WRITE_ARGUMENTS], 'write') +] + +RUN_COMMAND_ARGUMENTS = '''command: { ping: 1 } + commandName: ping''' + +DB_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'database', [ + 'pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ]'], 'read'), + Operation('listCollections', 'listCollections', + 'database', ['filter: {}'], 'read'), + Operation('listCollectionNames', 'listCollections', + 'database', ['filter: {}'], 'read'), # Optional. + Operation('runCommand', 'ping', 'database', [RUN_COMMAND_ARGUMENTS], 'read'), + Operation('createChangeStream', 'aggregate', 'database', ['pipeline: []'], 'read') +] + +INSERT_MANY_ARGUMENTS = '''documents: + - { _id: 2, x: 22 }''' + +BULK_WRITE_ARGUMENTS = '''requests: + - insertOne: + document: { _id: 2, x: 22 }''' + +COLLECTION_READ_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'collection', ['pipeline: []'], 'read'), + # Operation('count', 'count', 'collection', ['filter: {}'], 'read'), # Deprecated. + Operation('countDocuments', 'aggregate', 'collection', ['filter: {}'], 'read'), + Operation('estimatedDocumentCount', 'count', 'collection', [], 'read'), + Operation('distinct', 'distinct', 'collection', + ['fieldName: x', 'filter: {}'], 'read'), + Operation('find', 'find', 'collection', ['filter: {}'], 'read'), + Operation('findOne', 'find', 'collection', ['filter: {}'], 'read'), # Optional. + Operation('listIndexes', 'listIndexes', 'collection', [], 'read'), + Operation('listIndexNames', 'listIndexes', 'collection', [], 'read'), # Optional. + Operation('createChangeStream', 'aggregate', + 'collection', ['pipeline: []'], 'read'), +] + +COLLECTION_WRITE_OPERATIONS = [ + Operation('insertOne', 'insert', 'collection', + ['document: { _id: 2, x: 22 }'], 'write'), + Operation('insertMany', 'insert', 'collection', [INSERT_MANY_ARGUMENTS], 'write'), + Operation('deleteOne', 'delete', 'collection', ['filter: {}'], 'write'), + Operation('deleteMany', 'delete', 'collection', ['filter: {}'], 'write'), + Operation('replaceOne', 'update', 'collection', [ + 'filter: {}', 'replacement: { x: 22 }'], 'write'), + Operation('updateOne', 'update', 'collection', [ + 'filter: {}', 'update: { $set: { x: 22 } }'], 'write'), + Operation('updateMany', 'update', 'collection', [ + 'filter: {}', 'update: { $set: { x: 22 } }'], 'write'), + Operation('findOneAndDelete', 'findAndModify', + 'collection', ['filter: {}'], 'write'), + Operation('findOneAndReplace', 'findAndModify', 'collection', + ['filter: {}', 'replacement: { x: 22 }'], 'write'), + Operation('findOneAndUpdate', 'findAndModify', 'collection', + ['filter: {}', 'update: { $set: { x: 22 } }'], 'write'), + Operation('bulkWrite', 'insert', 'collection', [BULK_WRITE_ARGUMENTS], 'write'), + Operation('createIndex', 'createIndexes', 'collection', + ['keys: { x: 11 }', 'name: "x_11"'], 'write'), + Operation('dropIndex', 'dropIndexes', 'collection', ['name: "x_11"'], 'write'), + Operation('dropIndexes', 'dropIndexes', 'collection', [], 'write'), + Operation('aggregate', 'aggregate', 'collection', ['pipeline: [{$out: "output"}]'], 'write'), +] + +COLLECTION_OPERATIONS = COLLECTION_READ_OPERATIONS + COLLECTION_WRITE_OPERATIONS + +# Session and GridFS operations are generally tested in other files, so they're not included in the list of all +# operations. Individual generation functions can choose to include them if needed. +OPERATIONS = CLIENT_OPERATIONS + DB_OPERATIONS + COLLECTION_OPERATIONS + +# ./source/etc +DIR = os.path.dirname(os.path.realpath(__file__)) + + +def get_template(file, templates_dir): + path = f'{templates_dir}/{file}.yml.template' + return Template(open(path, 'r').read()) + + +def write_yaml(file, template, tests_dir, injections): + rendered = template.render(**injections) + path = f'{tests_dir}/{file}.yml' + open(path, 'w').write(rendered) + + +def generate(name, templates_dir, tests_dir, operations): + template = get_template(name, templates_dir) + injections = { + 'operations': operations, + } + write_yaml(name, template, tests_dir, injections) + + +def generate_retry_loop_tests(): + templates_dir = f'{os.path.dirname(DIR)}/client-backpressure/tests' + tests_dir = f'{os.path.dirname(DIR)}/client-backpressure/tests' + generate('backpressure-retry-loop', templates_dir, + tests_dir, OPERATIONS) + + +def generate_max_attempts_tests(): + templates_dir = f'{os.path.dirname(DIR)}/client-backpressure/tests' + tests_dir = f'{os.path.dirname(DIR)}/client-backpressure/tests' + generate('backpressure-retry-max-attempts', templates_dir, + tests_dir, OPERATIONS) + +generate_retry_loop_tests() +generate_max_attempts_tests() diff --git a/source/etc/generate-handshakeError-tests.py b/source/etc/generate-handshakeError-tests.py new file mode 100644 index 0000000000..a07a9df833 --- /dev/null +++ b/source/etc/generate-handshakeError-tests.py @@ -0,0 +1,163 @@ +from collections import namedtuple +from jinja2 import Template +import os +import sys + +Operation = namedtuple( + 'Operation', ['operation_name', 'command_name', 'object', 'arguments']) + +CLIENT_BULK_WRITE_ARGUMENTS = '''models: + - insertOne: + namespace: retryable-writes-handshake-tests.coll + document: { _id: 8, x: 88 }''' + +CLIENT_OPERATIONS = [ + Operation('listDatabases', 'listDatabases', 'client', ['filter: {}']), + Operation('listDatabaseNames', 'listDatabases', 'client', []), + Operation('createChangeStream', 'aggregate', 'client', ['pipeline: []']), + Operation('clientBulkWrite', 'bulkWrite', 'client', [CLIENT_BULK_WRITE_ARGUMENTS]) +] + +RUN_COMMAND_ARGUMENTS = '''command: { ping: 1 } + commandName: ping''' + +DB_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'database', [ + 'pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ]']), + Operation('listCollections', 'listCollections', + 'database', ['filter: {}']), + Operation('listCollectionNames', 'listCollections', + 'database', ['filter: {}']), # Optional. + Operation('runCommand', 'ping', 'database', [RUN_COMMAND_ARGUMENTS]), + Operation('createChangeStream', 'aggregate', 'database', ['pipeline: []']) +] + +INSERT_MANY_ARGUMENTS = '''documents: + - { _id: 2, x: 22 }''' + +BULK_WRITE_ARGUMENTS = '''requests: + - insertOne: + document: { _id: 2, x: 22 }''' + +COLLECTION_READ_OPERATIONS = [ + Operation('aggregate', 'aggregate', 'collection', ['pipeline: []']), + # Operation('count', 'count', 'collection', ['filter: {}']), # Deprecated. + Operation('countDocuments', 'aggregate', 'collection', ['filter: {}']), + Operation('estimatedDocumentCount', 'count', 'collection', []), + Operation('distinct', 'distinct', 'collection', + ['fieldName: x', 'filter: {}']), + Operation('find', 'find', 'collection', ['filter: {}']), + Operation('findOne', 'find', 'collection', ['filter: {}']), # Optional. + Operation('listIndexes', 'listIndexes', 'collection', []), + Operation('listIndexNames', 'listIndexes', 'collection', []), # Optional. + Operation('createChangeStream', 'aggregate', + 'collection', ['pipeline: []']), +] + +COLLECTION_WRITE_OPERATIONS = [ + Operation('insertOne', 'insert', 'collection', + ['document: { _id: 2, x: 22 }']), + Operation('insertMany', 'insert', 'collection', [INSERT_MANY_ARGUMENTS]), + Operation('deleteOne', 'delete', 'collection', ['filter: {}']), + Operation('deleteMany', 'delete', 'collection', ['filter: {}']), + Operation('replaceOne', 'update', 'collection', [ + 'filter: {}', 'replacement: { x: 22 }']), + Operation('updateOne', 'update', 'collection', [ + 'filter: {}', 'update: { $set: { x: 22 } }']), + Operation('updateMany', 'update', 'collection', [ + 'filter: {}', 'update: { $set: { x: 22 } }']), + Operation('findOneAndDelete', 'findAndModify', + 'collection', ['filter: {}']), + Operation('findOneAndReplace', 'findAndModify', 'collection', + ['filter: {}', 'replacement: { x: 22 }']), + Operation('findOneAndUpdate', 'findAndModify', 'collection', + ['filter: {}', 'update: { $set: { x: 22 } }']), + Operation('bulkWrite', 'insert', 'collection', [BULK_WRITE_ARGUMENTS]), + Operation('createIndex', 'createIndexes', 'collection', + ['keys: { x: 11 }', 'name: "x_11"']), + Operation('dropIndex', 'dropIndexes', 'collection', ['name: "x_11"']), + Operation('dropIndexes', 'dropIndexes', 'collection', []), +] + +COLLECTION_OPERATIONS = COLLECTION_READ_OPERATIONS + COLLECTION_WRITE_OPERATIONS + +# Session and GridFS operations are generally tested in other files, so they're not included in the list of all +# operations. Individual generation functions can choose to include them if needed. +OPERATIONS = CLIENT_OPERATIONS + DB_OPERATIONS + COLLECTION_OPERATIONS + +RETRYABLE_READ_OPERATIONS = [op for op in OPERATIONS if op.operation_name in + ['find', + 'findOne', + 'aggregate', + 'distinct', + 'count', + 'estimatedDocumentCount', + 'countDocuments', + 'createChangeStream', + 'listDatabases', + 'listDatabaseNames', + 'listCollections', + 'listCollectionNames', + 'listIndexes', + 'listIndexNames', + ] + ] + +RETRYABLE_WRITE_OPERATIONS = [op for op in OPERATIONS if op.operation_name in + ['insertOne', + 'updateOne', + 'deleteOne', + 'replaceOne', + 'findOneAndDelete', + 'findOneAndUpdate', + 'findOneAndReplace', + 'insertMany', + 'bulkWrite', + 'clientBulkWrite' + ] + ] + + +# ./source/etc +DIR = os.path.dirname(os.path.realpath(__file__)) + + +def get_template(file, templates_dir): + path = f'{templates_dir}/{file}.yml.template' + return Template(open(path, 'r').read()) + + +def write_yaml(file, template, tests_dir, injections): + rendered = template.render(**injections) + path = f'{tests_dir}/{file}.yml' + open(path, 'w').write(rendered) + + +def generate(name, templates_dir, tests_dir, operations): + template = get_template(name, templates_dir) + injections = { + 'operations': operations, + } + write_yaml(name, template, tests_dir, injections) + + +def generate_retryable_reads_handshake_error_tests(): + # ./source/retryable-reads/tests/etc/templates + templates_dir = f'{os.path.dirname(DIR)}/retryable-reads/tests/etc/templates' + # ./source/retryable-reads/tests/unified + tests_dir = f'{os.path.dirname(DIR)}/retryable-reads/tests/unified' + generate('handshakeError', templates_dir, + tests_dir, RETRYABLE_READ_OPERATIONS) + + +def generate_retryable_writes_handshake_error_tests(): + # ./source/retryable-writes/tests/etc/templates + templates_dir = f'{os.path.dirname(DIR)}/retryable-writes/tests/etc/templates' + # ./source/retryable-writes/tests/unified + tests_dir = f'{os.path.dirname(DIR)}/retryable-writes/tests/unified' + generate('handshakeError', templates_dir, + tests_dir, RETRYABLE_WRITE_OPERATIONS) + + +generate_retryable_reads_handshake_error_tests() +generate_retryable_writes_handshake_error_tests() diff --git a/source/extended-json.rst b/source/extended-json.rst index acda38a8ce..b0bb3b397b 100644 --- a/source/extended-json.rst +++ b/source/extended-json.rst @@ -1,917 +1,4 @@ -============= -Extended JSON -============= -:Spec: 587 -:Spec-ticket: SPEC-587 -:Title: Extended JSON -:Author: Luke Lovett, David Golden -:Spec Lead: David Golden, Jeff Yemin -:Advisory Group: Jeff Yemin, Christian Kvalheim, Hannes Magnusson, Matt Broadstone, Jesse Davis -:Status: Proposed -:Type: Standards -:Last Modified: July 20, 2017 -:Version: 2.0.0 - -.. contents:: - --------- - -Abstract -======== - -MongoDB Extended JSON is a string format for representing BSON documents. This -specification defines the canonical format for representing each BSON type in -the Extended JSON format. Thus, a tool that implements Extended JSON will be -able to parse the output of any tool that emits Canonical Extended JSON. It also -defines a Relaxed Extended JSON format that improves readability at the expense -of type information preservation. - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - -Naming ------- - -Acceptable naming deviations should fall within the basic style of the -language. For example, ``CanonicalExtendedJSON`` would be a name in Java, where -camel-case method names are used, but in Ruby ``canonical_extended_json`` would -be acceptable. - -Terms -===== - -*Type wrapper object* - a JSON value consisting of an object with one or more -``$``-prefixed keys that collectively encode a BSON type and its corresponding -value using only JSON value primitives. - -*Extended JSON* - A general term for one of many string formats based on the -JSON standard that describes how to represent BSON documents in JSON using -standard JSON types and/or type wrapper objects. This specification gives a -formal definition to variations of such a format. - -*Relaxed Extended JSON* - A string format based on the JSON standard that -describes BSON documents. Relaxed Extended JSON emphasizes readability and -interoperability at the expense of type preservation. - -*Canonical Extended JSON* - A string format based on the JSON standard that -describes BSON documents. Canonical Extended JSON emphasizes type preservation -at the expense of readability and interoperability. - -*Legacy Extended JSON* - A string format based on the JSON standard that -describes a BSON document. The Legacy Extended JSON format does not describe -a specific, standardized format, and many tools, drivers, and libraries -implement Extended JSON in conflicting ways. - -Specification -============= - -Extended JSON Format --------------------- - -The Extended JSON grammar extends the JSON grammar as defined in `section 2`_ of -the `JSON specification`_ by augmenting the possible JSON values as defined in -`Section 3`_. This specification defines two formats for Extended JSON: - -* Canonical Extended JSON -* Relaxed Extended JSON - -An Extended JSON value MUST conform to one of these two formats as described -in the table below. - -.. _section 2: https://tools.ietf.org/html/rfc7159#section-2 -.. _section 3: https://tools.ietf.org/html/rfc7159#section-3 - -Notes on grammar -................ - -* Key order: - - * Keys within Canonical Extended JSON type wrapper objects SHOULD be emitted - in the order described. - - * Keys within Relaxed Extended JSON type wrapper objects are unordered. - -* Terms in *italics* represent types defined elsewhere in the table or in the - `JSON specification`_. - -* JSON *numbers* (as defined in `Section 6`_ of the JSON specification) include - both integer and floating point types. For the purpose of this document, we - define the following subtypes: - - * Type *integer* means a JSON *number* without *frac* or *exp* components; - this is expressed in the JSON spec grammer as ``[minus] int``. - - * Type *non-integer* means a JSON *number* that is not an *integer*; it - must include either a *frac* or *exp* component or both. - - * Type *pos-integer* means a non-negative JSON *number* without *frac* - or *exp* components; this is expressed in the JSON spec grammar as ``int``. - -* A *hex string* is a JSON *string* that contains only hexadecimal - digits [0-9a-f]. It SHOULD be emitted lower-case, but MUST be read - in a case-insensitive fashion. - -* detail the contents of a value, including type information. - -* \[Square brackets\] specify a type constraint that restricts the specification - to a particular range or set of values. - -.. _section 6: https://tools.ietf.org/html/rfc7159#section-6 - -Conversion table -................ - -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|**BSON 1.1 Type or |**Canonical Extended JSON Format** |**Relaxed Extended JSON Format** | -|Convention** | | | -+====================+==========================================================+=======================================================+ -|ObjectId |{"$oid": | -| |string*>} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Symbol |{"$symbol": *string*} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|String |*string* | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Int32 |{"$numberInt": <32-bit signed integer as a *string*>} | *integer* | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Int64 |{"$numberLong": <64-bit signed integer as a *string*>} | *integer* | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Double \[finite\] |{"$numberDouble": <64-bit signed floating point as a | *non-integer* | -| |decimal *string*>} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Double |{"$numberDouble": | -|\[non-finite\] |"-Infinity", or "NaN">} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Decimal128 |{"$numberDecimal": } [#]_ | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Binary |{"$binary": {"base64": | -| |``=``) payload as a *string*>, "subType": }} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Code |{"$code": *string*} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|CodeWScope |{"$code": *string*, "$scope": *Document*} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Document |*object* (with Extended JSON extensions) | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Timestamp |{"$timestamp": {"t": *pos-integer*, "i": *pos-integer*}} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Regular Expression |{"$regularExpression": {pattern: *string*, | | -| |"options": }} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|DBPointer |{"$dbPointer": {"$ref": , | | -| |"$id": *ObjectId*}} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Datetime |{"$date": {"$numberLong": <64-bit signed integer | {"$date": }} | as decribed in RFC-3339 [#]_ with maximum time | -|to 9999 inclusive\] | | precision of milliseconds [#]_ as a *string*>} | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Datetime |{"$date": {"$numberLong": <64-bit signed integer | | -|\[year before 1970 |giving millisecs relative to the epoch, as a *string*>}} | | -|or after 9999\] | | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|DBRef [#]_ |{"$ref": , "$id": | | -| |} | | -|Note: this is not | | | -|technically a BSON |If the generator supports DBRefs with a database | | -|type, but it is a |component, and the database component is nonempty: | | -|common convention. | | | -| |{"$ref": , | | -| | "$id": , | | -| | "$db": } | | -| | | | -| |DBRefs may also have other fields that do not begin with | | -| |``$``, which MUST appear after ``$id`` and ``$db`` (if | | -| |supported). | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|MinKey |{"$minKey": 1} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|MaxKey |{"$maxKey": 1} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Undefined |{"$undefined": *true*} | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Array |*array* | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Boolean |*true* or *false* | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ -|Null |*null* | | -+--------------------+----------------------------------------------------------+-------------------------------------------------------+ - -.. [#] This MUST conform to the `Decimal128 specification`_ - -.. [#] BSON Regular Expression options MUST be in alphabetical order. - -.. [#] See https://docs.mongodb.com/manual/reference/glossary/#term-namespace - -.. [#] See https://tools.ietf.org/html/rfc3339#section-5.6 - -.. [#] Fractional seconds SHOULD have exactly 3 decimal places if the fractional part - is non-zero. Otherwise, fractional seconds SHOULD be omitted if zero. - -.. [#] See https://docs.mongodb.com/manual/reference/database-references/#dbrefs - -.. _Decimal128 specification: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst#writing-to-extended-json - -Representation of Non-finite Numeric Values -........................................... - -Following the `Extended JSON format for the Decimal128 type`_, non-finite numeric -values are encoded as follows: - -+----------------------------------------+----------------------------------------+ -|**Value** |**String** | -+========================================+========================================+ -|Positive Infinity |``Infinity`` | -+----------------------------------------+----------------------------------------+ -|Negative Infinity |``-Infinity`` | -+----------------------------------------+----------------------------------------+ -|NaN (all variants) |``NaN`` | -+----------------------------------------+----------------------------------------+ - -.. _Extended JSON format for the Decimal128 type: https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst#to-string-representation - -For example, a BSON floating-point number with a value of negative infinity -would be encoded as Extended JSON as follows:: - - {"$numberDouble": "-Infinity"} - -Parsers -------- - -An Extended JSON parser (hereafter just "parser") is a tool that transforms an -Extended JSON string into another representation, such as BSON or a -language-native data structure. - -By default, a parser MUST accept values in either Canonical Extended JSON -format or Relaxed Extended JSON format as described in this specification. A -parser MAY allow users to restrict parsing to only Canonical Extended JSON -format or only Relaxed Extended JSON format. - -A parser MAY also accept strings that adhere to other formats, such as -Legacy Extended JSON formats emitted by old versions of mongoexport or -other tools, but only if explicitly configured to do so. - -A parser that accepts Legacy Extended JSON MUST be configurable such that a JSON -text of a MongoDB query filter containing the `regex`_ query operator can be -parsed, e.g.:: - - { "$regex": { - "$regularExpression" : { "pattern": "foo*", "options": "" } - }, - "$options" : "ix" - } - -or:: - - { "$regex": { - "$regularExpression" : { "pattern": "foo*", "options": "" } - } - } - -A parser that accepts Legacy Extended JSON MUST be configurable such that a JSON -text of a MongoDB query filter containing the `type`_ query operator can be -parsed, e.g.:: - - { "zipCode" : { $type : 2 } } - -or:: - - { "zipCode" : { $type : "string" } } - -A parser SHOULD support at least 200 `levels of nesting`_ in an Extended JSON -document but MAY set other limits on strings it can accept as defined in -`section 9`_ of the `JSON specification`_. - -When parsing a JSON object other than the top-level object, the presence of a -``$``-prefixed key indicates the object could be a type wrapper object as -described in the Extended JSON `Conversion table`_. In such a case, the parser -MUST follow these rules, unless configured to allow Legacy Extended JSON, -in which case it SHOULD follow these rules: - -* Parsers MUST NOT consider key order as having significance. For example, - the document ``{"$code": "function(){}", "$scope": {}}`` must be considered - identical to ``{"$scope": {}, "$code": "function(){}"}``. - -* If the parsed object contains any of the special **keys** in the Conversion - table such as ``"$binary"``, ``"$timestamp"``, etc., then it must contain - exactly the keys of a type wrapper. Any missing or extra keys constitute an - error. - -* If the **keys** of the parsed object exactly match the **keys** of a type - wrapper in the Conversion table, and the **values** of the parsed object have - the correct type for the type wrapper as described in the Conversion table, - then the parser MUST interpret the parsed object as a type wrapper object of - the corresponding type. - -* If the **keys** of the parsed object exactly match the **keys** of a type - wrapper in the Conversion table, but any of the **values** are of an incorrect - type, then the parser MUST report an error. - -.. _regex: https://docs.mongodb.com/manual/reference/operator/query/regex/ - -.. _type: https://docs.mongodb.com/manual/reference/operator/query/type/ - -.. _section 9: https://tools.ietf.org/html/rfc7159#section-9 - -.. _JSON specification: https://tools.ietf.org/html/rfc7159 - -Special rules for parsing JSON numbers -...................................... - -The Relaxed Extended JSON format uses JSON numbers for several different -BSON types. In order to allow parsers to use language-native JSON decoders -(which may not distinguish numeric type when parsing), the following rules apply -to parsing JSON numbers: - -* If the number is a *non-integer*, parsers SHOULD interpret it as BSON Double. - -* If the number is an *integer*, parsers SHOULD interpret it as being of the - smallest BSON integer type that can represent the number exactly. If a parser - is unable to represent the number exactly as an integer (e.g. a large 64-bit - number on a 32-bit platform), it MUST interpret it as a BSON Double even if - this results in a loss of precision. The parser MUST NOT interpret it as a - BSON String containing a decimal representation of the number. - -Generators ----------- - -An Extended JSON generator (hereafter just "generator") produces strings in an -Extended JSON format. - -A generator MUST allow users to produce strings in either the Canonical -Extended JSON format or the Relaxed Extended JSON format. If generators -provide a default format, the default SHOULD be the Relaxed Extended JSON -format. - -A generator MAY be capable of exporting strings that adhere to other -formats, such as Legacy Extended JSON formats. - -A generator SHOULD support at least 100 `levels of nesting`_ in a BSON -document. - -Transforming BSON -................. - -Given a BSON document (e.g. a buffer of bytes meeting the requirements of the -BSON specification), a generator MUST use the corresponding JSON values or -Extended JSON type wrapper objects for the BSON type given in the Extended JSON -`Conversion table`_ for the desired format. When transforming a BSON document -into Extended JSON text, a generator SHOULD emit the JSON keys and values in -the same order as given in the BSON document. - -Transforming Language-Native data -................................. - -Given language-native data (e.g. type primitives, container types, classes, -etc.), if there is a semantically-equivalent BSON type for a given -language-native type, a generator MUST use the corresponding JSON values or -Extended JSON type wrapper objects for the BSON type given in the Extended JSON -`Conversion table`_ for the desired format. For example, a Python ``datetime`` -object must be represented the same as a BSON datetime type. A generator -SHOULD error if a language-native type has no semantically-equivalent BSON -type. - -Format and Method Names -....................... - -The following format names SHOULD be used for selecting formats for generator -output: - -* ``canonicalExtendedJSON`` (references Canonical Extended JSON as described in - this specification) - -* ``relaxedExtendedJSON`` (references Relaxed Extended JSON as described in - this specification) - -* ``legacyExtendedJSON`` (if supported: references Legacy Extended JSON, - with implementation-defined behavior) - -Generators MAY use these format names as part of function/method names or MAY -use them as arguments or constants, as needed. - -If a generator provides a generic `to_json` or `to_extended_json` method, it -MUST default to producing Relaxed Extended JSON or MUST be deprecated in -favor of a spec-compliant method. - -Restrictions and limitations ----------------------------- - -Extended JSON is designed primarily for testing and human inspection of BSON -documents. It is not designed to reliably round-trip BSON documents. One -fundamental limitation is that JSON objects are inherently unordered and -BSON objects are ordered. - -Further, Extended JSON uses ``$``-prefixed keys in type wrappers and has no -provision for escaping a leading ``$`` used elsewhere in a document. This -means that the Extended JSON representation of a document with ``$``-prefixed -keys could be indistinguishable from another document with a type wrapper with -the same keys. - -Extended JSON formats SHOULD NOT be used in contexts where ``$``-prefixed keys -could exist in BSON documents (with the exception of the DBRef convention, -which is accounted for in this spec). - -Test Plan -========= - -Drivers, tools, and libraries can test their compliance to this specification by -running the tests in version 2.0 and above of the `BSON Corpus Test Suite`_. - -.. _BSON Corpus Test Suite: https://github.com/mongodb/specifications/blob/master/source/bson-corpus/bson-corpus.rst - -Examples -======== - -Canonical Extended JSON Example -------------------------------- - -Consider the following document, written in Groovy with the MongoDB Java Driver:: - - { - "_id": new ObjectId("57e193d7a9cc81b4027498b5"), - "Symbol": new BsonSymbol("symbol"), - "String": "string", - "Int32": 42, - "Int64": 42L, - "Double": 42.42, - "SpecialFloat": Float.NaN, - "Decimal": new Decimal128(1234), - "Binary": UUID.fromString("c8edabc3-f738-4ca3-b68d-ab92a91478a3"), - "BinaryUserDefined": new Binary((byte) 0x80, new byte[]{1, 2, 3, 4, 5}), - "Code": new Code("function() {}"), - "CodeWithScope": new CodeWithScope("function() {}", new Document()), - "Subdocument": new Document("foo", "bar"), - "Array": Arrays.asList(1, 2, 3, 4, 5), - "Timestamp": new BSONTimestamp(42, 1), - "RegularExpression": new BsonRegularExpression("foo*", "xi"), - "DatetimeEpoch": new Date(0), - "DatetimePositive": new Date(Long.MAX_VALUE), - "DatetimeNegative": new Date(Long.MIN_VALUE), - "True": true, - "False": false, - "DBPointer": new BsonDbPointer( - "db.collection", new ObjectId("57e193d7a9cc81b4027498b1")), - "DBRef": new DBRef( - "database", "collection", new ObjectId("57fd71e96e32ab4225b723fb")), - "DBRefNoDB": new DBRef( - "collection", new ObjectId("57fd71e96e32ab4225b723fb")), - "Minkey": new MinKey(), - "Maxkey": new MaxKey(), - "Null": null, - "Undefined": new BsonUndefined() - } - -The above document is transformed into the following (newlines and spaces added -for readability):: - - { - "_id": { - "$oid": "57e193d7a9cc81b4027498b5" - }, - "Symbol": { - "$symbol": "symbol" - }, - "String": "string", - "Int32": { - "$numberInt": "42" - }, - "Int64": { - "$numberLong": "42" - }, - "Double": { - "$numberDouble": "42.42" - }, - "SpecialFloat": { - "$numberDouble": "NaN" - }, - "Decimal": { - "$numberDecimal": "1234" - }, - "Binary": { - "$binary": { - "base64": "o0w498Or7cijeBSpkquNtg==", - "subType": "03" - } - }, - "BinaryUserDefined": { - "$binary": { - "base64": "AQIDBAU=", - "subType": "80" - } - }, - "Code": { - "$code": "function() {}" - }, - "CodeWithScope": { - "$code": "function() {}", - "$scope": {} - }, - "Subdocument": { - "foo": "bar" - }, - "Array": [ - {"$numberInt": "1"}, - {"$numberInt": "2"}, - {"$numberInt": "3"}, - {"$numberInt": "4"}, - {"$numberInt": "5"} - ], - "Timestamp": { - "$timestamp": { "t": 42, "i": 1 } - }, - "RegularExpression": { - "$regularExpression": { - "pattern": "foo*", - "options": "ix" - } - }, - "DatetimeEpoch": { - "$date": { - "$numberLong": "0" - } - }, - "DatetimePositive": { - "$date": { - "$numberLong": "9223372036854775807" - } - }, - "DatetimeNegative": { - "$date": { - "$numberLong": "-9223372036854775808" - } - }, - "True": true, - "False": false, - "DBPointer": { - "$dbPointer": { - "$ref": "db.collection", - "$id": { - "$oid": "57e193d7a9cc81b4027498b1" - } - } - }, - "DBRef": { - "$ref": "collection", - "$id": { - "$oid": "57fd71e96e32ab4225b723fb" - }, - "$db": "database" - }, - "DBRefNoDB": { - "$ref": "collection", - "$id": { - "$oid": "57fd71e96e32ab4225b723fb" - } - }, - "Minkey": { - "$minKey": 1 - }, - "Maxkey": { - "$maxKey": 1 - }, - "Null": null, - "Undefined": { - "$undefined": true - } - } - -Relaxed Extended JSON Example ------------------------------ - -In Relaxed Extended JSON, the example document is transformed similarly -to Canonical Extended JSON, with the exception of the following -keys (newlines and spaces added for readability):: - - { - ... - "Int32": 42, - "Int64": 42, - "Double": 42.42, - ... - "DatetimeEpoch": { - "$date": "1970-01-01T00:00:00.000Z" - }, - ... - } - -Motivation for Change -===================== - -There existed many Extended JSON parser and generator implementations prior to -this specification that used conflicting formats, since there was no agreement -on the precise format of Extended JSON. This resulted in problems where the -output of some generators could not be consumed by some parsers. - -MongoDB drivers needed a single, standard Extended JSON format for testing that -covers all BSON types. However, there were BSON types that had no defined -Extended JSON representation. This spec primarily addresses that need, but -provides for slightly broader use as well. - -Design Rationale -================ - -Of Relaxed and Canonical Formats --------------------------------- - -There are various use cases for expressing BSON documents in a text rather -that binary format. They broadly fall into two categories: - -* Type preserving: for things like testing, where one has to describe the - expected form of a BSON document, it's helpful to be able to precisely - specify expected types. In particular, numeric types need to differentiate - between Int32, Int64 and Double forms. - -* JSON-like: for things like a web API, where one is sending a document (or a - projection of a document) that only uses ordinary JSON type primitives, it's - desirable to represent numbers in the native JSON format. This output is - also the most human readable and is useful for debugging and documentation. - -The two formats in this specification address these two categories of use cases. - -Of Parsers and Generators -------------------------- - -Parsers need to accept any valid Extended JSON string that a generator can -produce. Parsers and generators are permitted to accept and output strings in -other formats as well for backwards compatibility. - -.. _levels of nesting: - -Acceptable nesting depth has implications for resource usage so unlimited -nesting is not permitted. - -Generators support at least 100 levels of nesting in a BSON document -being transformed to Extended JSON. This aligns with MongoDB's own limitation of -100 levels of nesting. - -Parsers support at least 200 levels of nesting in Extended JSON text, -since the Extended JSON language can double the level of apparent nesting of a -BSON document by wrapping certain types in their own documents. - -Of Canonical Type Wrapper Formats ---------------------------------- - -Prior to this specification, BSON types fell into three categories with respect -to Legacy Extended JSON: - -1. A single, portable representation for the type already existed. - -2. Multiple representations for the type existed among various Extended JSON - generators, and those representations were in conflict with each other or - with current portability goals. - -3. No Legacy Extended JSON representation existed. - -If a BSON type fell into category (1), this specification just declares that -form to be canonical, since all drivers, tools, and libraries already know how -to parse or output this form. There are two exceptions: - -RegularExpression -................. - -The form ``{"$regex: , $options: "}`` has until this -specification been canonical. The change to ``{"$regularExpression": -{pattern: , "options": "}}`` is motivated by a conflict between -the previous canonical form and the ``$regex`` MongoDB query operator. The form -specified here disambiguates between the two, such that a parser can accept any -MongoDB query filter, even one containing the ``$regex`` operator. - -Binary -...... - -The form ``{"$binary": "AQIDBAU=", "$type": "80"}`` has until this specification -been canonical. The change to ``{"$binary": {"base64": AQIDBAU=", "subType": -"80"}`` is motivated by a conflict between the previous canonical form and the -``$type`` MongoDB query operator. The form specified here disambiguates between -the two, such that a parser can accept any MongoDB query filter, even one -containing the ``$type`` operator. - -Reconciled type wrappers -........................ - -If a BSON type fell into category (2), this specification selects a new common -representation for the type to be canonical. Conflicting formats were gathered -by surveying a number of Extended JSON generators, including the MongoDB Java -Driver (version 3.3.0), the MongoDB Python Driver (version 3.4.0.dev0), the -MongoDB Extended JSON module on NPM (version 1.7.1), and each minor version of -mongoexport from 2.4.14 through 3.3.12. When possible, we set the "strict" -option on the JSON codec. The following BSON types had conflicting Extended JSON -representations: - -Binary -'''''' - -Some implementations write the Extended JSON form of a Binary object with a -strict two-hexadecimal digit subtype (e.g. they output a leading ``0`` for -subtypes < 16). However, the NPM mongodb-extended-json module and Java driver -use a single hexadecimal digit to represent subtypes less than 16. This -specification makes both one- and two-digit representations acceptable. - -Code -'''' - -Mongoexport 2.4 does not quote the ``Code`` value when writing out the extended -JSON form of a BSON Code object. All other implementations do so. This spec -canonicalises the form where the Javascript code is quoted, since the latter -form adheres to the JSON specification and the former does not. As an -additional note, the NPM mongodb-extended-json module uses the form ``{"code": -""}, omitting the dollar sign (``$``) from the key. This -specification does not accomodate the eccentricity of a single library. - -CodeWithScope -''''''''''''' - -In addition to the same variants as BSON Code types, there are other variations -when turning CodeWithScope objects into Extended JSON. Mongoexport 2.4 and 2.6 -omit the scope portion of CodeWithScope if it is empty, making the output -indistinguishable from a Code type. All other implementations include the empty -scope. This specification therefore canonicalises the form where the scope is -always included. The presence of ``$scope`` is what differentiates Code from -CodeWithScope. - -Datetime -'''''''' - -Mongoexport 2.4 and the Java driver always transform a Datetime object into an -Extended JSON string of the form ``{"$date": }``. This form has -the problem of a potential loss of precision or range on the Datetimes that can -be represented. Mongoexport 2.6 transforms Datetime objects into an extended -JSON string of the form ``{"$date": }`` for -dates starting at or after the Unix epoch (UTC). Dates prior to the epoch take -the form ``{"$date": {"$numberLong": ""}}``. Starting in version -3.0, mongoexport always turns Datetime objects into strings of the form -``{"$date": }``. The NPM mongodb-extended-json -module does the same. The Python driver can also transform Datetime objects into -strings like ``{"$date": {"$numberLong": ""}}``. This -specification canonicalises this form, since this form is the most portable. - -In Relaxed Extended JSON format, this specification provides for ISO-8601 -representation for better readability, but limits it to a portable subset, from -the epoch to the end of the largest year that can be represented with four -digits. This should encompass most typical use of dates in applications. - -DBPointer -''''''''' - -Mongoexport 2.4 and 2.6 use the form ``{"$ref": , "$id": }``. All other implementations studied include the canonical ``ObjectId`` -form: ``{"$ref": , "$id": {"$oid": }}``. Neither of these -forms are distinguishable from that of DBRef, so this specification creates a -new format: ``{"$dbPointer": {"$ref": , "$id": {"$oid": }}}``. - -Newly-added type wrappers -......................... - -If a BSON type fell into category (3), above, this specification creates a type -wrapper format for the type. The following new Extended JSON type wrappers are -introduced by this spec: - -* ``$dbPointer`` - See above. - -* ``$numberInt`` - This is used to preserve the "int32" BSON type in Canonical - Extended JSON. Without using ``$numberInt``, this type will be - indistinguishable from a double in certain languages where the distinction - does not exist, such as Javascript. - -* ``$numberDouble`` - This is used to preserve the ``double`` type in Canonical - Extended JSON, as some JSON generators might omit a trailing ".0" for - integral types. It also supports representing non-finite values like NaN or - Infinity which are prohibited in the JSON specification for numbers. - -* ``$symbol`` - The use of the ``$symbol`` key preserves the symbol type in - Canonical Extended JSON, distinguishing it from JSON strings. - -Reference Implementation -======================== - -[*Canonical Extended JSON format reference implementation needs to be updated*] - -PyMongo implements the Canonical Extended JSON format, which must be chosen by -selecting the right option on the ``JSONOptions`` object:: - - from bson.json_util import dumps, DatetimeRepresentation, CANONICAL_JSON_OPTIONS - - dumps(document, json_options=CANONICAL_JSON_OPTIONS) - -[*Relaxed Extended JSON format reference implementation is TBD*] - -Implementation Notes -==================== - -JSON File Format ----------------- - -Some applications like mongoexport may wish to write multiple Extended JSON -documents to a single file. One way to do this is to list each JSON document -one-per-line. When doing this, it is important to ensure that special characters -like newlines are encoded properly (e.g. ``\n``). - -Duplicate Keys --------------- - -The BSON specification does not prohibit duplicate key names within the same -BSON document, but provides no semantics for the interpretation of duplicate -keys. The JSON specification says that names within an object should be unique, -and many JSON libraries are incapable of handling this scenario. - -This specification is silent on the matter, so as not to conflict with a future -change by either specification. - -Future Work -=========== - -This specification will need to be amended if future BSON types are added to the -BSON specification. - -Q&A -=== - -**Q**. Why was version 2 of the spec necessary? - -**A**. After Version 1 was released, several stakeholders raised concerns that -not providing an option to output BSON numbers as ordinary JSON numbers limited -the utility of Extended JSON for common historical uses. We decided to provide -a second format option and more clearly distinguish the use cases (and -limitations) inherent in each format. - -**Q**. My BSON parser doesn't distinguish every BSON type. Does my Extended -JSON generator need to distinguish these types? - -**A**. No. Some BSON parsers do not emit a unique type for each BSON type, -making round-tripping BSON through such libraries impossible without changing -the document. For example, a ``DBPointer`` will be parsed into a ``DBRef`` by -PyMongo. In such cases, a generator must emit the Extended JSON form for -whatever type the BSON parser emitted. It does not need to preserve type -information when that information has been lost by the BSON parser. - -**Q**. How can implementations which require backwards compatibility with Legacy -Extended JSON, in which BSON regular expressions were represented with -``$regex``, handle parsing of extended JSON test representing a MongoDB query -filter containing the ``$regex`` operator? - -**A**. An implementation can handle this in a number of ways: - -- Introduce an enumeration that determines the behavior of the parser. If the - value is LEGACY, it will parse ``$regex`` and not treat ``$regularExpression`` - specially, and if the value is CANONICAL, it will parse ``$regularExpression`` - and not treat ``$regex`` specially. -- Support both legacy and canonical forms in the parser without requiring the - application to specify one or the other. Making that work for the ``$regex`` - query operator use case will require that the rules set forth in the 1.0.0 - version of this specification are followed for ``$regex``; specifically, that - a document with a ``$regex`` key whose value is a JSON object should be - parsed as a normal document and not reported as an error. - -**Q**. How can implementations which require backwards compatibility with Legacy -Extended JSON, in which BSON binary values were represented like ``{"$binary": -"AQIDBAU=", "$type": "80"}``, handle parsing of extended JSON test representing -a MongoDB query filter containing the ``$type`` operator? - -**A**. An implementation can handle this in a number of ways: - -- Introduce an enumeration that determines the behavior of the parser. If the - value is LEGACY, it will parse the new binary form and not treat the legacy - one specially, and if the value is CANONICAL, it will parse the new form and - not treat the legacy form specially. -- Support both legacy and canonical forms in the parser without requiring the - application to specify one or the other. Making that work for the ``$type`` - query operator use case will require that the rules set forth in the 1.0.0 - version of this specification are followed for ``$type``; specifically, that - a document with a ``$type`` key whose value is an integral type, or a - document with a ``$type`` key but without a ``$binary`` key, should be - parsed as a normal document and not reported as an error. - -**Q**. Sometimes I see the term "extjson" used in other specifications. Is -"extjson" related to this specification? - -**A**. Yes, "extjson" is short for "Extended JSON". - -Changes -======= - -v2.0.0 ------- - -* Added "Relaxed" format. - -* Changed BSON timestamp type wrapper back to ``{"t": *int*, "i": *int*}`` for - backwards compatibility. (The change in v1 to unsigned 64-bit string was - premature optimization.) - -* Changed BSON regular expression type wrapper to - ``{"$regularExpression": {pattern: *string*, "options": *string*"}}``. - -* Changed BSON binary type wrapper to - ``{"$binary": {"base64": , - "subType": }}`` - -* Added "Restrictions and limitations" section. - -* Clarified parser and generator rules. +.. note:: + This specification has been converted to Markdown and renamed to + `extended-json.md `_. diff --git a/source/extended-json/extended-json.md b/source/extended-json/extended-json.md new file mode 100644 index 0000000000..ce3dacd2b5 --- /dev/null +++ b/source/extended-json/extended-json.md @@ -0,0 +1,742 @@ +# Extended JSON + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +MongoDB Extended JSON is a string format for representing BSON documents. This specification defines the canonical +format for representing each BSON type in the Extended JSON format. Thus, a tool that implements Extended JSON will be +able to parse the output of any tool that emits Canonical Extended JSON. It also defines a Relaxed Extended JSON format +that improves readability at the expense of type information preservation. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Naming + +Acceptable naming deviations should fall within the basic style of the language. For example, `CanonicalExtendedJSON` +would be a name in Java, where camel-case method names are used, but in Ruby `canonical_extended_json` would be +acceptable. + +## Terms + +*Type wrapper object* - a JSON value consisting of an object with one or more `$`-prefixed keys that collectively encode +a BSON type and its corresponding value using only JSON value primitives. + +*Extended JSON* - A general term for one of many string formats based on the JSON standard that describes how to +represent BSON documents in JSON using standard JSON types and/or type wrapper objects. This specification gives a +formal definition to variations of such a format. + +*Relaxed Extended JSON* - A string format based on the JSON standard that describes BSON documents. Relaxed Extended +JSON emphasizes readability and interoperability at the expense of type preservation. + +*Canonical Extended JSON* - A string format based on the JSON standard that describes BSON documents. Canonical Extended +JSON emphasizes type preservation at the expense of readability and interoperability. + +*Legacy Extended JSON* - A string format based on the JSON standard that describes a BSON document. The Legacy Extended +JSON format does not describe a specific, standardized format, and many tools, drivers, and libraries implement Extended +JSON in conflicting ways. + +## Specification + +### Extended JSON Format + +The Extended JSON grammar extends the JSON grammar as defined in +[section 2](https://tools.ietf.org/html/rfc7159#section-2) of the +[JSON specification](https://tools.ietf.org/html/rfc7159) by augmenting the possible JSON values as defined in +[Section 3](https://tools.ietf.org/html/rfc7159#section-3). This specification defines two formats for Extended JSON: + +- Canonical Extended JSON +- Relaxed Extended JSON + +An Extended JSON value MUST conform to one of these two formats as described in the table below. + +#### Notes on grammar + +- Key order: + - Keys within Canonical Extended JSON type wrapper objects SHOULD be emitted in the order described. + - Keys within Relaxed Extended JSON type wrapper objects are unordered. +- Terms in *italics* represent types defined elsewhere in the table or in the + [JSON specification](https://tools.ietf.org/html/rfc7159). +- JSON *numbers* (as defined in [Section 6](https://tools.ietf.org/html/rfc7159#section-6) of the JSON specification) + include both integer and floating point types. For the purpose of this document, we define the following subtypes: + - Type *integer* means a JSON *number* without *frac* or *exp* components; this is expressed in the JSON spec grammar + as `[minus] int`. + - Type *non-integer* means a JSON *number* that is not an *integer*; it must include either a *frac* or *exp* + component or both. + - Type *pos-integer* means a non-negative JSON *number* without *frac* or *exp* components; this is expressed in the + JSON spec grammar as `int`. +- A *hex string* is a JSON *string* that contains only hexadecimal digits `[0-9a-f]`. It SHOULD be emitted lower-case, + but MUST be read in a case-insensitive fashion. +- < Angle brackets > detail the contents of a value, including type information. +- [Square brackets] specify a type constraint that restricts the specification to a particular range or set of values. + +#### Conversion table + +| **BSON 1.1 Type or Convention** | **Canonical Extended JSON Format** | **Relaxed Extended JSON Format** | +| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| ObjectId | `{"$oid": ` < ObjectId bytes as 24-character, big-endian _hex string_ > `}` | < Same as Canonical Extended JSON > | +| Symbol | `{"$symbol": ` _string_ `}` | < Same as Canonical Extended JSON > | +| String | _string_ | < Same as Canonical Extended JSON > | +| Int32 | `{"$numberInt": ` < 32-bit signed integer as a _string_ > `}` | _integer_ | +| Int64 | `{"$numberLong": ` < 64-bit signed integer as a _string_ > `}` | _integer_ | +| Double [finite] | `{"$numberDouble": ` < 64-bit signed floating point as a decimal _string_ > `}` | _non-integer_ | +| Double [non-finite] | `{"$numberDouble": ` < One of the _strings_: `"Infinity"`, `"-Infinity"`, or `"NaN"`. > `}` | < Same as Canonical Extended JSON > | +| Decimal128 | `{"$numberDecimal": ` < decimal as a _string_[^1] > `}` | < Same as Canonical Extended JSON > | +| Binary | `{"$binary": {"base64": ` < base64-encoded (with padding as `=`) payload as a _string_ > `, "subType": ` < BSON binary type as a one- or two-character _hex string_ > `}}` | < Same as Canonical Extended JSON > | +| Code | `{"$code": ` _string_ `}` | < Same as Canonical Extended JSON > | +| CodeWScope | `{"$code": ` _string_ `, "$scope": ` _Document_ `}` | < Same as Canonical Extended JSON > | +| Document | _object_ (with Extended JSON extensions) | < Same as Canonical Extended JSON > | +| Timestamp | `{"$timestamp": {"t": ` _pos-integer_ `, "i": ` _pos-integer_ `}}` | < Same as Canonical Extended JSON > | +| Regular Expression | `{"$regularExpression": {pattern: ` _string_ `, "options": ` < BSON regular expression options as a _string_ or `""`[^2] > `}}` | < Same as Canonical Extended JSON > | +| DBPointer | `{"$dbPointer": {"$ref": ` < namespace[^3] as a _string_ > `, "$id": ` _ObjectId_ `}}` | < Same as Canonical Extended JSON > | +| Datetime [year from 1970 to 9999 inclusive] | `{"$date": {"$numberLong": ` < 64-bit signed integer giving millisecs relative to the epoch, as a _string_ > `}}` | `{"$date": ` ISO-8601 Internet Date/Time Format as described in RFC-3339[^4] with maximum time precision of milliseconds[^5] as a _string_ `}` | +| Datetime [year before 1970 or after 9999] | `{"$date": {"$numberLong": ` < 64-bit signed integer giving millisecs relative to the epoch, as a _string_ > `}}` | < Same as Canonical Extended JSON > | +| DBRef[^6]

Note: this is not technically a BSON type, but it is a common convention. | `{"$ref": ` < collection name as a _string_ > `, "$id": ` < Extended JSON for the id > `}`

If the generator supports DBRefs with a database component, and the database component is nonempty:

`{"$ref": ` < collection name as a _string_ > `, "$id": ` < Extended JSON for the id > `, "$db": ` < database name as a _string_ > `}`

DBRefs may also have other fields, which MUST appear after `$id` and `$db` (if supported). | < Same as Canonical Extended JSON > | +| MinKey | `{"$minKey": 1}` | < Same as Canonical Extended JSON > | +| MaxKey | `{"$maxKey": 1}` | < Same as Canonical Extended JSON > | +| Undefined | `{"$undefined": true}` | < Same as Canonical Extended JSON > | +| Array | _array_ | < Same as Canonical Extended JSON > | +| Boolean | `true` or `false` | < Same as Canonical Extended JSON > | +| Null | `null` | < Same as Canonical Extended JSON > | + +______________________________________________________________________ + +#### Representation of Non-finite Numeric Values + +Following the [Extended JSON format for the Decimal128 type](../bson-decimal128/decimal128.md#to-string-representation), +non-finite numeric values are encoded as follows: + +| **Value** | **String** | +| ------------------ | ----------- | +| Positive Infinity | `Infinity` | +| Negative Infinity | `-Infinity` | +| NaN (all variants) | `NaN` | + +For example, a BSON floating-point number with a value of negative infinity would be encoded as Extended JSON as +follows: + +```javascript +{"$numberDouble": "-Infinity"} +``` + +### Parsers + +An Extended JSON parser (hereafter just "parser") is a tool that transforms an Extended JSON string into another +representation, such as BSON or a language-native data structure. + +By default, a parser MUST accept values in either Canonical Extended JSON format or Relaxed Extended JSON format as +described in this specification. A parser MAY allow users to restrict parsing to only Canonical Extended JSON format or +only Relaxed Extended JSON format. + +A parser MAY also accept strings that adhere to other formats, such as Legacy Extended JSON formats emitted by old +versions of mongoexport or other tools, but only if explicitly configured to do so. + +A parser that accepts Legacy Extended JSON MUST be configurable such that a JSON text of a MongoDB query filter +containing the [regex](https://www.mongodb.com/docs/manual/reference/operator/query/regex/) query operator can be +parsed, e.g.: + +```javascript +{ "$regex": { + "$regularExpression" : { "pattern": "foo*", "options": "" } + }, + "$options" : "ix" +} +``` + +or: + +```javascript +{ "$regex": { + "$regularExpression" : { "pattern": "foo*", "options": "" } + } +} +``` + +A parser that accepts Legacy Extended JSON MUST be configurable such that a JSON text of a MongoDB query filter +containing the [type](https://www.mongodb.com/docs/manual/reference/operator/query/type/) query operator can be parsed, +e.g.: + +```javascript +{ "zipCode" : { $type : 2 } } +``` + +or: + +```javascript +{ "zipCode" : { $type : "string" } } +``` + +A parser SHOULD support at least 200 levels of nesting in an Extended JSON document but MAY set other limits on strings +it can accept as defined in [section 9](https://tools.ietf.org/html/rfc7159#section-9) of the +[JSON specification](https://tools.ietf.org/html/rfc7159). + +When parsing a JSON object other than the top-level object, the presence of a `$`-prefixed key indicates the object +could be a type wrapper object as described in the Extended JSON [Conversion table](#conversion-table). In such a case, +the parser MUST follow these rules, unless configured to allow Legacy Extended JSON, in which case it SHOULD follow +these rules: + +- Parsers MUST NOT consider key order as having significance. For example, the document + `{"$code": "function(){}", "$scope": {}}` must be considered identical to `{"$scope": {}, "$code": "function(){}"}`. + +- If the parsed object contains any of the special **keys** for a type in the [Conversion table](#conversion-table) + (e.g. `"$binary"`, `"$timestamp"`) then it must contain exactly the keys of the type wrapper. Any missing or extra + keys constitute an error. + + DBRef is the lone exception to this rule, as it is only a common convention and not a proper type. An object that + resembles a DBRef but fails to fully comply with its structure (e.g. has `$ref` but missing `$id`) MUST be left + as-is and MUST NOT constitute an error. + +- If the **keys** of the parsed object exactly match the **keys** of a type wrapper in the Conversion table, and the + **values** of the parsed object have the correct type for the type wrapper as described in the Conversion table, + then the parser MUST interpret the parsed object as a type wrapper object of the corresponding type. + +- If the **keys** of the parsed object exactly match the **keys** of a type wrapper in the Conversion table, but any of + the **values** are of an incorrect type, then the parser MUST report an error. + +- If the `$`-prefixed key does not match a known type wrapper in the Conversion table, the parser MUST NOT raise an + error and MUST leave the value as-is. See [Restrictions and limitations](#restrictions-and-limitations) for + additional information. + +#### Special rules for parsing JSON numbers + +The Relaxed Extended JSON format uses JSON numbers for several different BSON types. In order to allow parsers to use +language-native JSON decoders (which may not distinguish numeric type when parsing), the following rules apply to +parsing JSON numbers: + +- If the number is a *non-integer*, parsers SHOULD interpret it as BSON Double. +- If the number is an *integer*, parsers SHOULD interpret it as being of the smallest BSON integer type that can + represent the number exactly. If a parser is unable to represent the number exactly as an integer (e.g. a large + 64-bit number on a 32-bit platform), it MUST interpret it as a BSON Double even if this results in a loss of + precision. The parser MUST NOT interpret it as a BSON String containing a decimal representation of the number. + +#### Special rules for parsing `$uuid` fields + +As per the [UUID specification](../bson-binary-uuid/uuid.md), Binary subtype 3 or 4 are used to represent UUIDs in BSON. +Consequently, UUIDs are handled as per the convention described for the `Binary` type in the +[Conversion table](#conversion-table), e.g. the following document written with the MongoDB Python Driver: + +```javascript +{"Binary": uuid.UUID("c8edabc3-f738-4ca3-b68d-ab92a91478a3")} +``` + +is transformed into the following (newlines and spaces added for readability): + +```javascript +{"Binary": { + "$binary": { + "base64": "yO2rw/c4TKO2jauSqRR4ow==", + "subType": "04"} + } +} +``` + +> [!NOTE] +> The above described type conversion assumes that UUID representation is set to `STANDARD`. See the +> [UUID specification](../bson-binary-uuid/uuid.md) for more information about UUID representations. + +While this transformation preserves BSON subtype information (since UUIDs can be represented as BSON subtype 3 *or* 4), +base64-encoding is not the standard way of representing UUIDs and using it makes comparing these values against textual +representations coming from platform libraries difficult. Consequently, we also allow UUIDs to be represented in +extended JSON as: + +```javascript +{"$uuid": } +``` + +The rules for generating the canonical string representation of a UUID are defined in +[RFC 4122 Section 3](https://tools.ietf.org/html/rfc4122#section-3). Use of this format result in a more readable +extended JSON representation of the UUID from the previous example: + +```javascript +{"Binary": { + "$uuid": "c8edabc3-f738-4ca3-b68d-ab92a91478a3" + } +} +``` + +Parsers MUST interpret the `$uuid` key as BSON Binary subtype 4. Parsers MUST accept textual representations of UUIDs +that omit the URN prefix (usually `urn:uuid:`). Parsers MAY also accept textual representations of UUIDs that omit the +hyphens between hex character groups (e.g. `c8edabc3f7384ca3b68dab92a91478a3`). + +### Generators + +An Extended JSON generator (hereafter just "generator") produces strings in an Extended JSON format. + +A generator MUST allow users to produce strings in either the Canonical Extended JSON format or the Relaxed Extended +JSON format. If generators provide a default format, the default SHOULD be the Relaxed Extended JSON format. + +A generator MAY be capable of exporting strings that adhere to other formats, such as Legacy Extended JSON formats. + +A generator SHOULD support at least 100 levels of nesting in a BSON document. + +#### Transforming BSON + +Given a BSON document (e.g. a buffer of bytes meeting the requirements of the BSON specification), a generator MUST use +the corresponding JSON values or Extended JSON type wrapper objects for the BSON type given in the Extended JSON +[Conversion table](#conversion-table) for the desired format. When transforming a BSON document into Extended JSON text, +a generator SHOULD emit the JSON keys and values in the same order as given in the BSON document. + +#### Transforming Language-Native data + +Given language-native data (e.g. type primitives, container types, classes, etc.), if there is a semantically-equivalent +BSON type for a given language-native type, a generator MUST use the corresponding JSON values or Extended JSON type +wrapper objects for the BSON type given in the Extended JSON [Conversion table](#conversion-table) for the desired +format. For example, a Python `datetime` object must be represented the same as a BSON datetime type. A generator SHOULD +error if a language-native type has no semantically-equivalent BSON type. + +#### Format and Method Names + +The following format names SHOULD be used for selecting formats for generator output: + +- `canonicalExtendedJSON` (references Canonical Extended JSON as described in this specification) +- `relaxedExtendedJSON` (references Relaxed Extended JSON as described in this specification) +- `legacyExtendedJSON` (if supported: references Legacy Extended JSON, with implementation-defined behavior) + +Generators MAY use these format names as part of function/method names or MAY use them as arguments or constants, as +needed. + +If a generator provides a generic `to_json` or `to_extended_json` method, it MUST default to producing Relaxed Extended +JSON or MUST be deprecated in favor of a spec-compliant method. + +### Restrictions and limitations + +Extended JSON is designed primarily for testing and human inspection of BSON documents. It is not designed to reliably +round-trip BSON documents. One fundamental limitation is that JSON objects are inherently unordered and BSON objects are +ordered. + +Further, Extended JSON uses `$`-prefixed keys in type wrappers and has no provision for escaping a leading `$` used +elsewhere in a document. This means that the Extended JSON representation of a document with `$`-prefixed keys could be +indistinguishable from another document with a type wrapper with the same keys. + +Extended JSON formats SHOULD NOT be used in contexts where `$`-prefixed keys could exist in BSON documents (with the +exception of the DBRef convention, which is accounted for in this spec). + +## Test Plan + +Drivers, tools, and libraries can test their compliance to this specification by running the tests in version 2.0 and +above of the [BSON Corpus Test Suite](../bson-corpus/bson-corpus.md). + +## Examples + +### Canonical Extended JSON Example + +Consider the following document, written with the MongoDB Python Driver: + +```javascript +{ + "_id": bson.ObjectId("57e193d7a9cc81b4027498b5"), + "String": "string", + "Int32": 42, + "Int64": bson.Int64(42), + "Double": 42.42, + "Decimal": bson.Decimal128("1234.5"), + "Binary": uuid.UUID("c8edabc3-f738-4ca3-b68d-ab92a91478a3"), + "BinaryUserDefined": bson.Binary(b'123', 80), + "Code": bson.Code("function() {}"), + "CodeWithScope": bson.Code("function() {}", scope={}), + "Subdocument": {"foo": "bar"}, + "Array": [1, 2, 3, 4, 5], + "Timestamp": bson.Timestamp(42, 1), + "RegularExpression": bson.Regex("foo*", "xi"), + "DatetimeEpoch": datetime.datetime.utcfromtimestamp(0), + "DatetimePositive": datetime.datetime.max, + "DatetimeNegative": datetime.datetime.min, + "True": True, + "False": False, + "DBRef": bson.DBRef( + "collection", bson.ObjectId("57e193d7a9cc81b4027498b1"), database="database"), + "DBRefNoDB": bson.DBRef( + "collection", bson.ObjectId("57fd71e96e32ab4225b723fb")), + "Minkey": bson.MinKey(), + "Maxkey": bson.MaxKey(), + "Null": None +} +``` + +The above document is transformed into the following (newlines and spaces added for readability): + +```javascript +{ + "_id": { + "$oid": "57e193d7a9cc81b4027498b5" + }, + "String": "string", + "Int32": { + "$numberInt": "42" + }, + "Int64": { + "$numberLong": "42" + }, + "Double": { + "$numberDouble": "42.42" + }, + "Decimal": { + "$numberDecimal": "1234.5" + }, + "Binary": { + "$binary": { + "base64": "yO2rw/c4TKO2jauSqRR4ow==", + "subType": "04" + } + }, + "BinaryUserDefined": { + "$binary": { + "base64": "MTIz", + "subType": "80" + } + }, + "Code": { + "$code": "function() {}" + }, + "CodeWithScope": { + "$code": "function() {}", + "$scope": {} + }, + "Subdocument": { + "foo": "bar" + }, + "Array": [ + {"$numberInt": "1"}, + {"$numberInt": "2"}, + {"$numberInt": "3"}, + {"$numberInt": "4"}, + {"$numberInt": "5"} + ], + "Timestamp": { + "$timestamp": { "t": 42, "i": 1 } + }, + "RegularExpression": { + "$regularExpression": { + "pattern": "foo*", + "options": "ix" + } + }, + "DatetimeEpoch": { + "$date": { + "$numberLong": "0" + } + }, + "DatetimePositive": { + "$date": { + "$numberLong": "253402300799999" + } + }, + "DatetimeNegative": { + "$date": { + "$numberLong": "-62135596800000" + } + }, + "True": true, + "False": false, + "DBRef": { + "$ref": "collection", + "$id": { + "$oid": "57e193d7a9cc81b4027498b1" + }, + "$db": "database" + }, + "DBRefNoDB": { + "$ref": "collection", + "$id": { + "$oid": "57fd71e96e32ab4225b723fb" + } + }, + "Minkey": { + "$minKey": 1 + }, + "Maxkey": { + "$maxKey": 1 + }, + "Null": null +} +``` + +### Relaxed Extended JSON Example + +In Relaxed Extended JSON, the example document is transformed similarly to Canonical Extended JSON, with the exception +of the following keys (newlines and spaces added for readability): + +```javascript +{ + ... + "Int32": 42, + "Int64": 42, + "Double": 42.42, + ... + "DatetimeEpoch": { + "$date": "1970-01-01T00:00:00.000Z" + }, + ... +} +``` + +## Motivation for Change + +There existed many Extended JSON parser and generator implementations prior to this specification that used conflicting +formats, since there was no agreement on the precise format of Extended JSON. This resulted in problems where the output +of some generators could not be consumed by some parsers. + +MongoDB drivers needed a single, standard Extended JSON format for testing that covers all BSON types. However, there +were BSON types that had no defined Extended JSON representation. This spec primarily addresses that need, but provides +for slightly broader use as well. + +## Design Rationale + +### Of Relaxed and Canonical Formats + +There are various use cases for expressing BSON documents in a text rather that binary format. They broadly fall into +two categories: + +- Type preserving: for things like testing, where one has to describe the expected form of a BSON document, it's helpful + to be able to precisely specify expected types. In particular, numeric types need to differentiate between Int32, + Int64 and Double forms. +- JSON-like: for things like a web API, where one is sending a document (or a projection of a document) that only uses + ordinary JSON type primitives, it's desirable to represent numbers in the native JSON format. This output is also + the most human readable and is useful for debugging and documentation. + +The two formats in this specification address these two categories of use cases. + +### Of Parsers and Generators + +Parsers need to accept any valid Extended JSON string that a generator can produce. Parsers and generators are permitted +to accept and output strings in other formats as well for backwards compatibility. + + + +Acceptable nesting depth has implications for resource usage so unlimited nesting is not permitted. + +Generators support at least 100 levels of nesting in a BSON document being transformed to Extended JSON. This aligns +with MongoDB's own limitation of 100 levels of nesting. + +Parsers support at least 200 levels of nesting in Extended JSON text, since the Extended JSON language can double the +level of apparent nesting of a BSON document by wrapping certain types in their own documents. + +### Of Canonical Type Wrapper Formats + +Prior to this specification, BSON types fell into three categories with respect to Legacy Extended JSON: + +1. A single, portable representation for the type already existed. +2. Multiple representations for the type existed among various Extended JSON generators, and those representations were + in conflict with each other or with current portability goals. +3. No Legacy Extended JSON representation existed. + +If a BSON type fell into category (1), this specification just declares that form to be canonical, since all drivers, +tools, and libraries already know how to parse or output this form. There are two exceptions: + +#### RegularExpression + +The form `{"$regex: , $options: "}` has until this specification been canonical. The change to +`{"$regularExpression": {pattern: , "options": "}}` is motivated by a conflict between the previous +canonical form and the `$regex` MongoDB query operator. The form specified here disambiguates between the two, such that +a parser can accept any MongoDB query filter, even one containing the `$regex` operator. + +#### Binary + +The form `{"$binary": "AQIDBAU=", "$type": "80"}` has until this specification been canonical. The change to +`{"$binary": {"base64": "AQIDBAU=", "subType": "80"}}` is motivated by a conflict between the previous canonical form +and the `$type` MongoDB query operator. The form specified here disambiguates between the two, such that a parser can +accept any MongoDB query filter, even one containing the `$type` operator. + +#### Reconciled type wrappers + +If a BSON type fell into category (2), this specification selects a new common representation for the type to be +canonical. Conflicting formats were gathered by surveying a number of Extended JSON generators, including the MongoDB +Java Driver (version 3.3.0), the MongoDB Python Driver (version 3.4.0.dev0), the MongoDB Extended JSON module on NPM +(version 1.7.1), and each minor version of mongoexport from 2.4.14 through 3.3.12. When possible, we set the "strict" +option on the JSON codec. The following BSON types had conflicting Extended JSON representations: + +##### Binary + +Some implementations write the Extended JSON form of a Binary object with a strict two-hexadecimal digit subtype (e.g. +they output a leading `0` for subtypes < 16). However, the NPM mongodb-extended-json module and Java driver use a single +hexadecimal digit to represent subtypes less than 16. This specification makes both one- and two-digit representations +acceptable. + +##### Code + +Mongoexport 2.4 does not quote the `Code` value when writing out the extended JSON form of a BSON Code object. All other +implementations do so. This spec canonicalises the form where the Javascript code is quoted, since the latter form +adheres to the JSON specification and the former does not. As an additional note, the NPM mongodb-extended-json module +uses the form `{"code": ""}`, omitting the dollar sign (`$`) from the key. This specification does not +accommodate the eccentricity of a single library. + +##### CodeWithScope + +In addition to the same variants as BSON Code types, there are other variations when turning CodeWithScope objects into +Extended JSON. Mongoexport 2.4 and 2.6 omit the scope portion of CodeWithScope if it is empty, making the output +indistinguishable from a Code type. All other implementations include the empty scope. This specification therefore +canonicalises the form where the scope is always included. The presence of `$scope` is what differentiates Code from +CodeWithScope. + +##### Datetime + +Mongoexport 2.4 and the Java driver always transform a Datetime object into an Extended JSON string of the form +`{"$date": }`. This form has the problem of a potential loss of precision or range on the Datetimes that +can be represented. Mongoexport 2.6 transforms Datetime objects into an extended JSON string of the form +`{"$date": }`for dates starting at or after the Unix epoch (UTC). Dates prior to the +epoch take the form `{"$date": {"$numberLong": ""}}`. Starting in version 3.0, mongoexport always turns +Datetime objects into strings of the form `{"$date": }`. The NPM mongodb-extended-json +module does the same. The Python driver can also transform Datetime objects into strings like +`{"$date": {"$numberLong": ""}}`. This specification canonicalises this form, since this form is the +most portable. In Relaxed Extended JSON format, this specification provides for ISO-8601 representation for better +readability, but limits it to a portable subset, from the epoch to the end of the largest year that can be represented +with four digits. This should encompass most typical use of dates in applications. + +##### DBPointer + +Mongoexport 2.4 and 2.6 use the form`{"$ref": , "$id": }`. All other implementations studied +include the canonical `ObjectId` form:`{"$ref": , "$id": {"$oid": }}`. Neither of these forms are +distinguishable from that of DBRef, so this specification creates a new format: +`{"$dbPointer": {"$ref": , "$id": {"$oid": }}}`. + +##### Newly-added type wrappers . + +If a BSON type fell into category (3), above, this specification creates a type wrapper format for the type. The +following new Extended JSON type wrappers are introduced by this spec: + +- `$dbPointer`- See above. + +- `$numberInt` - This is used to preserve the "int32" BSON type in Canonical Extended JSON. Without using `$numberInt`, + this type will be indistinguishable from a double in certain languages where the distinction does not exist, such as + Javascript. + +- `$numberDouble` - This is used to preserve the `double`type in Canonical Extended JSON, as some JSON generators might + omit a trailing ".0" for integral types. + + It also supports representing non-finite values like NaN or Infinity which are prohibited in the JSON specification + for numbers. + +- `$symbol` - The use of the `$symbol` key preserves the symbol type in Canonical Extended JSON, distinguishing it from + JSON strings. + +### Reference Implementation + +*Canonical Extended JSON format reference implementation needs to be updated* + +PyMongo implements the Canonical Extended JSON format, which must be chosen by selecting the right option on the +`JSONOptions` object:: + +```python +from bson.json_util import dumps, DatetimeRepresentation, CANONICAL_JSON_OPTIONS +dumps(document, json_options=CANONICAL_JSON_OPTIONS) +``` + +*Relaxed Extended JSON format reference implementation is TBD* + +### Implementation Notes + +#### JSON File Format + +Some applications like mongoexport may wish to write multiple Extended JSON documents to a single file. One way to do +this is to list each JSON document one-per-line. When doing this, it is important to ensure that special characters like +newlines are encoded properly (e.g.`n`). + +#### Duplicate Keys + +The BSON specification does not prohibit duplicate key names within the same BSON document, but provides no semantics +for the interpretation of duplicate keys. The JSON specification says that names within an object should be unique, and +many JSON libraries are incapable of handling this scenario. This specification is silent on the matter, so as not to +conflict with a future change by either specification. + +### Future Work + +This specification will need to be amended if future BSON types are added to the BSON specification. + +## Q&A + +**Q**. Why was version 2 of the spec necessary? + +**A**. After Version 1 was released, several stakeholders raised concerns that not providing an option to output BSON +numbers as ordinary JSON numbers limited the utility of Extended JSON for common historical uses. We decided to provide +a second format option and more clearly distinguish the use cases (and limitations) inherent in each format. + +**Q**. My BSON parser doesn't distinguish every BSON type. Does my Extended JSON generator need to distinguish these +types? + +**A**. No. Some BSON parsers do not emit a unique type for each BSON type, making round-tripping BSON through such +libraries impossible without changing the document. For example, a `DBPointer` will be parsed into a `DBRef` by PyMongo. +In such cases, a generator must emit the Extended JSON form for whatever type the BSON parser emitted. It does not need +to preserve type information when that information has been lost by the BSON parser. + +**Q**. How can implementations which require backwards compatibility with Legacy Extended JSON, in which BSON regular +expressions were represented with `$regex`, handle parsing of extended JSON test representing a MongoDB query filter +containing the `$regex` operator? + +**A**. An implementation can handle this in a number of ways: - Introduce an enumeration that determines the behavior of +the parser. If the value is LEGACY, it will parse `$regex`and not treat `$regularExpression` specially, and if the value +is CANONICAL, it will parse `$regularExpression` and not treat `$regex` specially. - Support both legacy and canonical +forms in the parser without requiring the application to specify one or the other. Making that work for the `$regex` +query operator use case will require that the rules set forth in the 1.0.0 version of this specification are followed +for `$regex`; specifically, that a document with a `$regex` key whose value is a JSON object should be parsed as a +normal document and not reported as an error. + +**Q**. How can implementations which require backwards compatibility with Legacy Extended JSON, in which BSON binary +values were represented like `{"$binary": "AQIDBAU=", "$type": "80"}`, handle parsing of extended JSON test representing +a MongoDB query filter containing the `$type`operator? + +**A**. An implementation can handle this in a number of ways: + +Introduce an enumeration that determines the behavior of the parser. If the value is LEGACY, it will parse the new +binary form and not treat the legacy one specially, and if the value is CANONICAL, it will parse the new form and not +treat the legacy form specially. - Support both legacy and canonical forms in the parser without requiring the +application to specify one or the other. Making that work for the `$type` query operator use case will require that the +rules set forth in the 1.0.0 version of this specification are followed for `$type`; specifically, that a document with +a `$type` key whose value is an integral type, or a document with a `$type` key but without a `$binary` key, should be +parsed as a normal document and not reported as an error. + +**Q**. Sometimes I see the term "extjson" used in other specifications. Is "extjson" related to this specification? + +**A**. Yes, "extjson" is short for "Extended JSON". + +### Changelog + +- 2024-05-29: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter and reformat changelog. +- 2021-05-26: + - Remove any mention of extra dollar-prefixed keys being prohibited in a DBRef. MongoDB 5.0 and compatible drivers no + longer enforce such restrictions. + - Objects that resemble a DBRef without fully complying to its structure should be left as-is during parsing. - + 2020-09-01: Note that `$`-prefixed keys not matching a known type MUST be left as-is when parsing. This is + patch-level change as this behavior was already required in the BSON corpus tests ("Document with keys that start + with `$`"). +- 2020-09-08: + - Added support for parsing `$uuid` fields as BSON Binary subtype 4. + - Changed the example to using the MongoDB Python Driver. It previously used the MongoDB Java Driver. The new example + excludes the following BSON types that are unsupported in Python - `Symbol`,`SpecialFloat`,`DBPointer`, and + `Undefined`. Transformations for these types are now only documented in the [Conversion table](#conversion-table) +- 2017-07-20: + - Bumped specification to version 2.0. + + - Added "Relaxed" format. + + - Changed BSON timestamp type wrapper back to `{"t": *int*, "i": *int*}` for backwards compatibility. (The change in + v1 to unsigned 64-bit string was premature optimization) + + - Changed BSON regular expression type wrapper to `{"$regularExpression": {pattern: *string*, "options": *string*"}}`. + + - Changed BSON binary type wrapper to + `{"$binary": {"base64": , "subType": }}` + + - Added "Restrictions and limitations" section. + + - Clarified parser and generator rules. +- 2017-02-01: Initial specification version 1.0. + +[^1]: This MUST conform to the [Decimal128 specification](../bson-decimal128/decimal128.md#writing-to-extended-json) + +[^2]: BSON Regular Expression options MUST be in alphabetical order. + +[^3]: See [the docs manual](https://www.mongodb.com/docs/manual/reference/glossary/#term-namespace) + +[^4]: See [https://tools.ietf.org/html/rfc3339#section-5.6](https://tools.ietf.org/html/rfc3339#section-5.6) + +[^5]: Fractional seconds SHOULD have exactly 3 decimal places if the fractional part is non-zero. Otherwise, fractional + seconds SHOULD be omitted if zero. + +[^6]: See [the docs manual](https://www.mongodb.com/docs/manual/reference/database-references/#dbrefs) diff --git a/source/extra.css b/source/extra.css new file mode 100644 index 0000000000..4377acd0c9 --- /dev/null +++ b/source/extra.css @@ -0,0 +1,21 @@ +table { + margin-top: 0; + margin-bottom: 1rem; +} + +tr { + border-bottom: solid 1px var(--bs-border-color); +} + +td, th { + vertical-align: top; + padding: 0.4rem 0.8rem 0.4rem 0; +} + +ol li, ul li { + margin-top: 0.25rem; +} + +li > p { + margin-top: 1rem; +} \ No newline at end of file diff --git a/source/faas-automated-testing/faas-automated-testing.md b/source/faas-automated-testing/faas-automated-testing.md new file mode 100644 index 0000000000..30ee924bb8 --- /dev/null +++ b/source/faas-automated-testing/faas-automated-testing.md @@ -0,0 +1,341 @@ +# FaaS Automated Testing + +- Status: +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +This specification is about the ability for drivers to automate tests for "Functions as a Service" from continuous +integration. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### FaaS + +"Function as a Service", such as AWS Lambda. + +### Implementing Automated FaaS Tests + +#### AWS Lambda + +This section describes the required setup of an AWS Lambda function and the steps needed to automate the deployment and +execution of the function in Evergreen. + +##### Local Execution + +###### Prerequisites + +For the initial local setup the following are required: + +- The docker daemon running on the local machine. +- The [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) + +AWS access MUST be configured, either through `$HOME/.aws/credentials` or with the following environment variables: + +- `AWS_ACCESS_KEY_ID` +- `AWS_SECRET_ACCESS_KEY` +- `AWS_REGION` - Set to us-east-1 +- `MONGODB_URI` - The local MongoDB instance + +###### Project Initialization + +Create the new project via SAM and follow the prompts: + +```bash +sam init +``` + +For the template, select "AWS Quick Start Template". + +```bash +Which template source would you like to use? + 1 - AWS Quick Start Templates + 2 - Custom Template Location +Choice: 1 +``` + +For the quick start template, select "Hello World Example". + +```bash +Choose an AWS Quick Start application template + 1 - Hello World Example + 2 - Multi-step workflow + 3 - Serverless API + 4 - Scheduled task + 5 - Standalone function + 6 - Data processing + 7 - Infrastructure event management + 8 - Hello World Example With Powertools + 9 - Serverless Connector Hello World Example + 10 - Multi-step workflow with Connectors + 11 - Lambda EFS example + 12 - DynamoDB Example + 13 - Machine Learning +Template: 1 +``` + +When prompted for language if the driver language is not Python, select "N". + +```text +Use the most popular runtime and package type? (Python and zip) [y/N]: n +``` + +Then select the runtime for your driver: + +```bash +Which runtime would you like to use? + 1 - aot.dotnet7 (provided.al2) + 2 - dotnet6 + 3 - dotnet5.0 + 4 - dotnetcore3.1 + 5 - go1.x + 6 - go (provided.al2) + 7 - graalvm.java11 (provided.al2) + 8 - graalvm.java17 (provided.al2) + 9 - java11 + 10 - java8.al2 + 11 - java8 + 12 - nodejs18.x + 13 - nodejs16.x + 14 - nodejs14.x + 15 - nodejs12.x + 16 - python3.9 + 17 - python3.8 + 18 - python3.7 + 19 - ruby2.7 + 20 - rust (provided.al2) +Runtime: 12 +``` + +Select Zip package type: + +```bash +What package type would you like to use? + 1 - Zip + 2 - Image +Package type: 1 +``` + +Then follow the remaining prompts for the driver language to finish setup. Drivers MAY choose to also enable X-Ray +tracing and CloudWatch Application Insights during these next steps. + +*NOTE* - If the driver wants to skip prompts in the setup it can provide defaults to the sam init command. Example: + +```bash +sam init --name my-hello-world-app \ + --app-template "hello-world" \ + --runtime go1.x \ + --package-type Zip +``` + +###### Function Setup + +In the newly created project directory modify the template.yaml file: + +Change default timeout to 30 seconds: + +```yaml +Globals: + Function: + Timeout: 30 +``` + +Add a root parameter for the MongoDB connection string: + +```yaml +Parameters: + MongoDbUri: + Type: String + Description: The MongoDB connection string. +``` + +Replace all instances in the template.yaml of `HelloWorld` with `MongoDB` and then modify the root `Resources` config to +add the MONGODB_URI env variable reference and change the `CodeUri` to mongodb/ : Then rename the `hello-world` +directory to `mongodb`. Do not change the `Handler` and `Runtime` properties. + +```yaml +Resources: + MongoDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: mongodb/ + Environment: + Variables: + MONGODB_URI: !Ref MongoDbUri +``` + +If the generated template contains Resources.Events.CatchAll.Properties.Path then change it to /mongodb and if it also +contains Resources.Handler modify that to mongodb as well. + +```yaml +Resources: + Events: + CatchAll: + Properties: + Path: /mongodb + Handler: mongodb +``` + +If possible, install the current driver under test into the lambda environment, to avoid having to release the driver in +order to test features or catch regressions. See docs on + for how to create a .zip file deployment +with dependencies. + +Start the local MongoDB instance. If using Docker Desktop on MacOS, set +`MONGODB_URI=mongodb://host.docker.internal:27017` in order for the function to be able to access the host port. + +Run the function locally from the same directory where the template.yaml resides: + +```bash +sam build +sam local invoke --parameter-overrides "MongoDbUri=${MONGODB_URI}" +``` + +*NOTE* "127.0.0.1" in the MONGODB_URI MUST be replaced with "host.docker.internal" to test a local MongoDB deployment. +If "host.docker.internal" does not work (can occur on M1 machines), drivers MAY choose to use a +[bridged docker container](https://docs.docker.com/network/bridge/) to test locally. + +###### Implementing the Function + +Drivers MUST setup the function as would be done in their appropriate language. In the function implementation the +driver MUST: + +- Create a MongoClient that points to MONGODB_URI. +- Add listeners for the following monitoring events: ServerHeartbeatStarted, ServerHeartbeatFailed, CommandSucceeded, + CommandFailed, ConnectionCreated, ConnectionClosed. +- Drivers MUST perform a single insert and then a single delete of the inserted document to force write operations on + the primary node. +- Drivers MUST record the durations and counts of the heartbeats, the durations of the commands, as well as keep track + of the number of open connections, and report this information in the function response as JSON. +- Drivers MUST assert no ServerHeartbeat events contain the `awaited=True` flag to confirm that the streaming protocol + is disabled ([DRIVERS-2578](https://jira.mongodb.org/browse/DRIVERS-2578)). + +###### Running in Continuous Integration + +Running in CI requires Evergreen to be setup to assume the appropriate role in AWS and then execute the script in +drivers-evergreen-tools with the required environment variables. An explanation of the required environment is as +follows: + +| Name | Description | +| ----------------------------- | ----------------------------------- | +| LAMBDA_AWS_ROLE_ARN | The role ARN to assume | +| TEST_LAMBDA_DIRECTORY | The lambda function directory | +| DRIVERS_TOOLS | Location of drivers-evergreen-tools | +| DRIVERS_ATLAS_PUBLIC_API_KEY | The Atlas public API key | +| DRIVERS_ATLAS_PRIVATE_API_KEY | The Atlas private API key | +| DRIVERS_ATLAS_LAMBDA_USER | The Atlas cluster user name | +| DRIVERS_ATLAS_LAMBDA_PASSWORD | The Atlas cluster user password | +| DRIVERS_ATLAS_GROUP_ID | The driver's Atlas group id | +| LAMBDA_STACK_NAME | The driver's Lambda stack name | +| AWS_REGION | The function AWS region | +| AWS_ACCESS_KEY_ID | Assume role automatically sets this | +| AWS_SECRET_ACCESS_KEY | Assume role automatically sets this | +| AWS_SESSION_TOKEN | Assume role automatically sets this | + +The value for `LAMBDA_AWS_ROLE_ARN` MUST be stored in Evergreen project settings. The value can be found in the +`drivers/atlas-qa` vault. + +The values for `DRIVERS_ATLAS_PUBLIC_API_KEY`, `DRIVERS_ATLAS_PRIVATE_API_KEY`, `DRIVERS_ATLAS_LAMBDA_USER`, +`DRIVERS_ATLAS_LAMBDA_PASSWORD` and `DRIVERS_ATLAS_GROUP_ID` can be obtained programmatically from the +`drivers/atlas-qa` vault. + +See +[Secrets Handling README](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/secrets_handling/README.md) +for details on how to access the secrets. + +Supported Evergreen variants that have the AWS SAM CLI installed: + +- ubuntu2204 +- ubuntu1804 +- ubuntu1804-workstation +- ubuntu2204-arm64 +- ubuntu2004-arm64 +- ubuntu1804-arm64 +- rhel90 +- rhel80 +- rhel84 +- rhel90-selinux +- rhel80-selinux +- rhel90-arm64 +- rhel82-arm64 + +This is an example task group in the Evergreen config that accomplishes this, using subprocess.exec to execute scripts +that call the drivers-evergreen-tools functions inside of it for setup, teardown, and execution: + +```yaml +tasks: + - name: "test-aws-lambda-deployed" + commands: + - func: "install dependencies" + - command: ec2.assume_role + params: + role_arn: ${LAMBDA_AWS_ROLE_ARN} + duration_seconds: 3600 + - command: subprocess.exec + params: + working_dir: src + binary: bash + add_expansions_to_env: true + args: + - ${DRIVERS_TOOLS}/.evergreen/aws_lambda/run-deployed-lambda-aws-tests.sh + env: + TEST_LAMBDA_DIRECTORY: ${PROJECT_DIRECTORY}/test/lambda + AWS_REGION: us-east-1 +task_groups: + - name: test_aws_lambda_task_group + setup_group: + - func: fetch source + - command: subprocess.exec + params: + working_dir: src + binary: bash + add_expansions_to_env: true + args: + - ${DRIVERS_TOOLS}/.evergreen/atlas/setup-atlas-cluster.sh + - command: expansions.update + params: + file: src/atlas-expansion.yml + teardown_group: + - command: subprocess.exec + params: + working_dir: src + binary: bash + add_expansions_to_env: true + args: + - ${DRIVERS_TOOLS}/.evergreen/atlas/teardown-atlas-cluster.sh + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 + tasks: + - test-aws-lambda-deployed +``` + +Drivers MUST run the function on a single variant in Evergreen, in order to not potentially hit the Atlas API rate +limit. The variant itself MUST have the SAM CLI installed. + +Description of the behaviour of run-deployed-lambda-aws-tests.sh: + +- Builds the Lambda function locally +- Deploys the Lambda function to AWS. +- Queries for the Lambda function ARN. +- Invokes the Lambda function cold and frozen. +- Initiates a primary failover of the cluster in Atlas. +- Calls the frozen lambda function again. +- Deletes the Lambda function. + +## Changelog + +- 2025-08-07: Added instructions for accessing secrets from the vault. +- 2024-02-27: Migrated from reStructuredText to Markdown. +- 2023-08-21: Drivers MUST assert that the streaming protocol is disabled in the Lambda function. +- 2023-08-17: Fixed URI typo, added host note, increase assume role duration. +- 2023-06-22: Updated evergreen configuration to use task groups. +- 2023-04-14: Added list of supported variants, added additional template config. diff --git a/source/faas-automated-testing/faas-automated-testing.rst b/source/faas-automated-testing/faas-automated-testing.rst new file mode 100644 index 0000000000..be7a7f3aff --- /dev/null +++ b/source/faas-automated-testing/faas-automated-testing.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `faas-automated-testing.md `_. diff --git a/source/find_getmore_killcursors_commands.rst b/source/find_getmore_killcursors_commands.rst index 3b796190a3..0b3b947043 100644 --- a/source/find_getmore_killcursors_commands.rst +++ b/source/find_getmore_killcursors_commands.rst @@ -1,689 +1,4 @@ -.. role:: javascript(code) - :language: javascript -======================================= -Find, getMore and killCursors commands. -======================================= - -:Spec: 137 -:Version: 1.3 -:Title: Find, getMore and killCursors commands -:Author: Christian Kvalheim -:Lead: Christian Kvalheim -:Advisors: \Anna Herlihy, Robert Stam -:Status: Accepted -:Type: Standards -:Minimum Server Version: 3.2 -:Last Modified: October 21, 2015 - -.. contents:: - -Abstract -======== - -The Find, GetMore and KillCursors commands in MongoDB 3.2 or later replace the use of the legacy OP_QUERY, OP_GET_MORE and OP_KILL_CURSORS wire protocol messages. This specification lays out how drivers should interact with the new commands when compared to the legacy wire protocol level messages. - -Definitions -=========== - -Meta ----- - -The keywords "MUST", “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119`_. - -.. _RFC 2119: https://www.ietf.org/rfc/rfc2119.txt - -Terms ------ - -Document -^^^^^^^^ - -:: - - The term Document refers to the implementation in the driver's language of a BSON document. - -Command -^^^^^^^ - -:: - - A BSON document containing the fields making up a MongoDB server command. - -Wire Protocol -^^^^^^^^^^^^^ - -:: - - The binary protocol used to talk with MongoDB over a socket. It’s made up by the OP_QUERY, OP_GET_MORE, OP_KILL_CURSORS, OP_INSERT, OP_UPDATE and OP_DELETE. - -Guidance -======== - -Documentation -------------- - -The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation for the driver. - -Implementation --------------- - -If the **isMaster** command returns **maxWireVersion >= 4**, drivers: - -* MUST implement queries with the ``find`` command instead of ``OP_QUERY``. - -* MUST implement cursor operations with the ``getMore`` and ``killCursors`` commands - instead of ``OP_GET_MORE`` and ``OP_KILL_CURSORS``, respectively. - -* MUST NOT use OP_QUERY except to execute commands. - -Commands -======== - -find ----- - -The **find** command replaces the query functionality of the OP_QUERY wire protocol message but cannot execute queries against special collections. Unlike the legacy OP_QUERY wire protocol message, the **find** command cannot be used to execute other commands. - -.. code:: javascript - - { - "find": , - "filter": { ... }, - "sort": { ... }, - "projection": { ... }, - "hint": { ... }|, - "skip": , - "limit": , - "batchSize": , - "singleBatch": , - "comment": , - "maxScan": , - "maxTimeMS": , - "max": { ... }, - "min": { ... }, - "returnKey": , - "showRecordId": , - "snapshot": , - "tailable": , - "oplogReplay": , - "noCursorTimeout": , - "awaitData": , - "allowPartialResults": , - "readConcern": { ...} - } - -The accepted parameters are described in the table below. Parameters marked "Req" are required by the server and MUST be included in the command. Parameters marked "Def" define the default values assumed by the server if the parameter is omitted]. - -.. list-table:: Find command parameters - :widths: 15 15 15 15 15 30 - :header-rows: 1 - - * - Parameter - - Req - - Def. - - Type - - CRUD API Mapping - - Description - * - find - - X - - - - String - - - - Its argument MUST be a string specifying the name of the collection - * - filter - - X - - - - Doc. - - filter - - The query predicate. - * - sort - - - - - - Doc. - - FindOptions.sort - - If specified, then the result set will be sorted accordingly. The document is in expected to be in ordered form. - * - projection - - - - - - Doc. - - FindOptions.projection - - If provided it specifies the inclusion or exclusion of fields in the returned documents. - * - hint - - - - - - Doc. - String - - modifiers.$hint - - If specified, then the query system will only consider plans using the hinted index. - - If the driver provides a document, it takes the following format - - { field1: <-1/1>, ... fieldN: <-1/1> } - - If the driver provides a string, it is the name of the index to use as the hint. For an index specification {a: 1} this might take the form of the string a_1. - * - skip - - - - 0 - - int64 - - FindOptions.skip - - Specifies the starting point for the returned documents. - * - limit - - - - - - int64 - - FindOptions.limit - - A limit of 0 has the same meaning as the absence of a limit. - * - batchSize - - - - - - int64 - - FindOptions.batchSize - - batchSize specifies the maximum number of documents returned in a find or getMore command. - * - singleBatch - - - - false - - Bool - - - - If true, then the server will return a single batch up to the maximum server message size, and then close the ClientCursor. The client cannot issue any OP_GET_MORE messages or getMore commands. - * - comment - - - - - - String - - FindOptions.comment - - The comment meta-operator makes it possible to attach a comment to a query. - * - maxScan - - - - - - Int32 >= 0 - - modifiers.$maxScan - - Constrains the query to only scan the specified number of documents when fulfilling the query. - * - maxTimeMS - - - - - - Int32 >= 0 - - FindOptions.maxTimeMS - - Specifies a cumulative time limit in milliseconds for processing operations on the cursor - * - max - - - - - - Doc. - - modifiers.$max - - Specify a max value to specify the exclusive upper bound for a specific index in order to constrain the results of find(). The max specifies the upper bound for all keys of a specific index in order. - - The specified document takes the form of - - { field1: , ... fieldN: } - * - min - - - - - - Doc. - - modifiers.$min - - Specify a min value to specify the inclusive lower bound for a specific index in order to constrain the results of find(). The min specifies the lower bound for all keys of a specific index in order. - - The specified document takes the form of - - { field1: , ... fieldN: } - * - returnKey - - - - - - Bool - - modifiers.$returnKey - - Only return the index field or fields for the results of the query. If returnKey is set to true and the query does not use an index to perform the read operation, the returned documents will not contain any fields. - * - showRecordId - - - - - - Bool - - modifiers.$showDiskLoc - - The showRecordId field returns the internal MongoDB record id for each document returned by the query. - * - snapshot - - - - - - Bool - - modifiers.$snapshot - - The snapshot operator prevents the cursor from returning a document more than once because an intervening write operation. - * - tailable - - - - - - Bool - - Set if FindOptions.cursorType is either CursorType.TAILABLE or CursorType.TAILABLE_AWAIT - - Specify that find command MUST return a tailable cursor. - - Can only only be used if the find command is operating over a capped collections. - * - oplogReplay - - - - - - Bool - - FindOptions.oplogReply - - Internal replication use only. - * - noCursorTimeout - - - - - - Bool - - FindOptions.noCursorTimeout - - The server normally times out idle cursors after an inactivity period (10 minutes) to prevent excess memory use. Set this option to prevent that. - * - awaitData - - - - - - Bool - - Set if FindOptions.cursorType is CursorType.TAILABLE_AWAIT - - If True awaitData MUST have tailable. maxTimeMS on getMore can be used to control the amount of time the cursor waits for new documents before returning an empty result. - * - allowPartialResults - - - - - - Bool - - FindOptions.allowPartialResults - - Get partial results from a mongos if some shards are down (instead of throwing an error). - - Drivers MUST NOT send this field if the topology type is not 'Sharded' - * - readConcern - - - - - - Doc - - N/A - - MAY be set on CRUD specification (see readConcern specification for details) - - Allows driver to specify if the query should be performed against a specific snapshot view of the documents in a collection. (N.B. this is not the same as the "snapshot" option, above.) - - The readConcern option takes the following document specification. - { - level: "[majority|local]", - } - - level: “local” is the default, if no level is explicitly specified. - level: “local” means to do a read with no snapshot; this is the behavior of reads in 3.0 and prior versions of MongoDB. - level: “majority” means to do a read from the latest committed snapshot known to the server (which could be stale). - - -For a successful command, the document returned from the server has the following format: - -.. code:: javascript - - { - "cursor": { - "id": , - "ns": , - "firstBatch": [ - ... - ] - }, - "ok": 1 - } - -Special Collection names -^^^^^^^^^^^^^^^^^^^^^^^^ - -The find command **does not support querying on system collections**, so if drivers are using any system collections instead of the inprog, killop, unlock, etc. commands they SHOULD default to using the old-style OP_QUERY. - -Any driver that provides helpers for any of the special collections below SHOULD use the replacement commands if **ismaster.maxWireVersion >= 4** or higher. - -.. list-table:: Special Collection Names - :widths: 15 30 - :header-rows: 1 - - * - Special collection name - - Replacement Command - * - $cmd.sys.inprog - - currentOp - * - $cmd.sys.unlock - - fsyncUnlock - * - .system.indexes - - listIndexes - * - .system.namespaces - - listCollections - -Exhaust -^^^^^^^ - -The **find** command does not support the exhaust flag from **OP_QUERY**. Drivers that support exhaust MUST fallback to existing **OP_QUERY** wire protocol messages. - -Interactions with OP_QUERY -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When sending a find operation as a find command rather than a legacy **OP_QUERY** find only the **slaveOk** flag is honored of the flags available in the **flag** field on the wire protocol. - -For the **find**, **getMore** and **killCursors** commands the **numberToReturn** field SHOULD be -1. To execute **find** commands against a secondary the driver MUST set the **slaveOk** bit for the **find** command to successfully execute. - -The **slaveOk** flag SHOULD not be set for all follow-up **getMore** and **killCursors** commands. The cursor on the server keeps the original **slaveOk** value first set on the **find** command. - -More detailed information about the interaction of the **slaveOk** with **OP_QUERY** can be found in the Server Selection Spec `Passing a Read Preference`_. - -.. _Passing a Read Preference: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#passing-read-preference-to-mongos - -Behavior of Limit, skip and batchSize -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The new **find** command has different semantics to the existing 3.0 and earlier **OP_QUERY** wire protocol message. The **limit** field is a hard limit on the total number of documents returned by the cursor no matter what **batchSize** is provided. - -Once the limit on the cursor has been reached the server will destroy the cursor and return a **cursorId** of **0** in the **OP_REPLY**. This differs from existing **OP_QUERY** behavior where there is no server side concept of limit and where the driver **MUST** keep track of the limit on the client side and **MUST** send a **OP_KILL_CURSORS** wire protocol message when it limit is reached. - -When setting the **batchSize** on the **find** and **getMore** command the value MUST be based on the cursor limit calculations specified in the `CRUD`_ specification. - -In the following example the **limit** is set to **4** and the **batchSize** is set to **3** the following commands are executed. - -.. code:: javascript - - {find: ..., batchSize:3} - {getMore: ..., batchSize:1} - -.. _CRUD: https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#id16 - -If there are not enough documents in the cursor to fulfill the **limit** defined, the cursor runs to exhaustion and is closed, returning a cursorId of 0 to the client. - -Below are are some examples of using **limit**, **skip** and **batchSize**. - -We have 100 documents in the collection **t**. We execute the following **find** command in the shell. - -.. code:: javascript - - var b = db.runCommand({find:"t", limit:20, batchSize:10}); - - db.runCommand({getMore:b.cursor.id, collection:"t", batchSize:20}); - -The **find** command executes and returns the first 10 results. The **getMore** command returns the final 10 results reaching the **limit** of 20 documents. - -The **skip** option works in the same way as the current **OP_QUERY** starting the cursor after skipping **n** number of documents of the query. - -.. code:: javascript - - var b = db.runCommand({find:"t", limit:20, batchSize:10, skip:85}); - - db.runCommand({getMore:b.cursor.id, collection:"t", batchSize:20}); - -The **find** command returns the documents 86-95 and the **getMore** returns the last 5 documents. - -Mapping OP_QUERY behavior to the find command limit and batchSize fields -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The way that limit, batchSize and singleBatch are defined for the find command differs from how these were specified in OP_QUERY and the CRUD spec. - -Specifically, *negative* values for **limit** and **batchSize** are no longer allowed and the **singleBatch** option is used instead of negative values. - -In order to have consistency between old and new applications, the following transformations MUST be performed before adding options to the **find** command: - -.. code:: - - singleBatch = (limit < 0) || (batchSize < 0) - limit = abs(limit) - if singleBatch: - batchSize = (limit == 0) ? abs(batchSize) : limit - else: - batchSize = abs(batchSize) - -Further, after these transformation: - -* If **limit** is zero, it MUST be omitted from **find** options -* If **batchSize** is zero, it MUST be omitted from **find** options -* If **singleBatch** is false, it MUST be omitted from **find** options - -BatchSize of 1 -^^^^^^^^^^^^^^ - -In 3.2 a batchSize of 1 means return a single document for the find command and it will not destroy the cursor after the first batch of documents are returned. Given a query returning 4 documents the number of commands issues will be. - -1. **find** command with batchSize=1 -2. **getMore** command with batchSize=1 -3. **getMore** command with batchSize=1 -4. **getMore** command with batchSize=1 - -The driver **SHOULD NOT attempt to emulate the behavior seen in 3.0 or earlier** as the new find command enables the user expected behavior of allowing the first result to contain a single document when specifying batchSize=1. - -Tailable cursors -^^^^^^^^^^^^^^^^ - -Tailable cursors have some fundamental changes compared to the existing **OP_QUERY** implementation. To create a tailable cursor you execute the following command: - -.. code:: javascript - - var b = db.runCommand({ find:"t", tailable: true }); - -To create a tailable cursor with **tailable** and **awaitData**, execute the following command: - -.. code:: javascript - - var b = db.runCommand({ find:"t", tailable: true, awaitData: true }); - -If **maxTimeMS** is not set in FindOptions, the driver SHOULD refrain from setting **maxTimeMS** on the **find** or **getMore** commands issued by the driver and allow the server to use its internal default value for **maxTimeMS**. - -Semantics of maxTimeMS for a Driver -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the case of a **non-tailable cursor query** OR **a tailable cursor query with awaitData == false**, the driver MUST set maxTimeMS on the **find** command and MUST NOT set maxTimeMS on the **getMore** command. - -In the case of **a tailable cursor with awaitData == true** the driver MUST provide a Cursor level option named **maxAwaitTimeMS** (See CRUD specification for details). The **maxTimeMS** option on the **getMore** command MUST be set to the value of the option **maxAwaitTimeMS**. If no **maxAwaitTimeMS** is specified, the driver SHOULD not set **maxTimeMS** on the **getMore** command. - -getMore -------- - -The **getMore** command replaces the **OP_GET_MORE** wire protocol message. The query flags passed to OP_QUERY for a getMore command MUST be slave_ok=True when sent to a secondary. The OP_QUERY namespace MUST be the same as for the **find** and **killCursors** commands. The command takes the following object. - -.. code:: javascript - - { - "getMore": , - "collection": , - "batchSize": , - "maxTimeMS": - } - -The accepted parameters are described in the table below. - -.. list-table:: getMore command parameters - :widths: 15 15 15 30 - :header-rows: 1 - - * - Parameter - - Req - - Type - - Description - * - getMore - - X - - int64 - - Specifies the cursorid of the ClientCursor that this getMore should exercise. - * - collection - - X - - String - - The name of the collection on which the query is operating. - * - batchSize - - X - - Int32 - - Indicates how many results should be returned in the next batch to the client. Errors if zero or negative. - * - maxTimeMS - - - - Int32 - - If not set, the server defaults to it’s internal maxTimeMS setting. - - Please see the "Semantics of maxTimeMS" section for more details. - -The **batchSize** MUST be an int32 larger than 0. If **batchSize** is equal to 0 it must be omitted. If **batchSize** is less than 0 it must be turned into a positive integer using **Math.abs** or equivalent function in your language. - -On success, the getMore command will return the following: - -.. code:: javascript - - { - "cursor": { - "id": , - "ns": , - "nextBatch": [ - ... - ] - }, - "ok": 1 - } - -killCursors ------------ - -The **killCursors** command replaces the **OP_KILL_CURSORS** wire protocol message. The OP_QUERY namespace MUST be the same as for the **find** and **getMore** commands. The **killCursors** command is optional to implement in **MongoDB 3.2**. - -.. code:: javascript - - { - "killCursors": , - "cursors": [ - - , - … - - ] - } - -The accepted parameters are described in the table below. The query flags passed to OP_QUERY for a killCursors command MUST be slave_ok=True when sent to a secondary. - -.. list-table:: killCursors command parameters - :widths: 15 15 15 30 - :header-rows: 1 - - * - Parameter - - Req - - Type - - Description - * - killCursors - - X - - String - - The collection name used in the find command that created this cursor. - * - cursors - - X - - Array of int64’s - - An array of one or more cursorId’s - -The command response will be as follows: - -.. code:: javascript - - { - "cursorsKilled": [ - - , - … - - ], - "cursorsNotFound": [ - - , - … - - ], - "cursorsAlive": [ - - , - … - - ], - ok: 1 - } - -The **cursorsAlive** array contain cursors that were not possible to kill. The information SHOULD be ignored by the driver. - -Difference from 3.0 OP_KILL_CURSORS -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -One of the differences with the new **killCursors** command compared to the **OP_KILL_CURSORS** wire protocol message is that the **killCursors** command returns a response while the **OP_KILL_CURSORS** wire protocol does not. - -The **OP_REPLY** message has the following general structure. - -.. code:: javascript - - struct { - int32 messageLength; // total message size, including - // this - - int32 requestID; // identifier for this message - - int32 responseTo; // requestID from the original - // request(used in reponses from db) - - int32 opCode; // request type - see table below - - int32 responseFlags; // bit vector - see details below - - int64 cursorID; // cursor id if client needs to do - // get more's - - int32 startingFrom; // where in the cursor this reply is - // starting - - int32 numberReturned; // number of documents in the reply - - document* documents; // documents - } - -For the **find**, **getMore** and **killCursors** MongoDB returns a single document meaning **numberReturned** is set to **1**. This is in contrast to MongoDB 3.0 and earlier where a **OP_QUERY** query will set **numberReturned** to >= 0. - -A driver MUST deserialize the command result and extract the **firstBatch** and **nextBatch** arrays for the **find** and **getMore** commands to access the returned documents. - -The result from the **killCursors** command MAY be safely ignored. - -If the driver supports returning **raw** BSON buffers instead of deserialized documents there might be a need to be able to partially deserialize documents to be able to efficiently provide the behavior in comparison to existing **OP_QUERY** queryresults. - -Errors -====== - -The **find** and **getMore** commands will report errors using the standard mechanism: an "ok: 0" field paired with “errmsg” and “code” fields. See below for example error responses: - -.. code:: shell - - > db.runCommand({find: "t", sort: {padding: -1}}) - -.. code:: javascript - - { - "errmsg" : "exception: Executor error: Overflow sort stage buffered data usage of 41630570 bytes exceeds internal limit of 33554432 bytes", - "code" : 28616, - "ok" : 0 - } - -.. code:: shell - - > db.runCommand({find: "t", foo: "bar"}) - -.. code:: javascript - - { - "ok" : 0, - "errmsg" : "Failed to parse: { find: \"t\", foo: \"bar\" }. Unrecognized field 'foo'.", - "code" : 2 - } - -Like other commands, the find and getMore commands will not use the OP_REPLY response flags. `OP_REPLY Documentation`_ - -.. _OP_REPLY Documentation: http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-reply - -FAQ -=== - -Changes in error handling for 3.2 tailable cursor -------------------------------------------------- - -Tailable cursors pointing to documents in a capped collection that get overwritten will return a zero document result in MongoDB 3.0 or earlier but will return an error in MongoDB 3.2 - -Explain command ---------------- - -There is no equivalent of the $explain modifier in the find command. The driver SHOULD use the **explain** command. Information about the command can be found in the `Explain command reference`_. - -.. _Explain command reference: http://docs.mongodb.org/manual/reference/command/explain/ - -ReadPreference and Mongos -------------------------- - -The **find** command does not include a readPreference field. To pass a readPreference to a **mongos** use the **$readPreference** field and format your command as. - -.. code:: javascript - - {$query: {find: ...}, $readPreference: {}} - -This format is general for all commands when executing against a Mongos proxy. - -More in depth information about passing read preferences to Mongos can be found in the Server Selection Specification `Server Selection Specification`_. - -.. _Server Selection Specification: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#passing-read-preference-to-mongos - -Changes -======= -2015-09-30 slaveOk flag must be set to true on **getMore** and **killCursors** commands to make drivers have same behavior as for OP_GET_MORE and OP_KILL_CURSORS. - -2015-10-13 added guidance on batchSize values as related to the **getMore** command. SlaveOk flag SHOULD not be set on getMore and killCursors commands. Introduced maxAwaitTimeMS option for setting maxTimeMS on getMore commands when the cursor is a tailable cursor with awaitData set. - -2015-10-21 If no **maxAwaitTimeMS** is specified, the driver SHOULD not set **maxTimeMS** on the **getMore** command. +.. note:: + This specification has been converted to Markdown and renamed to + `find_getmore_killcursors_commands.md `_. diff --git a/source/find_getmore_killcursors_commands/find_getmore_killcursors_commands.md b/source/find_getmore_killcursors_commands/find_getmore_killcursors_commands.md new file mode 100644 index 0000000000..251d5b2b38 --- /dev/null +++ b/source/find_getmore_killcursors_commands/find_getmore_killcursors_commands.md @@ -0,0 +1,481 @@ +# Find, getMore and killCursors commands + +- Status: Accepted +- Minimum Server Version: 3.2 + +## Abstract + +The Find, GetMore and KillCursors commands in MongoDB 3.2 or later replace the use of the legacy OP_QUERY, OP_GET_MORE +and OP_KILL_CURSORS wire protocol messages. This specification lays out how drivers should interact with the new +commands when compared to the legacy wire protocol level messages. + +## Definitions + +### Meta + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Terms + +#### Document + +The term Document refers to the implementation in the driver's language of a BSON document. + +#### Command + +A BSON document containing the fields making up a MongoDB server command. + +#### Wire Protocol + +The binary protocol used to talk with MongoDB over a socket. It's made up by the OP_QUERY, OP_GET_MORE, OP_KILL_CURSORS, +OP_INSERT, OP_UPDATE and OP_DELETE. + +## Guidance + +### Documentation + +The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation +for the driver. + +### Implementation + +If the **hello** command returns **maxWireVersion >= 4**, drivers: + +- MUST implement queries with the `find` command instead of `OP_QUERY`. +- MUST implement cursor operations with the `getMore` and `killCursors` commands instead of `OP_GET_MORE` and + `OP_KILL_CURSORS`, respectively. +- MUST NOT use OP_QUERY except to execute commands. + +## Commands + +### find + +The [find](https://www.mongodb.com/docs/manual/reference/command/find/) command replaces the query functionality of the +OP_QUERY wire protocol message but cannot execute queries against special collections. Unlike the legacy OP_QUERY wire +protocol message, the **find** command cannot be used to execute other commands. + +For a successful command, the document returned from the server has the following format: + +```javascript +{ + "cursor": { + "id": , + "ns": , + "firstBatch": [ + ... + ] + }, + "ok": 1 +} +``` + +#### Special Collection names + +The find command **does not support querying on system collections**, so if drivers are using any system collections +instead of the inprog, killop, unlock, etc. commands they SHOULD default to using the old-style OP_QUERY. + +Any driver that provides helpers for any of the special collections below SHOULD use the replacement commands if +**hello.maxWireVersion >=4** or higher. + +| Special collection name | Replacement Command | +| ------------------------------ | ------------------- | +| `$cmd.sys.inprog` | currentOp | +| `$cmd.sys.unlock` | fsyncUnlock | +| `.system.indexes` | listIndexes | +| `.system.namespaces` | listCollections | + +Special Collection Names + +#### Exhaust + +This section only applies to drivers that support exhaust cursors. + +The exhaust protocol differs based on the server version: + +| Server version | Server behavior | +| --------------- | ------------------------------------------------------------------------------------------------------------------------- | +| 4.0 and earlier | Only supports exhaust over legacy **OP_QUERY**. The **find** command does not support the exhaust flag from **OP_QUERY**. | +| 4.2 to 5.0 | Supports exhaust both over legacy **OP_QUERY** and **OP_MSG**. | +| 5.1 and later | Supports exhaust over **OP_MSG**. | + +Therefore drivers that implement exhaust cursors: + +| Server version | Driver behavior | +| --------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| 4.0 and earlier | Drivers MUST use legacy **OP_QUERY**. | +| 4.2 to 5.0 | Drivers SHOULD use **OP_MSG** but MAY use legacy **OP_QUERY**. | +| 5.1 and later | Drivers MUST only use **OP_MSG**. Alternatively, drivers MAY fallback to a non-exhaust cursor when an exhaust cursor is requested. | + +#### Interactions with OP_QUERY + +When sending a find operation as a find command rather than a legacy **OP_QUERY** find only the **secondaryOk** flag is +honored of the flags available in the **flag** field on the wire protocol. + +For the **find**, **getMore** and **killCursors** commands the **numberToReturn** field SHOULD be -1. To execute +**find** commands against a secondary the driver MUST set the **secondaryOk** bit for the **find** command to +successfully execute. + +The **secondaryOk** flag SHOULD not be set for all follow-up **getMore** and **killCursors** commands. The cursor on the +server keeps the original **secondaryOk** value first set on the **find** command. + +More detailed information about the interaction of the **secondaryOk** with **OP_QUERY** can be found in the Server +Selection Spec [Passing a Read Preference](../server-selection/server-selection.md#passing-read-preference-to-mongos). + +#### Behavior of Limit, skip and batchSize + +The **find** command has different semantics to the existing 3.0 and earlier **OP_QUERY** wire protocol message. The +**limit** field is a hard limit on the total number of documents returned by the cursor no matter what **batchSize** is +provided. This includes other limiting operations, such as the **$limit** aggregation pipeline stage. This differs from +existing **OP_QUERY** behavior where there is no server-side concept of limit and where the driver **MUST** keep track +of the limit on the client side and **MUST** send a **OP_KILL_CURSORS** wire protocol message when the limit is reached. + +When setting the **batchSize** on the **find** and **getMore** commands the value of **batchSize** **MUST** be based on +the cursor limit calculations specified in the [CRUD](../crud/crud.md#find) specification. + +Note that with 5.0, the server-side handling of cursors with a limit has changed. Before 5.0, some cursors were +automatically closed when the limit was reached (e.g. when running **find** with **limit**), and the reply document did +not include a cursor ID (i.e. `cursor.id` was `0`). Starting with 5.0, all cursor-producing operations will return a +cursor ID if the end of the batch being returned lines up with the limit on the cursor. In this case, drivers **MUST** +ensure the cursor is closed on the server, either by exhausting the cursor or by using **killCursors** to kill it. + +In the following example the **limit** is set to **4** and the **batchSize** is set to **3** the following commands are +executed. The last command is either **killCursors** or **getMore**, depending on how a driver ensures the cursor is +closed on 5.0: + +```javascript +{find: ..., batchSize:3, limit:4} +{getMore: ..., batchSize:1} // Returns remaining items but leaves cursor open on 5.0+ +{...} // Kills server-side cursor. Necessary on 5.0+ +``` + +If there are not enough documents in the cursor to fulfill the **limit** defined, the cursor runs to exhaustion and is +closed, returning a cursorId of 0 to the client. + +Below are are some examples of using **limit**, **skip** and **batchSize**. + +We have 100 documents in the collection **t**. We execute the following **find** command in the shell. + +```javascript +var b = db.runCommand({find:"t", limit:20, batchSize:10}); +db.runCommand({getMore:b.cursor.id, collection:"t", batchSize:20}); +``` + +The **find** command executes and returns the first 10 results. The **getMore** command returns the final 10 results +reaching the **limit** of 20 documents. + +The **skip** option works in the same way as the current **OP_QUERY** starting the cursor after skipping **n** number of +documents of the query. + +```javascript +var b = db.runCommand({find:"t", limit:20, batchSize:10, skip:85}); +db.runCommand({getMore:b.cursor.id, collection:"t", batchSize:20}); +``` + +The **find** command returns the documents 86-95 and the **getMore** returns the last 5 documents. + +#### Mapping OP_QUERY behavior to the find command limit and batchSize fields + +The way that limit, batchSize and singleBatch are defined for the find command differs from how these were specified in +OP_QUERY and the CRUD spec. + +Specifically, *negative* values for **limit** and **batchSize** are no longer allowed and the **singleBatch** option is +used instead of negative values. + +In order to have consistency between old and new applications, the following transformations MUST be performed before +adding options to the **find** command: + +```text +singleBatch = (limit < 0) || (batchSize < 0) +limit = abs(limit) +if singleBatch: + batchSize = (limit == 0) ? abs(batchSize) : limit +else: + batchSize = abs(batchSize) +``` + +Further, after these transformation: + +- If **limit** is zero, it MUST be omitted from **find** options +- If **batchSize** is zero, it MUST be omitted from **find** options +- If **singleBatch** is false, it MUST be omitted from **find** options + +#### BatchSize of 1 + +In 3.2 a batchSize of 1 means return a single document for the find command and it will not destroy the cursor after the +first batch of documents are returned. Given a query returning 4 documents the number of commands issues will be. + +1. **find** command with batchSize=1 +2. **getMore** command with batchSize=1 +3. **getMore** command with batchSize=1 +4. **getMore** command with batchSize=1 + +The driver **SHOULD NOT attempt to emulate the behavior seen in 3.0 or earlier** as the new find command enables the +user expected behavior of allowing the first result to contain a single document when specifying batchSize=1. + +#### Tailable cursors + +By default, most cursors are non-tailable. For example, a typical `find` cursor is exhausted when all results for the +filter have been returned. MongoDB also supports creating cursors that "tail" or follow the target namespace for new +data. This is done via the `find` command's `tailable` option. Querying a capped collection is one use case for a +tailable cursor. A tailable cursor can receive `getMore` responses with an empty `nextBatch` array, which does not +indicate that the cursor has been exhausted. + +Additionally, tailable cursors may "await data" from the server, which means that the server will wait to respond to the +client's `getMore` command until new data is available or some period of time has elapsed, whichever comes first. A +tailable `find` cursor can be configured using the `awaitData` option. The amount of time the server will wait for new +data is based on the `maxTimeMS` field of the `getMore` (or server default if unspecified). If the time does expire an +empty batch will be returned. A `maxTimeMS` field cannot be sent if the cursor was not configured with `awaitData=true`. + +To create a tailable `find` cursor you execute the following command: + +```typescript +interface FindCommand { + /** The namespace to run the find command on */ + find: string; + /** The filter to control what documents are returned */ + filter: BSONDocument; + /** Informs the server whether to keep the cursor open even when there are no results to satisfy the query */ + tailable?: boolean; + /** + * Informs the server whether to block on a `getMore`'s `maxTimeMS` before returning an empty `nextBatch`. + * This must be set if getMores are to include `maxTimeMS` values. + */ + awaitData?: boolean; + /** Controls the amount of milliseconds the server will allow the operations to run for */ + maxTimeMS?: PositiveIntegerNumber; + // ... Note: additional options unrelated to tailable cursors omitted +} +``` + +If **maxTimeMS** is not set in FindOptions, the driver SHOULD refrain from setting **maxTimeMS** on the **find** or +**getMore** commands issued by the driver and allow the server to use its internal default value for **maxTimeMS**. + +#### Semantics of maxTimeMS for a Driver + +In the case of a **non-tailable cursor query** OR **a tailable cursor query with awaitData == false**, the driver MUST +set maxTimeMS on the **find** command and MUST NOT set maxTimeMS on the **getMore** command. + +In the case of **a tailable cursor with awaitData == true** the driver MUST provide a Cursor level option named +**maxAwaitTimeMS** (See CRUD specification for details). The **maxTimeMS** option on the **getMore** command MUST be set +to the value of the option **maxAwaitTimeMS**. If no **maxAwaitTimeMS** is specified, the driver SHOULD not set +**maxTimeMS** on the **getMore** command. + +### getMore + +The [getMore](https://www.mongodb.com/docs/manual/reference/command/getMore/) command replaces the **OP_GET_MORE** wire +protocol message. The query flags passed to OP_QUERY for a getMore command MUST be secondaryOk=true when sent to a +secondary. The OP_QUERY namespace MUST be the same as for the **find** and **killCursors** commands. + +```typescript +interface GetMoreCommand { + /** Set to the nonzero cursor id */ + getMore: int64; + /** Set to the namespace returned by the initial command response */ + collection: string; + /** + * User configurable document count for the batch returned for this getMore. + * Only attached to command document if nonzero. + */ + batchSize?: PositiveIntegerNumber; + /** + * User configurable time limit enforced by the server. + */ + maxTimeMS?: PositiveIntegerNumber; + /** + * User configurable comment that can be used to identify the operation in logs. + * This can be any BSON value. + */ + comment?: BSONValue; +} +``` + +On success, the getMore command will return the following: + +```typescript +interface GetMoreResponse { + ok: 1; + cursor: { + /** The cursor id, this may be equal to zero indicating the cursor is exhausted or closed */ + id: int64; + /** + * The namespace the cursor is running on. + * This value may be different than the namespace the driver started the cursor on, for example, database level aggregations. + */ + ns: string; + /** + * The next batch of documents. + * This array may be empty, in the case of a tailable cursor, which DOES NOT indicate the cursor is exhausted. + */ + nextBatch: BSONArray; + } + // ... Note: additional non-relevant fields omitted +} +``` + +The driver's local cursor MUST update its `id` and `ns`, as well as store the `nextBatch` from every `getMore` response. + +### killCursors + +The [killCursors](https://www.mongodb.com/docs/manual/reference/command/killCursors/) command replaces the +**OP_KILL_CURSORS** wire protocol message. The OP_QUERY namespace MUST be the same as for the **find** and **getMore** +commands. The **killCursors** command is optional to implement in **MongoDB 3.2**. + +The command response will be as follows: + +```typescript +{ + "cursorsKilled": [ + + , + ... + + ], + "cursorsNotFound": [ + + , + ... + + ], + "cursorsAlive": [ + + , + ... + + ], + ok: 1 +} +``` + +The **cursorsAlive** array contain cursors that were not possible to kill. The information SHOULD be ignored by the +driver. + +#### Difference from 3.0 OP_KILL_CURSORS + +One of the differences with the new **killCursors** command compared to the **OP_KILL_CURSORS** wire protocol message is +that the **killCursors** command returns a response while the **OP_KILL_CURSORS** wire protocol does not. + +The **OP_REPLY** message has the following general structure. + +```c +struct { + int32 messageLength; // total message size, including + // this + + int32 requestID; // identifier for this message + + int32 responseTo; // requestID from the original + // request(used in responses from db) + + int32 opCode; // request type - see table below + + int32 responseFlags; // bit vector - see details below + + int64 cursorID; // cursor id if client needs to do + // get more's + + int32 startingFrom; // where in the cursor this reply is + // starting + + int32 numberReturned; // number of documents in the reply + + document* documents; // documents +} +``` + +For the **find**, **getMore** and **killCursors** MongoDB returns a single document meaning **numberReturned** is set to +**1**. This is in contrast to MongoDB 3.0 and earlier where a **OP_QUERY** query will set **numberReturned** to >= 0. + +A driver MUST deserialize the command result and extract the **firstBatch** and **nextBatch** arrays for the **find** +and **getMore** commands to access the returned documents. + +The result from the **killCursors** command MAY be safely ignored. + +If the driver supports returning **raw** BSON buffers instead of deserialized documents there might be a need to be able +to partially deserialize documents to be able to efficiently provide the behavior in comparison to existing **OP_QUERY** +queryresults. + +## Errors + +The **find** and **getMore** commands will report errors using the standard mechanism: an "ok: 0" field paired with +"errmsg" and "code" fields. See below for example error responses: + +```javascript +db.runCommand({find: "t", sort: {padding: -1}}) +``` + +```javascript +{ + "errmsg" : "exception: Executor error: Overflow sort stage buffered data usage of 41630570 bytes exceeds internal limit of 33554432 bytes", + "code" : 28616, + "ok" : 0 +} +``` + +```javascript +db.runCommand({find: "t", foo: "bar"}) +``` + +```javascript +{ + "ok" : 0, + "errmsg" : "Failed to parse: { find: \"t\", foo: \"bar\" }. Unrecognized field 'foo'.", + "code" : 2 +} +``` + +Like other commands, the find and getMore commands will not use the OP_REPLY response flags. +[OP_REPLY Documentation](https://www.mongodb.com/docs/meta-driver/latest/legacy/mongodb-wire-protocol/#op-reply) + +## FAQ + +### Changes in error handling for 3.2 tailable cursor + +Tailable cursors pointing to documents in a capped collection that get overwritten will return a zero document result in +MongoDB 3.0 or earlier but will return an error in MongoDB 3.2 + +### Explain command + +There is no equivalent of the `$explain` modifier in the find command. The driver SHOULD use the **explain** command. +Information about the command can be found in the +[Explain command reference](https://www.mongodb.com/docs/manual/reference/command/explain/). + +### ReadPreference and Mongos + +The **find** command does not include a readPreference field. To pass a readPreference to a **mongos** use the +**$readPreference** field and format your command as. + +```javascript +{$query: {find: ...}, $readPreference: {}} +``` + +This format is general for all commands when executing against a Mongos proxy. + +More in depth information about passing read preferences to Mongos can be found in the Server Selection Specification +[Server Selection Specification](../server-selection/server-selection.md#passing-read-preference-to-mongos). + +## Changelog + +- 2024-07-30: Migrated from reStructuredText to Markdown. + +- 2023-05-10: Improve tailable cursor description and update the `getMore` section code blocks. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-02-01: Replace examples/tables for find, getMore, and killCursors with server manual links. + +- 2021-12-14: Exhaust cursors may fallback to non-exhaust cursors on 5.1+ servers. Relax requirement of OP_MSG for + exhaust cursors. + +- 2021-08-27: Exhaust cursors must use OP_MSG on 3.6+ servers. + +- 2021-04-06: Updated to use hello and secondaryOk. + +- 2015-10-21: If no **maxAwaitTimeMS** is specified, the driver SHOULD not set **maxTimeMS** on the **getMore** command. + +- 2015-10-13: Added guidance on batchSize values as related to the **getMore** command. Legacy secondaryOk flag SHOULD + not be set on getMore and killCursors commands. Introduced maxAwaitTimeMS option for setting maxTimeMS on getMore + commands when the cursor is a tailable cursor with awaitData set. + +- 2015-09-30: Legacy secondaryOk flag must be set to true on **getMore** and **killCursors** commands to make drivers + have same behavior as for OP_GET_MORE and OP_KILL_CURSORS. diff --git a/source/gridfs/gridfs-spec.md b/source/gridfs/gridfs-spec.md new file mode 100644 index 0000000000..e7fe407df2 --- /dev/null +++ b/source/gridfs/gridfs-spec.md @@ -0,0 +1,1105 @@ +# GridFS Spec + +- Status: Accepted +- Minimum Server Version: 2.2 + +______________________________________________________________________ + +## Abstract + +GridFS is a convention drivers use to store and retrieve BSON binary data (type "x05") that exceeds MongoDB's +BSON-document size limit of 16 MiB. When this data, called a **user file**, is written to the system, GridFS divides the +file into **chunks** that are stored as distinct documents in a **chunks collection**. To retrieve a stored file, GridFS +locates and returns all of its component chunks. Internally, GridFS creates a **files collection document** for each +stored file. Files collection documents hold information about stored files, and they are stored in a **files +collection**. + +This spec defines a basic API for GridFS. This spec also outlines advanced GridFS features that drivers can choose to +support in their implementations. Additionally, this document attempts to clarify the meaning and purpose of all fields +in the GridFS data model, disambiguate GridFS terminology, and document configuration options that were previously +unspecified. + +## Definitions + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Terms + +**Bucket name** + +A prefix under which a GridFS system's collections are stored. Collection names for the files and chunks collections are +prefixed with the bucket name. The bucket name MUST be configurable by the user. Multiple buckets may exist within a +single database. The default bucket name is "fs". + +**Chunk** + +A section of a user file, stored as a single document in the "chunks" collection of a GridFS bucket. The default size +for the data field in chunks is 255 KiB. Chunk documents have the following form: + +```javascript +{ + "_id" : , + "files_id" : , + "n" : , + "data" : +} +``` + +**\_id** + +a unique ID for this document of type BSON ObjectId + +**files_id** + +the id for this file (the `_id` from the files collection document). This field takes the type of the corresponding +`_id` in the files collection. + +**\_id** + +the index number of this chunk, zero-based. + +**data** + +a chunk of data from the user file + +**Chunks collection** + +A collection in which chunks of a user file are stored. The name for this collection is the word 'chunks' prefixed by +the bucket name. The default is "fs.chunks". + +**Empty chunk** + +A chunk with a zero length "data" field. + +**Files collection** + +A collection in which information about stored files is stored. There will be one files collection document per stored +file. The name for this collection is the word "files" prefixed by the bucket name. The default is "fs.files". + +**Files collection document** + +A document stored in the files collection that contains information about a single stored file. Files collection +documents have the following form: + +```javascript +{ + "_id" : , + "length" : , + "chunkSize" : , + "uploadDate" : , + "md5" : , + "filename" : , + "contentType" : , + "aliases" : , + "metadata" : +} +``` + +**\_id** + +a unique ID for this document. Usually this will be of type ObjectId, but a custom `_id` value provided by the +application may be of any type. + +**length** + +the length of this stored file, in bytes + +**chunkSize** + +the size, in bytes, of each data chunk of this file. This value is configurable by file. The default is 255 KiB. + +**uploadDate** + +the date and time this file was added to GridFS, stored as a BSON datetime value. The value of this field MUST be the +datetime when the upload completed, not the datetime when it was begun. + +**md5** + +DEPRECATED, a hash of the contents of the stored file + +**filename** + +the name of this stored file; this does not need to be unique + +**contentType** + +DEPRECATED, any MIME type, for application use only + +**aliases** + +DEPRECATED, for application use only + +**metadata** + +any additional application data the user wishes to store + +Note: some older versions of GridFS implementations allowed applications to add arbitrary fields to the files collection +document at the root level. New implementations of GridFS will not allow this, but must be prepared to handle existing +files collection documents that might have additional fields. + +Note: drivers SHOULD store length as Int64 and chunkSize as Int32 when creating new GridFS files. However, drivers MUST +be able to handle existing GridFS files where the length and chunkSize fields might have been stored using a different +numeric data type. + +**Orphaned chunk** + +A document in the chunks collections for which the "files_id" does not match any `_id` in the files collection. Orphaned +chunks may be created if write or delete operations on GridFS fail part-way through. + +**Stored File** + +A user file that has been stored in GridFS, consisting of a files collection document in the files collection and zero +or more documents in the chunks collection. + +**Stream** + +An abstraction that represents streamed I/O. In some languages a different word is used to represent this abstraction. + +**TFileId** + +While GridFS file id values are ObjectIds by default, an application may choose to use custom file id values, which may +be of any type. In this spec the term TFileId refers to whatever data type is appropriate in the driver's programming +language to represent a file id. This would be something like object, BsonValue or a generic `` type parameter. + +**User File** + +A data added by a user to GridFS. This data may map to an actual file on disk, a stream of input, a large data object, +or any other large amount of consecutive data. + +## Specification + +### Guidance + +#### Documentation + +The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation +for the driver. + +#### Operations + +All drivers MUST offer the Basic API operations defined in the following sections and MAY offer the Advanced API +operations. This does not preclude a driver from offering more. + +#### Operation Parameters + +All drivers MUST offer the same options for each operation as defined in the following sections. This does not preclude +a driver from offering more. The options parameter is optional. A driver SHOULD NOT require a user to specify optional +parameters. + +#### Deviations + +A non-exhaustive list of acceptable deviations are as follows: + +- Using named parameters instead of an options hash. For instance, + + ```javascript + id = bucket.upload_from_stream(filename, source, chunkSizeBytes: 16 * 1024); + ``` + +- Using a fluent style for constructing a GridFSBucket instance: + + ```javascript + bucket = new GridFSBucket(database) + .withReadPreference(ReadPreference.Secondary); + ``` + +When using a fluent-style builder, all options should be named rather than inventing a new word to include in the +pipeline (like options). Required parameters are still required to be on the initiating constructor. + +#### Timeouts + +Drivers MUST enforce timeouts for all operations per +[Client Side Operations Timeout: GridFS API](../client-side-operations-timeout/client-side-operations-timeout.md#gridfs-api). + +### Naming + +All drivers MUST name operations, objects, and parameters as defined in the following sections. + +Deviations are permitted as outlined below. + +#### Deviations + +When deviating from a defined name, an author should consider if the altered name is recognizable and discoverable to +the user of another driver. + +A non-exhaustive list of acceptable naming deviations are as follows: + +- Using "bucketName" as an example, Java would use "bucketName" while Python would use "bucket_name". However, calling + it "bucketPrefix" would not be acceptable. +- Using "maxTimeMS" as an example, .NET would use "MaxTime" where its type is a TimeSpan structure that includes units. + However, calling it "MaximumTime" would not be acceptable. +- Using "GridFSUploadOptions" as an example, Javascript wouldn't need to name it while other drivers might prefer to + call it "GridFSUploadArgs" or "GridFSUploadParams". However, calling it "UploadOptions" would not be acceptable. +- Languages that use a different word than "Stream" to represent a streamed I/O abstraction may replace the word + "Stream" with their language's equivalent word. For example, open_upload_stream might be called open_upload_file or + open_upload_writer if appropriate. +- Languages that support overloading MAY shorten the name of some methods as appropriate. For example, + download_to_stream and download_to_stream_by_name MAY be overloaded download_to_stream methods with different + parameter types. Implementers are encouraged not to shorten method names unnecessarily, because even if the shorter + names are not ambiguous today they might become ambiguous in the future as new features are added. + +## API + +This section presents two groups of features, a basic API that a driver MUST implement, and a more advanced API that +drivers MAY choose to implement additionally. + +## Basic API + +### Configurable GridFSBucket class + +```javascript +class GridFSBucketOptions { + + /** + * The bucket name. Defaults to 'fs'. + */ + bucketName : String optional; + + /** + * The chunk size in bytes. Defaults to 255 KiB. + */ + chunkSizeBytes : Int32 optional; + + /** + * The write concern. Defaults to the write concern of the database. + */ + writeConcern : WriteConcern optional; + + /** + * The read concern. Defaults to the read concern of the database. + */ + readConcern : ReadConcern optional; + + /** + * The read preference. Defaults to the read preference of the database. + */ + readPreference : ReadPreference optional; + + /** + * TRANSITIONAL: This option is provided for backwards compatibility. + * It MUST be supported while a driver supports MD5 and MUST be removed + * (or made into a no-op) when a driver removes MD5 support entirely. + * When true, the GridFS implementation will not compute MD5 checksums + * of uploaded files. Defaults to false. + */ + disableMD5: Boolean +} + +class GridFSBucket { + + /** + * Create a new GridFSBucket object on @db with the given @options. + */ + GridFSBucket new(Database db, GridFSBucketOptions options=null); + +} +``` + +Creates a new GridFSBucket object, managing a GridFS bucket within the given database. + +GridFSBucket objects MUST allow the following options to be configurable: + +- **bucketName:** the name of this GridFS bucket. The files and chunks collection for this GridFS bucket are prefixed by + this name followed by a dot. Defaults to "fs". This allows multiple GridFS buckets, each with a unique name, to + exist within the same database. +- **chunkSizeBytes:** the number of bytes stored in chunks for new user files added through this GridFSBucket object. + This will not reformat existing files in the system that use a different chunk size. Defaults to 255 KiB. + +IF a driver supports configuring readConcern, readPreference or writeConcern at the database or collection level, then +GridFSBucket objects MUST also allow the following options to be configurable: + +- **readConcern:** defaults to the read concern on the parent database (or client object if the parent database has no + read concern). +- **readPreference:** defaults to the read preference on the parent database (or client object if the parent database + has no read preference). +- **writeConcern:** defaults to the write concern on the parent database (or client object if the parent database has no + write concern). + +The following option is transitional: + +- **disableMD5:** this allows users to disable MD5 when operating under FIPS restrictions. It is provided to allow a + transition period as drivers remove MD5 support. Until a driver removes MD5 support, drivers MUST support this + option. Following a driver's normal feature removal cycle, when MD5 support is removed, this option MUST be removed + or otherwise made into a no-op option. + +GridFSBucket instances are immutable. Their properties MUST NOT be changed after the instance has been created. If your +driver provides a fluent way to provide new values for properties, these fluent methods MUST return new instances of +GridFSBucket. + +### Indexes + +For efficient execution of various GridFS operations the following indexes MUST exist: + +- an index on { filename : 1, uploadDate : 1 } on the files collection +- a unique index on { files_id : 1, n : 1 } on the chunks collection + +Normally we leave it up to the user to create whatever indexes they see fit, but because GridFS is likely to be looked +at as a black box we should create these indexes automatically in a way that involves the least amount of overhead +possible. + +#### Before read operations + +For read operations, drivers MUST assume that the proper indexes exist. + +#### Before write operations + +Immediately before the **first** write operation on an instance of a GridFSBucket class is attempted (and not earlier), +drivers MUST: + +- determine if the files collection is empty using the primary read preference mode. +- and if so, create the indexes described above if they do not already exist + +To determine whether the files collection is empty drivers SHOULD execute the equivalent of the following shell command: + +> > db.fs.files.findOne({}, { \_id : 1 }) + +If no document is returned the files collection is empty. + +This method of determining whether the files collection is empty should perform better than checking the count in the +case where the files collection is sharded. + +Drivers MUST check whether the indexes already exist before attempting to create them. This supports the scenario where +an application is running with read-only authorizations. + +When checking whether an index exists drivers MUST compare numeric values by value even when they are of different +types, because the actual type will depend on how the index was created (for example, indexes created using the shell +will have double as the type for numeric values). + +For example, the following index specifications should all be treated as equivalent: + +- { filename : 1, uploadDate : 1 } // where 1 is either a 32-bit or 64-bit integer +- { filename : 1, uploadDate : 1.0 } +- { filename : 1.0, uploadDate : 1 } +- { filename : 1.0, uploadDate : 1.0 } + +If a driver determines that it should create the indexes, it MUST raise an error if the attempt to create the indexes +fails. + +Drivers MUST create the indexes in foreground mode. + +### File Upload + +```javascript +class GridFSUploadOptions { + + /** + * The number of bytes per chunk of this file. Defaults to the + * chunkSizeBytes in the GridFSBucketOptions. + */ + chunkSizeBytes : Int32 optional; + + /** + * User data for the 'metadata' field of the files collection document. + * If not provided the driver MUST omit the metadata field from the + * files collection document. + */ + metadata : Document optional; + + /** + * DEPRECATED: A valid MIME type. If not provided the driver MUST omit the + * contentType field from the files collection document. + * + * Applications wishing to store a contentType should add a contentType field + * to the metadata document instead. + */ + contentType : String optional; + + /** + * DEPRECATED: An array of aliases. If not provided the driver MUST omit the + * aliases field from the files collection document. + * + * Applications wishing to store aliases should add an aliases field to the + * metadata document instead. + */ + aliases: String[] optional; + +} + +class GridFSBucket { + + /** + * Opens a Stream that the application can write the contents of the file to. + * The driver generates the file id. + * + * Returns a Stream to which the application will write the contents. + * + * Note: this method is provided for backward compatibility. In languages + * that use generic type parameters, this method may be omitted since + * the TFileId type might not be an ObjectId. + */ + Stream open_upload_stream(string filename, GridFSUploadOptions options=null); + + /** + * Opens a Stream that the application can write the contents of the file to. + * The application provides a custom file id. + * + * Returns a Stream to which the application will write the contents. + */ + Stream open_upload_stream_with_id(TFileId id, string filename, GridFSUploadOptions options=null); + + /** + * Uploads a user file to a GridFS bucket. The driver generates the file id. + * + * Reads the contents of the user file from the @source Stream and uploads it + * as chunks in the chunks collection. After all the chunks have been uploaded, + * it creates a files collection document for @filename in the files collection. + * + * Returns the id of the uploaded file. + * + * Note: this method is provided for backward compatibility. In languages + * that use generic type parameters, this method may be omitted since + * the TFileId type might not be an ObjectId. + */ + ObjectId upload_from_stream(string filename, Stream source, GridFSUploadOptions options=null); + + /** + * Uploads a user file to a GridFS bucket. The application supplies a custom file id. + * + * Reads the contents of the user file from the @source Stream and uploads it + * as chunks in the chunks collection. After all the chunks have been uploaded, + * it creates a files collection document for @filename in the files collection. + * + * Note: there is no need to return the id of the uploaded file because the application + * already supplied it as a parameter. + */ + void upload_from_stream_with_id(TFileId id, string filename, Stream source, GridFSUploadOptions options=null); +} +``` + +Uploads a user file to a GridFS bucket. For languages that have a Stream abstraction, drivers SHOULD use that Stream +abstraction. For languages that do not have a Stream abstraction, drivers MUST create an abstraction that supports +streaming. + +In the case of open_upload_stream, the driver returns a Stream to which the application will write the contents of the +file. As the application writes the contents to the returned Stream, the contents are uploaded as chunks in the chunks +collection. When the application signals it is done writing the contents of the file by calling close (or its +equivalent) on the returned Stream, a files collection document is created in the files collection. Once the Stream has +been closed (and the files collection document has been created) a driver MUST NOT allow further writes to the upload +Stream. + +The driver MUST make the Id of the new file available to the caller. Typically a driver SHOULD make the Id available as +a property named Id on the Stream that is returned. In languages where that is not idiomatic, a driver MUST make the Id +available in a way that is appropriate for that language. + +In the case of upload_from_stream, the driver reads the contents of the user file by consuming the the source Stream +until end of file is reached. The driver does NOT close the source Stream. + +Drivers MUST take an "options" document with configurable parameters. Drivers for dynamic languages MUST ignore any +unrecognized fields in the options for this method (this does not apply to drivers for static languages which define an +Options class that by definition only contains valid fields). + +Note that in GridFS, "filename" is not a unique identifier. There may be many stored files with the same filename stored +in a GridFS bucket under different ids. Multiple stored files with the same filename are called 'revisions', and the +'uploadDate' is used to distinguish newer revisions from older ones. + +**Implementation details:** + +If "chunkSizeBytes" is set through the options, that value MUST be used as the chunk size for this stored file. If this +parameter is not specified, the default chunkSizeBytes setting for this GridFSBucket object MUST be used instead. + +To store a user file, the file must have a unique id. In some cases the driver can generate a unique ObjectId to serve +as the id for the file being uploaded. Otherwise the application provides the value. Drivers store the contents of the +user file in the chunks collection by breaking up the contents into chunks of size "chunkSizeBytes". For a non-empty +user file, for each nth section of the file, drivers create a chunk document and set its fields as follows: + +- Files_id: the id generated for this stored file. +- N: this is the nth section of the stored file, zero based. +- Data: a section of file data, stored as BSON binary data with subtype 0x00. All chunks except the last one must be + exactly 'chunkSizeBytes' long. The last chunk can be smaller, and should only be as large as necessary. + +Historically, while streaming the user file, drivers computed an MD5 digest for the (now deprecated) 'md5' field of the +files collection document. If drivers preserve this behavior for backwards compatibility, they MUST provide the +'disableMD5' member of GridFSBucketOptions. When 'disableMD5' is true, drivers MUST NOT compute an MD5 digest or include +it in the files collection document. If drivers no longer support the deprecated 'md5' field, they MUST NOT provide the +'disableMD5' member (or it MUST be a no-op) and MUST NOT compute MD5. + +After storing all chunk documents generated for the user file in the "chunks" collection, drivers create a files +collection document for the file and store it in the files collection. The fields in the files collection document are +set as follows: + +| Name | Description | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| length | the length of this stored file, in bytes. | +| chunksize | the chunk size in bytes used to break the user file into chunks. While the configuration option is named "chunkSizeBytes" for clarity, for legacy reasons, the files collection document uses only "chunkSize". | +| uploaddate | a BSON datetime object for the current time, in UTC, when the files collection document was created. | +| md5 | MD5 checksum for this user file, computed from the file's data, stored as a hex string, if computed, otherwise omitted. | +| filename | the filename passed to this function, UTF-8 encoded. | +| contenttype | the "contentType" passed in the options, if provided; otherwise omitted. | +| aliases | the array passed in the options, if provided; otherwise omitted. | +| metadata | the "metadata" document passed in the options, if provided; otherwise omitted. | + +If a user file contains no data, drivers MUST still create a files collection document for it with length set to zero. +Drivers MUST NOT create any empty chunks for this file. + +Drivers MUST NOT run the 'filemd5' database command to confirm that all chunks were successfully uploaded. We assume +that if none of the inserts failed then the chunks must have been successfully inserted, and running the 'filemd5' +command would just be unnecessary overhead and doesn't work for sharded chunk keys anyway. + +**Operation Failure** + +If any of the above operations fail against the server, drivers MUST raise an error. If some inserts succeeded before +the failed operation, these become orphaned chunks. Drivers MUST NOT attempt to clean up these orphaned chunks. The +rationale is that whatever failure caused the orphan chunks will most likely also prevent cleaning up the orphaned +chunks, and any attempts to clean up the orphaned chunks will simply cause long delays before reporting the original +failure to the application. + +**Aborting an upload** + +Drivers SHOULD provide a mechanism to abort an upload. When using open_upload_stream, the returned Stream SHOULD have an +Abort method. When using upload_from_stream, the upload will be aborted if the source stream raises an error. + +When an upload is aborted any chunks already uploaded MUST be deleted. Note that this differs from the case where an +attempt to insert a chunk fails, in which case drivers immediately report the failure without attempting to delete any +chunks already uploaded. + +Abort MUST raise an error if it is unable to successfully abort the upload (for example, if an error occurs while +deleting any chunks already uploaded). However, if the upload is being aborted because the source stream provided to +upload_from_stream raised an error then the original error should be re-raised. + +Abort MUST also close the Stream, or at least place it in an aborted state, so any further attempts to write additional +content to the Stream after Abort has been called fail immediately. + +### File Download + +```javascript +class GridFSBucket { + + /** Opens a Stream from which the application can read the contents of the stored file + * specified by @id. + * + * Returns a Stream. + */ + Stream open_download_stream(TFileId id); + + /** + * Downloads the contents of the stored file specified by @id and writes + * the contents to the @destination Stream. + */ + void download_to_stream(TFileId id, Stream destination); + +} +``` + +Downloads a stored file from a GridFS bucket. For languages that have a Stream abstraction, drivers SHOULD use that +Stream abstraction. For languages that do not have a Stream abstraction, drivers MUST create an abstraction that +supports streaming. + +In the case of open_download_stream, the application reads the contents of the stored file by reading from the returned +Stream until end of file is reached. The application MUST call close (or its equivalent) on the returned Stream when it +is done reading the contents. + +In the case of download_to_stream the driver writes the contents of the stored file to the provided Stream. The driver +does NOT call close (or its equivalent) on the Stream. + +Note: By default a file id is of type ObjectId. If an application uses custom file ids it may be of any type. + +**Implementation details:** + +Drivers must first retrieve the files collection document for this file. If there is no files collection document, the +file either never existed, is in the process of being deleted, or has been corrupted, and the driver MUST raise an +error. + +Then, implementers retrieve all chunks with files_id equal to id, sorted in ascending order on "n". + +However, when downloading a zero length stored file the driver MUST NOT issue a query against the chunks collection, +since that query is not necessary. For a zero length file, drivers return either an empty stream or send nothing to the +provided stream (depending on the download method). + +If a networking error or server error occurs, drivers MUST raise an error. + +As drivers stream the stored file they MUST check that each chunk received is the next expected chunk (i.e. it has the +expected "n" value) and that the data field is of the expected length. In the case of open_download_stream, if the +application stops reading from the stream before reaching the end of the stored file, any errors that might exist beyond +the point at which the application stopped reading won't be detected by the driver. + +### File Deletion + +```javascript +class GridFSBucket { + + /** + * Given a @id, delete this stored file's files collection document and + * associated chunks from a GridFS bucket. + */ + void delete(TFileId id); + +} +``` + +Deletes the stored file's files collection document and associated chunks from the underlying database. + +As noted for download(), drivers that previously used id's of a different type MAY implement a delete() method that +accepts that type, but MUST mark that method as deprecated. + +**Implementation details:** + +There is an inherent race condition between the chunks and files collections. Without some transaction-like behavior +between these two collections, it is always possible for one client to delete a stored file while another client is +attempting a read of the stored file. For example, imagine client A retrieves a stored file's files collection document, +client B deletes the stored file, then client A attempts to read the stored file's chunks. Client A wouldn"t find any +chunks for the given stored file. To minimize the window of vulnerability of reading a stored file that is the process +of being deleted, drivers MUST first delete the files collection document for a stored file, then delete its associated +chunks. + +If there is no such file listed in the files collection, drivers MUST raise an error. Drivers MAY attempt to delete any +orphaned chunks with files_id equal to id before raising the error. + +If a networking or server error occurs, drivers MUST raise an error. + +### File deletion by filename + +To rename all revisions of a stored file with the specified filename, drivers SHOULD provide the method +`delete_by_name`: + +```javascript +class GridFSBucket { + + /** + * Deletes all stored files with the specified @filename from a GridFS bucket. + */ + void delete_by_name(string filename); + +} +``` + +This method is an optimisation over deleting each revision of a stored file individually. + +**Implementation details:** + +Drivers MUST first find the `_id` field of all files collection documents with the given filename. Drivers MUST then +delete all files collection documents with the found ids. Drivers MUST then delete all chunks with `files_id` in the +found ids that were just deleted. + +If there are no files collection documents with the given filename, drivers MUST raise an error. + +### Generic Find on Files Collection + +```javascript +class GridFSFindOptions { + + /** + * Enables writing to temporary files on the server. When set to true, the server + * can write temporary data to disk while executing the find operation on the files collection. + * + * This option is sent only if the caller explicitly provides a value. The default + * is to not send a value. For servers < 3.2, this option is ignored and not sent + * as allowDiskUse does not exist in the OP_QUERY wire protocol. + * + * @see https://www.mongodb.com/docs/manual/reference/command/find/ + */ + allowDiskUse: Optional; + + /** + * The number of documents to return per batch. + */ + batchSize : Int32 optional; + + /** + * The maximum number of documents to return. + */ + limit : Int32 optional; + + /** + * The maximum amount of time to allow the query to run. + */ + maxTimeMS: Int64 optional; + + /** + * The server normally times out idle cursors after an inactivity period + * to prevent excess memory use. Set this option to prevent that. + */ + noCursorTimeout : Boolean optional; + + /** + * The number of documents to skip before returning. + */ + skip : Int32 optional; + + /** + * The order by which to sort results. Defaults to not sorting. + */ + sort : Document optional; + +} + +class GridFSBucket { + + /** + * Find and return the files collection documents that match @filter. + */ + Iterable find(Document filter, GridFSFindOptions options=null); + +} +``` + +This call will trigger a find() operation on the files collection using the given filter. Drivers returns a sequence of +documents that can be iterated over. Drivers return an empty or null set when there are no matching files collection +documents. As the number of files could be large, drivers SHOULD return a cursor-like iterable type and SHOULD NOT +return a fixed-size array type. + +**Implementation details:** + +Drivers SHOULD NOT perform any validation on the filter. If the filter contains fields that do not exist within files +collection documents, then an empty result set will be returned. + +Drivers MUST propagate the GridFSFindOptions to the FindOptions passed to the underlying Find operation. + +Drivers MUST document how users query files collection documents, including how to query metadata, e.g. using a filter +like { metadata.fieldname : "some_criteria" }. + +## Advanced API + +### File Download by Filename + +```javascript +class GridFSDownloadByNameOptions { + + /** + * Which revision (documents with the same filename and different uploadDate) + * of the file to retrieve. Defaults to -1 (the most recent revision). + * + * Revision numbers are defined as follows: + * 0 = the original stored file + * 1 = the first revision + * 2 = the second revision + * etc… + * -2 = the second most recent revision + * -1 = the most recent revision + */ + revision : Int32 optional; + +} + +class GridFSBucket { + + /** Opens a Stream from which the application can read the contents of the stored file + * specified by @filename and the revision in @options. + * + * Returns a Stream. + */ + Stream open_download_stream_by_name(string filename, GridFSDownloadByNameOptions options=null); + + /** + * Downloads the contents of the stored file specified by @filename and by the + * revision in @options and writes the contents to the @destination Stream. + */ + void download_to_stream_by_name(string filename, Stream destination, + GridFSDownloadByNameOptions options=null); + +} +``` + +Retrieves a stored file from a GridFS bucket. For languages that have a Stream abstraction, drivers SHOULD use that +Stream abstraction. For languages that do not have a Stream abstraction, drivers MUST create an abstraction that +supports streaming. + +**Implementation details:** + +If there is no file with the given filename, or if the requested revision does not exist, drivers MUST raise an error +with a distinct message for each case. + +Drivers MUST select the files collection document of the file to-be-returned by running a query on the files collection +for the given filename, sorted by uploadDate (either ascending or descending, depending on the revision requested) and +skipping the appropriate number of documents. For negative revision numbers, the sort is descending and the number of +documents to skip equals (-revision - 1). For non-negative revision numbers, the sort is ascending and the number of +documents to skip equals the revision number. + +If a networking error or server error occurs, drivers MUST raise an error. + +### Partial File Retrieval + +In the case of open_download_stream, drivers SHOULD support partial file retrieval by allowing the application to read +only part of the stream. If a driver does support reading only part of the stream, it MUST do so using the standard +stream methods of its language for seeking to a position in a stream and reading the desired amount of data from that +position. This is the preferred method of supporting partial file retrieval. + +In the case of download_to_stream, drivers are not required to support partial file retrieval. If they choose to do so, +drivers can support this operation by adding "start" and "end" to their supported options for download_to_stream. These +values represent non-negative byte offsets from the beginning of the file. When "start" and "end" are specified, drivers +return the bytes of the file in `[start, end)`. If "start" and "end" are equal no data is returned. + +If either "start" or "end" is invalid, drivers MUST raise an error. These values are considered invalid if they are +negative, greater than the file length, or if "start" is greater than "end". + +When performing partial reads, drivers SHOULD use the file's "chunkSize" to calculate which chunks contain the desired +section and avoid reading unneeded documents from the "chunks" collection. + +### Renaming stored files + +```javascript +class GridFSBucket { + + /** + * Renames the stored file with the specified @id. + */ + void rename(TFileId id, string new_filename); + +} +``` + +Sets the filename field in the stored file's files collection document to the new filename. + +**Implementation details:** + +Drivers construct and execute an update_one command on the files collection using `{ _id: @id }` as the filter and +`{ $set : { filename : "new_filename" } }` as the update parameter. + +If `renameByName` is not implemented to rename multiple revisions of the same filename, users must retrieve the full +list of files collection documents for a given filename and execute "rename" on each corresponding `_id`. + +If there is no file with the given id, drivers MUST raise an error. + +### Renaming stored files by filename + +To rename all revisions of a stored file with the specified filename, drivers SHOULD provide the method +`rename_by_name`: + +```javascript +class GridFSBucket { + + /** + * Renames all revisions of the stored file with the specified @filename. + */ + void rename_by_name(string filename, string new_filename); + +} +``` + +**Implementation details:** + +Drivers construct and execute an update_many command on the files collection using `{ filename: @filename }` as the +filter and `{ $set : { filename : "new_filename" } }` as the update parameter. + +If there is no file with the given filename, drivers MUST raise an error. + +### Dropping an entire GridFS bucket + +```javascript +class GridFSBucket { + + /** + * Drops the files and chunks collections associated with + * this bucket. + */ + void drop(); + +} +``` + +This method drops the files and chunks collections associated with this GridFS bucket. + +Drivers should drop the files collection first, and then the chunks collection. + +## Test Plan + +TBD + +## Motivation for Change + +The [existing GridFS documentation](https://www.mongodb.com/docs/manual/core/gridfs/) is only concerned with the +underlying data model for this feature, and does not specify what basic set of features an implementation of GridFS +should or should not provide. As a result, GridFS is currently implemented across drivers, but with varying APIs, +features, and behavior guarantees. Current implementations also may not conform to the existing documentation. + +This spec documents minimal operations required by all drivers offering GridFS support, along with optional features +that drivers may choose to support. This spec is also explicit about what features/behaviors of GridFS are not specified +and should not be supported. Additionally, this spec validates and clarifies the existing data model, deprecating fields +that are undesirable or incorrect. + +## Design Rationale + +Why is the default chunk size 255 KiB? + +On MMAPv1, the server provides documents with extra padding to allow for in-place updates. When the "data" field of a +chunk is limited to 255 KiB, it ensures that the whole chunk document (the chunk data along with an `_id` and other +information) will fit into a 256 KiB section of memory, making the best use of the provided padding. Users setting +custom chunk sizes are advised not to use round power-of-two values, as the whole chunk document is likely to exceed +that space and demand extra padding from the system. WiredTiger handles its memory differently, and this optimization +does not apply. However, because application code generally won"t know what storage engine will be used in the database, +always avoiding round power-of-two chunk sizes is recommended. + +Why can"t I alter documents once they are in the system? + +GridFS works with documents stored in multiple collections within MongoDB. Because there is currently no way to +atomically perform operations across collections in MongoDB, there is no way to alter stored files in a way that +prevents race conditions between GridFS clients. Updating GridFS stored files without that server functionality would +involve a data model that could support this type of concurrency, and changing the GridFS data model is outside of the +scope of this spec. + +Why provide a "rename" method? + +By providing users with a reasonable alternative for renaming a file, we can discourage users from writing directly to +the files collections under GridFS. With this approach we can prevent critical files collection documents fields from +being mistakenly altered. + +Why is there no way to perform arbitrary updates on the files collection? + +The rename helper defined in this spec allows users to easily rename a stored file. While updating files collection +documents in other, more granular ways might be helpful for some users, validating such updates to ensure that other +files collection document fields remain protected is a complicated task. We leave the decision of how best to provide +this functionality to a future spec. + +What is the "md5" field of a files collection document and how was it used? + +"md5" holds an MD5 checksum that is computed from the original contents of a user file. Historically, GridFS did not use +acknowledged writes, so this checksum was necessary to ensure that writes went through properly. With acknowledged +writes, the MD5 checksum is still useful to ensure that files in GridFS have not been corrupted. A third party directly +accessing the 'files' and "chunks" collections under GridFS could, inadvertently or maliciously, make changes to +documents that would make them unusable by GridFS. Comparing the MD5 in the files collection document to a re-computed +MD5 allows detecting such errors and corruption. However, drivers now assume that the stored file is not corrupted, and +applications that want to use the MD5 value to check for corruption must do so themselves. + +Why store the MD5 checksum instead of creating the hash as-needed? + +The MD5 checksum must be computed when a file is initially uploaded to GridFS, as this is the only time we are +guaranteed to have the entire uncorrupted file. Computing it on-the-fly as a file is read from GridFS would ensure that +our reads were successful, but guarantees nothing about the state of the file in the system. A successful check against +the stored MD5 checksum guarantees that the stored file matches the original and no corruption has occurred. + +Why are MD5 checksums now deprecated? What should users do instead? + +MD5 is prohibited by FIPS 140-2. Operating systems and libraries operating in FIPS mode do not provide the MD5 +algorithm. To avoid a broken GridFS feature on such systems, the use of MD5 with GridFS is deprecated, should not be +added to new implementations, and should be removed from existing implementations according to the deprecation policy of +individual drivers. Applications that desire a file digest should implement it outside of GridFS and store it with other +file metadata. + +Why do drivers no longer need to call the filemd5 command on upload? + +When a chunk is inserted and no error occurs the application can assume that the chunk was correctly inserted. No other +operations that insert or modify data require the driver to double check that the operation succeeded. It can be assumed +that any errors would have been detected by use of the appropriate write concern. Using filemd5 also prevents users from +sharding chunk keys. + +What about write concern? + +This spec leaves the choice of how to set write concern to driver authors. Implementers may choose to accept write +concern through options on the given methods, to set a configurable write concern on the GridFS object, to enforce a +single write concern for all GridFS operations, or to do something different. + +If a user has given GridFS a write concern of 0, should we perform MD5 calculations? (If supported for backwards +compatibility) + +Yes, because the checksum is used for detecting future corruption or misuse of GridFS collections. + +Is GridFS limited by sharded systems? + +For best performance, clients using GridFS on a sharded system should use a shard key that ensures all chunks for a +given stored file are routed to the same shard. Therefore, if the chunks collection is sharded, you should shard on the +files_id. Normally only the chunks collection benefits from sharding, since the files collection is usually small. +Otherwise, there are no limitations to GridFS on sharded systems. + +Why is contentType deprecated? + +Most fields in the files collection document are directly used by the driver, with the exception of: metadata, +contentType and aliases. All information that is purely for use of the application should be embedded in the 'metadata' +document. Users of GridFS who would like to store a contentType for use in their applications are encouraged to add a +'contentType' field to the "metadata" document instead of using the deprecated top-level "contentType" field. + +Why are aliases deprecated? + +The "aliases" field of the files collection documents was misleading. It implies that a file in GridFS could be accessed +by alternate names when, in fact, none of the existing implementations offer this functionality. For GridFS +implementations that retrieve stored files by filename or support specifying specific revisions of a stored file, it is +unclear how "aliases" should be interpreted. Users of GridFS who would like to store alternate filenames for use in +their applications are encouraged to add an "aliases" field to the "metadata" document instead of using the deprecated +top-level "aliases" field. + +What happened to the put and get methods from earlier drafts? + +Upload and download are more idiomatic names that more clearly indicate their purpose. Get and put are often associated +with getting and setting properties of a class, and using them instead of download and upload was confusing. + +Why aren't there methods to upload and download byte arrays? + +We assume that GridFS files are usually quite large and therefore that the GridFS API must support streaming. Most +languages have easy ways to wrap a stream around a byte array. Drivers are free to add helper methods that directly +support uploading and downloading GridFS files as byte arrays. + +Should drivers report an error if a stored file has extra chunks? + +The length and the chunkSize fields of the files collection document together imply exactly how many chunks a stored +file should have. If the chunks collection has any extra chunks the stored file is in an inconsistent state. Ideally we +would like to report that as an error, but this is an extremely unlikely state and we don't want to pay a performance +penalty checking for an error that is almost never there. Therefore, drivers MAY ignore extra chunks. + +Why have we changed our mind about requiring the file id to be an ObjectId? + +This spec originally required the file id for all new GridFS files to be an ObjectId and specified that the driver +itself would be the one to generate the ObjectId when a new file was uploaded. While this sounded like a good idea, it +has since become evident that there are valid use cases for an application to want to generate its own file id, and that +an application wouldn't necessarily want to use ObjectId as the type of the file id. The most common case where an +application would want to use a custom file id is when the chunks collection is to be sharded and the application wants +to use a custom file id that is suitable for sharding. Accordingly, we have relaxed this spec to allow an application to +supply a custom file id (of any type) when uploading a new file. + +How can we maintain backward compatibility while supporting custom file ids? + +For most methods supporting custom file ids is as simple as relaxing the type of the id parameter from ObjectId to +something more general like object or BSON value (or to a type parameter like `` in languages that support +generic methods). In a few cases new methods were added to support custom file ids. The original upload_from_stream +method returned an ObjectId, and support for custom file ids is implemented by adding a new method that takes the custom +file id as an additional parameter. Drivers should continue to support the original method if possible to maintain +backward compatibility. This spec does not attempt to completely mandate how each driver should maintain backward +compatibility, as different languages have different approaches and capabilities for maintaining backward compatibility. + +## Backwards Compatibility + +This spec presents a new API for GridFS systems, which may break existing functionality for some drivers. The following +are suggestions for ways to mitigate these incompatibilities. + +File revisions + +This document presents a basic API that does not support specifying specific revisions of a stored file, and an advanced +API that does. Drivers MAY choose to implement whichever API is closest to the functionality they now support. Note that +the methods for file insertion are the same whether specifying specific revisions is supported or not. + +Method names + +If drivers provide methods that conform to the functionality outlined in this document, drivers MAY continue to provide +those methods under their existing names. In this case, drivers SHOULD make it clear in their documentation that these +methods have equivalents defined in the spec under a different name. + +ContentType field + +Drivers MAY continue to create a "contentType'" field within files collection documents, so that applications depending +on this field continue to work. However, drivers SHOULD make it clear in their documentation that this field is +deprecated, and is not used at all in driver code. Documentation SHOULD encourage users to store contentType in the +"metadata" document instead. + +Aliases field + +Drivers MAY continue to create an "aliases" field within files collection documents, so that applications depending on +this field continue to work. However, drivers SHOULD make it clear in their documentation that this field is deprecated, +and is not used at all in driver code. Documentation SHOULD encourage users to store aliases in the "metadata" document +instead. + +## Reference Implementation + +TBD + +## Future work + +Changes to the GridFS data model are out-of-scope for this spec, but may be considered for the future. + +The ability to alter or append to existing GridFS files has been cited as something that would greatly improve the +system. While this functionality is not in-scope for this spec (see "Why can"t I alter documents once they are in the +system?") it is a potential area of growth for the future. + +## Changelog + +- 2024-10-30: Add `delete_by_name` and `rename_by_name` +- 2024-10-28: Removed deprecated fields from tests: `md5`, `contentType`, `aliases` +- 2024-02-27: Migrated from reStructuredText to Markdown. +- 2016-05-10: Support custom file ids +- 2016-10-07: Drivers SHOULD handle any numeric type of length and chunkSize +- 2016-10-07: Added ReadConcern to the GridFS spec +- 2016-10-07: Modified a JSON test that was testing optional behavior +- 2018-01-31: Deprecated MD5, and specified an option to disable MD5 until removed +- 2018-07-05: Must not use 'filemd5' +- 2020-01-17: Added allowDiskUse to GridFSFindOptions +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec +- 2022-10-05: Remove spec front matter and reformat changelog. diff --git a/source/gridfs/gridfs-spec.rst b/source/gridfs/gridfs-spec.rst index a9c85c2729..2778af7bcb 100644 --- a/source/gridfs/gridfs-spec.rst +++ b/source/gridfs/gridfs-spec.rst @@ -1,1185 +1,4 @@ -=========== -GridFS Spec -=========== -:Spec: 105 -:Title: GridFS Spec -:Authors: Samantha Ritter and Robert Stam -:Advisors: David Golden and Jeff Yemin -:Status: Approved -:Type: Standards -:Minimum Server Version: 2.2 -:Last Modified: July 5, 2018 -:Version: 1.3 - -.. contents:: - --------- - -Abstract -======== - -GridFS is a convention drivers use to store and retrieve BSON binary -data (type “\\x05”) that exceeds MongoDB’s BSON-document size limit of -16MB. When this data, called a **user file**, is written to the system, -GridFS divides the file into **chunks** that are stored as distinct -documents in a **chunks collection**. To retrieve a stored file, GridFS -locates and returns all of its component chunks. Internally, GridFS -creates a **files collection document** for each stored file. Files -collection documents hold information about stored files, and they are -stored in a **files collection**. - -This spec defines a basic API for GridFS. This spec also outlines -advanced GridFS features that drivers can choose to support in their -implementations. Additionally, this document attempts to clarify the -meaning and purpose of all fields in the GridFS data model, disambiguate -GridFS terminology, and document configuration options that were -previously unspecified. - -Definitions -=========== - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, -“SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this -document are to be interpreted as described in `RFC 2119 `_. - -Terms ------ - -Bucket name - A prefix under which a GridFS system’s collections are stored. Collection names for the files and chunks - collections are prefixed with the bucket name. The bucket name MUST be configurable by the user. Multiple - buckets may exist within a single database. The default bucket name is ‘fs’. - -Chunk - A section of a user file, stored as a single document in the ‘chunks’ collection of a GridFS bucket. - The default size for the data field in chunks is 255KB. Chunk documents have the following form: - - .. code:: javascript - - { - "_id" : , - "files_id" : , - "n" : , - "data" : - } - - :_id: a unique ID for this document of type BSON ObjectId - :files_id: the id for this file (the _id from the files collection document). This field takes the type of - the corresponding _id in the files collection. - :n: the index number of this chunk, zero-based. - :data: a chunk of data from the user file - -Chunks collection - A collection in which chunks of a user file are stored. The name for this collection is the word 'chunks' - prefixed by the bucket name. The default is ‘fs.chunks’. - -Empty chunk - A chunk with a zero length “data” field. - -Files collection - A collection in which information about stored files is stored. There will be one files collection document - per stored file. The name for this collection is the word ‘files’ prefixed by the bucket name. The default - is ‘fs.files’. - -Files collection document - A document stored in the files collection that contains information about a single stored file. Files collection - documents have the following form: - - .. code:: javascript - - { - "_id" : , - "length" : , - "chunkSize" : , - "uploadDate" : , - "md5" : , - "filename" : , - "contentType" : , - "aliases" : , - "metadata" : - } - - :_id: a unique ID for this document. Usually this will be of type ObjectId, but a custom _id value provided by - the application may be of any type. - :length: the length of this stored file, in bytes - :chunkSize: the size, in bytes, of each data chunk of this file. This value is configurable by file. The default is 255KB. - :uploadDate: the date and time this file was added to GridFS, stored as a BSON datetime value. The value of this - field MUST be the datetime when the upload completed, not the datetime when it was begun. - :md5: DEPRECATED, a hash of the contents of the stored file - :filename: the name of this stored file; this does not need to be unique - :contentType: DEPRECATED, any MIME type, for application use only - :aliases: DEPRECATED, for application use only - :metadata: any additional application data the user wishes to store - - Note: some older versions of GridFS implementations allowed applications to - add arbitrary fields to the files collection document at the root level. New - implementations of GridFS will not allow this, but must be prepared to - handle existing files collection documents that might have additional fields. - - Note: drivers SHOULD store length as Int64 and chunkSize as Int32 when creating new GridFS files. However, drivers MUST - be able to handle existing GridFS files where the length and chunkSize fields might have been stored using a - different numeric data type. - -Orphaned chunk - A document in the chunks collections for which the - “files_id” does not match any “_id” in the files collection. Orphaned - chunks may be created if write or delete operations on GridFS fail - part-way through. - -Stored File - A user file that has been stored in GridFS, consisting - of a files collection document in the files collection and zero or more - documents in the chunks collection. - -Stream - An abstraction that represents streamed I/O. In some languages a different - word is used to represent this abstraction. - -TFileId - While GridFS file id values are ObjectIds by default, an application may choose - to use custom file id values, which may be of any type. In this spec the term - TFileId refers to whatever data type is appropriate in the driver's programming - language to represent a file id. This would be something like object, BsonValue or - a generic type parameter. - -User File - A data added by a user to GridFS. This data may map to an actual file on disk, a stream of input, a large data - object, or any other large amount of consecutive data. - -Specification -============= - -Guidance --------- - -Documentation -~~~~~~~~~~~~~ - -The documentation provided in code below is merely for driver authors -and SHOULD NOT be taken as required documentation for the driver. - -Operations -~~~~~~~~~~ - -All drivers MUST offer the Basic API operations defined in the following -sections and MAY offer the Advanced API operations. This does not -preclude a driver from offering more. - -Operation Parameters -~~~~~~~~~~~~~~~~~~~~ - -All drivers MUST offer the same options for each operation as defined in -the following sections. This does not preclude a driver from offering -more. The options parameter is optional. A driver SHOULD NOT require a -user to specify optional parameters. - -Deviations -~~~~~~~~~~ - -A non-exhaustive list of acceptable deviations are as follows: - -- Using named parameters instead of an options hash. For instance, - - .. code:: javascript - - id = bucket.upload_from_stream(filename, source, chunkSizeBytes: 16 * 1024); - -- Using a fluent style for constructing a GridFSBucket instance: - - .. code:: javascript - - bucket = new GridFSBucket(database) - .withReadPreference(ReadPreference.Secondary); - -When using a fluent-style builder, all options should be named -rather than inventing a new word to include in the pipeline (like -options). Required parameters are still required to be on the -initiating constructor. - -Naming ------- - -All drivers MUST name operations, objects, and parameters as defined in -the following sections. - -Deviations are permitted as outlined below. - -Deviations -~~~~~~~~~~ - -When deviating from a defined name, an author should consider if the -altered name is recognizable and discoverable to the user of another -driver. - -A non-exhaustive list of acceptable naming deviations are as follows: - -- Using "bucketName" as an example, Java would use "bucketName" while - Python would use "bucket_name". However, calling it - "bucketPrefix" would not be acceptable. - -- Using "maxTimeMS" as an example, .NET would use "MaxTime" where its - type is a TimeSpan structure that includes units. However, - calling it "MaximumTime" would not be acceptable. - -- Using "GridFSUploadOptions" as an example, Javascript wouldn't need - to name it while other drivers might prefer to call it - "GridFSUploadArgs" or "GridFSUploadParams". However, calling it - "UploadOptions" would not be acceptable. - -- Languages that use a different word than "Stream" to represent a - streamed I/O abstraction may replace the word "Stream" with their - language's equivalent word. For example, open_upload_stream might - be called open_upload_file or open_upload_writer if appropriate. - -- Languages that support overloading MAY shorten the name of some - methods as appropriate. For example, download_to_stream and - download_to_stream_by_name MAY be overloaded - download_to_stream methods with different parameter types. - Implementers are encouraged not to shorten method names - unnecessarily, because even if the shorter names are not - ambiguous today they might become ambiguous in the future as new - features are added. - -API -=== - -This section presents two groups of features, a basic API that a driver -MUST implement, and a more advanced API that drivers MAY choose to -implement additionally. - -Basic API -========= - -Configurable GridFSBucket class -------------------------------- - -.. code:: javascript - - class GridFSBucketOptions { - - /** - * The bucket name. Defaults to 'fs'. - */ - bucketName : String optional; - - /** - * The chunk size in bytes. Defaults to 255KB. - */ - chunkSizeBytes : Int32 optional; - - /** - * The write concern. Defaults to the write concern of the database. - */ - writeConcern : WriteConcern optional; - - /** - * The read concern. Defaults to the read concern of the database. - */ - readConcern : ReadConcern optional; - - /** - * The read preference. Defaults to the read preference of the database. - */ - readPreference : ReadPreference optional; - - /** - * TRANSITIONAL: This option is provided for backwards compatibility. - * It MUST be supported while a driver supports MD5 and MUST be removed - * (or made into a no-op) when a driver removes MD5 support entirely. - * When true, the GridFS implementation will not compute MD5 checksums - * of uploaded files. Defaults to false. - */ - disableMD5: Boolean - } - - class GridFSBucket { - - /** - * Create a new GridFSBucket object on @db with the given @options. - */ - GridFSBucket new(Database db, GridFSBucketOptions options=null); - - } - -Creates a new GridFSBucket object, managing a GridFS bucket within the -given database. - -GridFSBucket objects MUST allow the following options to be -configurable: - -- **bucketName:** the name of this GridFS bucket. The files and chunks - collection for this GridFS bucket are prefixed by this name - followed by a dot. Defaults to “fs”. This allows multiple GridFS - buckets, each with a unique name, to exist within the same - database. - -- **chunkSizeBytes:** the number of bytes stored in chunks for new - user files added through this GridFSBucket object. This will not - reformat existing files in the system that use a different chunk - size. Defaults to 255KB. - -IF a driver supports configuring readConcern, readPreference or writeConcern -at the database or collection level, then GridFSBucket objects MUST also allow -the following options to be configurable: - -- **readConcern:** defaults to the read concern on the parent - database (or client object if the parent database has no read - concern). - -- **readPreference:** defaults to the read preference on the parent - database (or client object if the parent database has no read - preference). - -- **writeConcern:** defaults to the write concern on the parent - database (or client object if the parent database has no write - concern). - -The following option is transitional: - -- **disableMD5:** this allows users to disable MD5 when operating under - FIPS restrictions. It is provided to allow a transition period as - drivers remove MD5 support. Until a driver removes MD5 support, - drivers MUST support this option. Following a driver's normal - feature removal cycle, when MD5 support is removed, this option MUST be - removed or otherwise made into a no-op option. - -GridFSBucket instances are immutable. Their properties MUST NOT be -changed after the instance has been created. If your driver provides a -fluent way to provide new values for properties, these fluent methods -MUST return new instances of GridFSBucket. - -Indexes -------- - -For efficient execution of various GridFS operations the following -indexes MUST exist: - -- an index on { filename : 1, uploadDate : 1 } on the files collection - -- a unique index on { files_id : 1, n : 1 } on the chunks collection - -Normally we leave it up to the user to create whatever indexes they see -fit, but because GridFS is likely to be looked at as a black box we -should create these indexes automatically in a way that involves the -least amount of overhead possible. - -Before read operations -~~~~~~~~~~~~~~~~~~~~~~ - -For read operations, drivers MUST assume that the proper indexes exist. - -Before write operations -~~~~~~~~~~~~~~~~~~~~~~~ - -Immediately before the **first** write operation on an instance of a GridFSBucket -class is attempted (and not earlier), drivers MUST: - -- determine if the files collection is empty using the primary read preference mode. -- and if so, create the indexes described above if they do not already exist - -To determine whether the files collection is empty drivers SHOULD execute -the equivalent of the following shell command: - -.. code :: javascript - - > db.fs.files.findOne({}, { _id : 1 }) - -If no document is returned the files collection is empty. - -This method of determining whether the files collection is empty should perform better -than checking the count in the case where the files collection is sharded. - -Drivers MUST check whether the indexes already exist before attempting to create them. -This supports the scenario where an application is running with read-only authorizations. - -If a driver determines that it should create the indexes, it MUST raise an error -if the attempt to create the indexes fails. - -Drivers MUST create the indexes in foreground mode. - -File Upload ------------ - -.. code:: javascript - - class GridFSUploadOptions { - - /** - * The number of bytes per chunk of this file. Defaults to the - * chunkSizeBytes in the GridFSBucketOptions. - */ - chunkSizeBytes : Int32 optional; - - /** - * User data for the 'metadata' field of the files collection document. - * If not provided the driver MUST omit the metadata field from the - * files collection document. - */ - metadata : Document optional; - - /** - * DEPRECATED: A valid MIME type. If not provided the driver MUST omit the - * contentType field from the files collection document. - * - * Applications wishing to store a contentType should add a contentType field - * to the metadata document instead. - */ - contentType : String optional; - - /** - * DEPRECATED: An array of aliases. If not provided the driver MUST omit the - * aliases field from the files collection document. - * - * Applications wishing to store aliases should add an aliases field to the - * metadata document instead. - */ - aliases: String[] optional; - - } - - class GridFSBucket { - - /** - * Opens a Stream that the application can write the contents of the file to. - * The driver generates the file id. - * - * Returns a Stream to which the application will write the contents. - * - * Note: this method is provided for backward compatibility. In languages - * that use generic type parameters, this method may be omitted since - * the TFileId type might not be an ObjectId. - */ - Stream open_upload_stream(string filename, GridFSUploadOptions options=null); - - /** - * Opens a Stream that the application can write the contents of the file to. - * The application provides a custom file id. - * - * Returns a Stream to which the application will write the contents. - */ - Stream open_upload_stream_with_id(TFileId id, string filename, GridFSUploadOptions options=null); - - /** - * Uploads a user file to a GridFS bucket. The driver generates the file id. - * - * Reads the contents of the user file from the @source Stream and uploads it - * as chunks in the chunks collection. After all the chunks have been uploaded, - * it creates a files collection document for @filename in the files collection. - * - * Returns the id of the uploaded file. - * - * Note: this method is provided for backward compatibility. In languages - * that use generic type parameters, this method may be omitted since - * the TFileId type might not be an ObjectId. - */ - ObjectId upload_from_stream(string filename, Stream source, GridFSUploadOptions options=null); - - /** - * Uploads a user file to a GridFS bucket. The application supplies a custom file id. - * - * Reads the contents of the user file from the @source Stream and uploads it - * as chunks in the chunks collection. After all the chunks have been uploaded, - * it creates a files collection document for @filename in the files collection. - * - * Note: there is no need to return the id of the uploaded file because the application - * already supplied it as a a parameter. - */ - void upload_from_stream_with_id(TFileId id, string filename, Stream source, GridFSUploadOptions options=null); - } - -Uploads a user file to a GridFS bucket. For languages that have a Stream -abstraction, drivers SHOULD use that Stream abstraction. For languages -that do not have a Stream abstraction, drivers MUST create an -abstraction that supports streaming. - -In the case of open_upload_stream, the driver returns a Stream to which the -application will write the contents of the file. As the application writes the -contents to the returned Stream, the contents are uploaded as chunks in the chunks -collection. When the application signals it is done writing the contents of the -file by calling close (or its equivalent) on the returned Stream, a files collection -document is created in the files collection. Once the Stream has been closed (and the -files collection document has been created) a driver MUST NOT allow further writes -to the upload Stream. - -The driver MUST make the Id of the new file available to the caller. Typically -a driver SHOULD make the Id available as a property named Id on the -Stream that is returned. In languages where that is not idiomatic, a driver -MUST make the Id available in a way that is appropriate for that language. - -In the case of upload_from_stream, the driver reads the contents of -the user file by consuming the the source Stream until end of file is -reached. The driver does NOT close the source Stream. - -Drivers MUST take an ‘options’ document with configurable parameters. -Drivers for dynamic languages MUST ignore any unrecognized fields in the options -for this method (this does not apply to drivers for static languages which -define an Options class that by definition only contains valid fields). - -Note that in GridFS, ‘filename’ is not a unique identifier. There may be -many stored files with the same filename stored in a GridFS bucket under -different ids. Multiple stored files with the same filename are called -'revisions', and the 'uploadDate' is used to distinguish newer revisions -from older ones. - -**Implementation details:** - -If ‘chunkSizeBytes’ is set through the options, that value MUST be used -as the chunk size for this stored file. If this parameter is not -specified, the default chunkSizeBytes setting for this GridFSBucket -object MUST be used instead. - -To store a user file, the file must have a unique id. In some cases the -driver can generate a unique ObjectId to serve as the id for the -file being uploaded. Otherwise the application provides the value. -Drivers store the contents of the user file in the chunks -collection by breaking up the contents into chunks of size -‘chunkSizeBytes’. For a non-empty user file, for each n\ :sup:`th` -section of the file, drivers create a chunk document and set its fields -as follows: - -:files_id: the id generated for this stored file. -:n: this is the n\ :sup:`th` section of the stored file, zero based. -:data: a section of file data, stored as BSON binary data with subtype 0x00. All chunks except - the last one must be exactly 'chunkSizeBytes' long. The last chunk can be smaller, - and should only be as large as necessary. - -Historically, while streaming the user file, drivers computed an MD5 digest for -the (now deprecated) 'md5' field of the files collection document. If drivers -preserve this behavior for backwards compatibility, they MUST provide the -'disableMD5' member of GridFSBucketOptions. When 'disableMD5' is true, drivers -MUST NOT compute an MD5 digest or include it in the files collection document. -If drivers no longer support the deprecated 'md5' field, they MUST NOT provide -the 'disableMD5' member (or it MUST be a no-op) and MUST NOT compute MD5. - -After storing all chunk documents generated for the user file in the -‘chunks’ collection, drivers create a files collection document for the -file and store it in the files collection. The fields in the files -collection document are set as follows: - -:length: the length of this stored file, in bytes. -:chunkSize: the chunk size in bytes used to break the user file into chunks. While the configuration - option is named ‘chunkSizeBytes’ for clarity, for legacy reasons, the files collection document - uses only ‘chunkSize’. -:uploadDate: a BSON datetime object for the current time, in UTC, when the files collection document was created. -:md5: MD5 checksum for this user file, computed from the file’s data, stored as a hex string, if computed, otherwise omitted. -:filename: the filename passed to this function, UTF-8 encoded. -:contentType: the ‘contentType’ passed in the options, if provided; otherwise omitted. -:aliases: the array passed in the options, if provided; otherwise omitted. -:metadata: the ‘metadata’ document passed in the options, if provided; otherwise omitted. - -If a user file contains no data, drivers MUST still create a files -collection document for it with length set to zero. Drivers MUST NOT -create any empty chunks for this file. - -Drivers MUST NOT run the 'filemd5' database command to confirm -that all chunks were successfully uploaded. We assume that if none of -the inserts failed then the chunks must have been successfully inserted, -and running the 'filemd5' command would just be unnecessary overhead -and doesn't work for sharded chunk keys anyway. - -**Operation Failure** - -If any of the above operations fail against the server, drivers MUST -raise an error. If some inserts succeeded before the failed operation, -these become orphaned chunks. Drivers MUST NOT attempt to clean up these -orphaned chunks. The rationale is that whatever failure caused the -orphan chunks will most likely also prevent cleaning up the orphaned -chunks, and any attempts to clean up the orphaned chunks will simply -cause long delays before reporting the original failure to the -application. - -**Aborting an upload** - -Drivers SHOULD provide a mechanism to abort an upload. When using -open_upload_stream, the returned Stream SHOULD have an Abort method. -When using upload_from_stream, the upload will be aborted if the -source stream raises an error. - -When an upload is aborted any chunks already uploaded MUST be deleted. -Note that this differs from the case where an attempt to insert a chunk -fails, in which case drivers immediately report the failure without -attempting to delete any chunks already uploaded. - -Abort MUST raise an error if it is unable to succesfully abort the -upload (for example, if an error occurs while deleting any chunks -already uploaded). However, if the upload is being aborted because -the source stream provided to upload_from_stream raised an error -then the original error should be re-raised. - -Abort MUST also close the Stream, or at least place it in an aborted -state, so any further attempts to write additional content to the Stream -after Abort has been called fail immediately. - -File Download -------------- - -.. code:: javascript - - class GridFSBucket { - - /** Opens a Stream from which the application can read the contents of the stored file - * specified by @id. - * - * Returns a Stream. - */ - Stream open_download_stream(TFileId id); - - /** - * Downloads the contents of the stored file specified by @id and writes - * the contents to the @destination Stream. - */ - void download_to_stream(TFileId id, Stream destination); - - } - -Downloads a stored file from a GridFS bucket. For languages that have a -Stream abstraction, drivers SHOULD use that Stream abstraction. For -languages that do not have a Stream abstraction, drivers MUST create an -abstraction that supports streaming. - -In the case of open_download_stream, the application reads the -contents of the stored file by reading from the returned Stream until -end of file is reached. The application MUST call close (or its -equivalent) on the returned Stream when it is done reading the contents. - -In the case of download_to_stream the driver writes the contents of -the stored file to the provided Stream. The driver does NOT call close -(or its equivalent) on the Stream. - -Note: By default a file id is of type ObjectId. If an application -uses custom file ids it may be of any type. - -**Implementation details:** - -Drivers must first retrieve the files collection document for this file. -If there is no files collection document, the file either never existed, -is in the process of being deleted, or has been corrupted, and the -driver MUST raise an error. - -Then, implementers retrieve all chunks with files_id equal to id, -sorted in ascending order on “n”. - -However, when downloading a zero length stored file the driver MUST NOT -issue a query against the chunks collection, since that query is not -necessary. For a zero length file, drivers return either an empty stream or -send nothing to the provided stream (depending on the download method). - -If a networking error or server error occurs, drivers MUST raise an -error. - -As drivers stream the stored file they MUST check that each chunk -received is the next expected chunk (i.e. it has the expected "n" value) -and that the data field is of the expected length. In the case of -open_download_stream, if the application stops reading from the stream -before reaching the end of the stored file, any errors that might exist -beyond the point at which the application stopped reading won't be -detected by the driver. - -File Deletion -------------- - -.. code:: javascript - - class GridFSBucket { - - /** - * Given a @id, delete this stored file’s files collection document and - * associated chunks from a GridFS bucket. - */ - void delete(TFileId id); - - } - -Deletes the stored file’s files collection document and associated -chunks from the underlying database. - -As noted for download(), drivers that previously used id’s of a -different type MAY implement a delete() method that accepts that type, -but MUST mark that method as deprecated. - -**Implementation details:** - -There is an inherent race condition between the chunks and files -collections. Without some transaction-like behavior between these two -collections, it is always possible for one client to delete a stored -file while another client is attempting a read of the stored file. For -example, imagine client A retrieves a stored file’s files collection -document, client B deletes the stored file, then client A attempts to -read the stored file’s chunks. Client A wouldn’t find any chunks for the -given stored file. To minimize the window of vulnerability of reading a -stored file that is the process of being deleted, drivers MUST first -delete the files collection document for a stored file, then delete its -associated chunks. - -If there is no such file listed in the files collection, drivers MUST raise an -error. Drivers MAY attempt to delete any orphaned chunks with files_id equal -to id before raising the error. - -If a networking or server error occurs, drivers MUST raise an error. - -Generic Find on Files Collection --------------------------------- - -.. code:: javascript - - class GridFSFindOptions { - - /** - * The number of documents to return per batch. - */ - batchSize : Int32 optional; - - /** - * The maximum number of documents to return. - */ - limit : Int32 optional; - - /** - * The maximum amount of time to allow the query to run. - */ - maxTimeMS: Int64 optional; - - /** - * The server normally times out idle cursors after an inactivity period (10 minutes) - * to prevent excess memory use. Set this option to prevent that. - */ - noCursorTimeout : Boolean optional; - - /** - * The number of documents to skip before returning. - */ - skip : Int32; - - /** - * The order by which to sort results. Defaults to not sorting. - */ - sort : Document optional; - - } - - class GridFSBucket { - - /** - * Find and return the files collection documents that match @filter. - */ - Iterable find(Document filter, GridFSFindOptions options=null); - - } - -This call will trigger a find() operation on the files collection using -the given filter. Drivers returns a sequence of documents that can be -iterated over. Drivers return an empty or null set when there are no -matching files collection documents. As the number of files could be -large, drivers SHOULD return a cursor-like iterable type and SHOULD NOT -return a fixed-size array type. - -**Implementation details:** - -Drivers SHOULD NOT perform any validation on the filter. If the filter -contains fields that do not exist within files collection documents, -then an empty result set will be returned. - -Drivers MUST document how users query files collection documents, -including how to query metadata, e.g. using a filter like { -metadata.fieldname : “some_criteria” }. - -Advanced API -============ - -File Download by Filename -------------------------- - -.. code:: javascript - - class GridFSDownloadByNameOptions { - - /** - * Which revision (documents with the same filename and different uploadDate) - * of the file to retrieve. Defaults to -1 (the most recent revision). - * - * Revision numbers are defined as follows: - * 0 = the original stored file - * 1 = the first revision - * 2 = the second revision - * etc… - * -2 = the second most recent revision - * -1 = the most recent revision - */ - revision : Int32 optional; - - } - - class GridFSBucket { - - /** Opens a Stream from which the application can read the contents of the stored file - * specified by @filename and the revision in @options. - * - * Returns a Stream. - */ - Stream open_download_stream_by_name(string filename, GridFSDownloadByNameOptions options=null); - - /** - * Downloads the contents of the stored file specified by @filename and by the - * revision in @options and writes the contents to the @destination Stream. - */ - void download_to_stream_by_name(string filename, Stream destination, - GridFSDownloadByNameOptions options=null); - - } - -Retrieves a stored file from a GridFS bucket. For languages that have a -Stream abstraction, drivers SHOULD use that Stream abstraction. For -languages that do not have a Stream abstraction, drivers MUST create an -abstraction that supports streaming. - -**Implementation details:** - -If there is no file with the given filename, or if the requested -revision does not exist, drivers MUST raise an error with a distinct -message for each case. - -Drivers MUST select the files collection document of the file -to-be-returned by running a query on the files collection for the given -filename, sorted by uploadDate (either ascending or descending, -depending on the revision requested) and skipping the appropriate number -of documents. For negative revision numbers, the sort is descending and -the number of documents to skip equals (-revision - 1). For non-negative -revision numbers, the sort is ascending and the number of documents to -skip equals the revision number. - -If a networking error or server error occurs, drivers MUST raise an -error. - -Partial File Retrieval ----------------------- - -In the case of open_download_stream, drivers SHOULD support partial -file retrieval by allowing the application to read only part of the -stream. If a driver does support reading only part of the stream, it -MUST do so using the standard stream methods of its language for seeking -to a position in a stream and reading the desired amount of data -from that position. This is the preferred method of supporting -partial file retrieval. - -In the case of download_to_stream, drivers are not required to support -partial file retrieval. If they choose to do so, drivers can support -this operation by adding ‘start’ and ‘end’ to their supported options -for download_to_stream. These values represent non-negative -byte offsets from the beginning of the file. When ‘start’ and ‘end’ are -specified, drivers return the bytes of the file in [start, end). If -‘start’ and ‘end’ are equal no data is returned. - -If either ‘start’ or ‘end’ is invalid, drivers MUST raise an error. -These values are considered invalid if they are negative, greater than -the file length, or if ‘start’ is greater than ‘end’. - -When performing partial reads, drivers SHOULD use the file’s ‘chunkSize’ -to calculate which chunks contain the desired section and avoid reading -unneeded documents from the ‘chunks’ collection. - -Renaming stored files ---------------------- - -.. code:: javascript - - class GridFSBucket { - - /** - * Renames the stored file with the specified @id. - */ - void rename(TFileId id, string new_filename); - - } - -Sets the filename field in the stored file’s files collection document -to the new filename. - -**Implementation details:** - -Drivers construct and execute an update_one command on the files -collection using { _id: @id } as the filter and { $set : { filename : -"new_filename" } } as the update parameter. - -To rename multiple revisions of the same filename, users must retrieve -the full list of files collection documents for a given filename and -execute “rename” on each corresponding “_id”. - -If there is no file with the given id, drivers MUST raise an error. - -Dropping an entire GridFS bucket --------------------------------- - -.. code:: javascript - - class GridFSBucket { - - /** - * Drops the files and chunks collections associated with - * this bucket. - */ - void drop(); - - } - -This method drops the files and chunks collections associated with this -GridFS bucket. - -Drivers should drop the files collection first, and then the chunks -collection. - -Test Plan -========= - -TBD - -Motivation for Change -===================== - -The `existing GridFS documentation `__ is -only concerned with the underlying data model for this feature, and does -not specify what basic set of features an implementation of GridFS -should or should not provide. As a result, GridFS is currently -implemented across drivers, but with varying APIs, features, and -behavior guarantees. Current implementations also may not conform to the -existing documentation. - -This spec documents minimal operations required by all drivers offering -GridFS support, along with optional features that drivers may choose to -support. This spec is also explicit about what features/behaviors of -GridFS are not specified and should not be supported. Additionally, this -spec validates and clarifies the existing data model, deprecating fields -that are undesirable or incorrect. - -Design Rationale -================ - -Why is the default chunk size 255KB? - On MMAPv1, the server provides documents with extra padding to allow for - in-place updates. When the ‘data’ field of a chunk is limited to 255KB, - it ensures that the whole chunk document (the chunk data along with an - _id and other information) will fit into a 256KB section of memory, - making the best use of the provided padding. Users setting custom chunk - sizes are advised not to use round power-of-two values, as the whole - chunk document is likely to exceed that space and demand extra padding - from the system. WiredTiger handles its memory differently, and this - optimization does not apply. However, because application code generally - won’t know what storage engine will be used in the database, always - avoiding round power-of-two chunk sizes is recommended. - -Why can’t I alter documents once they are in the system? - GridFS works with documents stored in multiple collections within - MongoDB. Because there is currently no way to atomically perform - operations across collections in MongoDB, there is no way to alter - stored files in a way that prevents race conditions between GridFS - clients. Updating GridFS stored files without that server functionality - would involve a data model that could support this type of concurrency, - and changing the GridFS data model is outside of the scope of this spec. - -Why provide a ‘rename’ method? - By providing users with a reasonable alternative for renaming a file, we - can discourage users from writing directly to the files collections - under GridFS. With this approach we can prevent critical files - collection documents fields from being mistakenly altered. - -Why is there no way to perform arbitrary updates on the files collection? - The rename helper defined in this spec allows users to easily rename a - stored file. While updating files collection documents in other, more - granular ways might be helpful for some users, validating such updates - to ensure that other files collection document fields remain protected - is a complicated task. We leave the decision of how best to provide this - functionality to a future spec. - -What is the ‘md5’ field of a files collection document and how was it used? - ‘md5’ holds an MD5 checksum that is computed from the original contents - of a user file. Historically, GridFS did not use acknowledged writes, so - this checksum was necessary to ensure that writes went through properly. - With acknowledged writes, the MD5 checksum is still useful to ensure - that files in GridFS have not been corrupted. A third party directly - accessing the 'files' and ‘chunks’ collections under GridFS could, - inadvertently or maliciously, make changes to documents that would make - them unusable by GridFS. Comparing the MD5 in the files collection - document to a re-computed MD5 allows detecting such errors and - corruption. However, drivers now assume that the stored file is not - corrupted, and applications that want to use the MD5 value to check for - corruption must do so themselves. - -Why store the MD5 checksum instead of creating the hash as-needed? - The MD5 checksum must be computed when a file is initially uploaded to - GridFS, as this is the only time we are guaranteed to have the entire - uncorrupted file. Computing it on-the-fly as a file is read from GridFS - would ensure that our reads were successful, but guarantees nothing - about the state of the file in the system. A successful check against - the stored MD5 checksum guarantees that the stored file matches the - original and no corruption has occurred. - -Why are MD5 checksums now deprecated? What should users do instead? - MD5 is prohibited by FIPS 140-2. Operating systems and libraries operating - in FIPS mode do not provide the MD5 algorithm. To avoid a broken - GridFS feature on such systems, the use of MD5 with GridFS is deprecated, - should not be added to new implementations, and should be removed from - existing implementations according to the deprecation policy of individual - drivers. Applications that desire a file digest should implement it - outside of GridFS and store it with other file metadata. - -Why do drivers no longer need to call the filemd5 command on upload? - When a chunk is inserted and no error occurs the application can assume - that the chunk was correctly inserted. No other operations that insert - or modify data require the driver to double check that the operation - succeeded. It can be assumed that any errors would have been detected by - use of the appropriate write concern. Using filemd5 also prevents users - from sharding chunk keys. - -What about write concern? - This spec leaves the choice of how to set write concern to driver - authors. Implementers may choose to accept write concern through options - on the given methods, to set a configurable write concern on the GridFS - object, to enforce a single write concern for all GridFS operations, or - to do something different. - -If a user has given GridFS a write concern of 0, should we perform MD5 calculations? (If supported for backwards compatibility) - Yes, because the checksum is used for detecting future corruption or - misuse of GridFS collections. - -Is GridFS limited by sharded systems? - For best performance, clients using GridFS on a sharded system should - use a shard key that ensures all chunks for a given stored file are - routed to the same shard. Therefore, if the chunks collection is - sharded, you should shard on the files_id. Normally only the chunks - collection benefits from sharding, since the files collection is usually - small. Otherwise, there are no limitations to GridFS on sharded systems. - -Why is contentType deprecated? - Most fields in the files collection document are directly used by the - driver, with the exception of: metadata, contentType and aliases. All - information that is purely for use of the application should be embedded - in the 'metadata' document. Users of GridFS who would like to store a - contentType for use in their applications are encouraged to add a - 'contentType' field to the ‘metadata’ document instead of using the - deprecated top-level ‘contentType’ field. - -Why are aliases deprecated? - The ‘aliases’ field of the files collection documents was misleading. It - implies that a file in GridFS could be accessed by alternate names when, - in fact, none of the existing implementations offer this functionality. - For GridFS implementations that retrieve stored files by filename or - support specifying specific revisions of a stored file, it is unclear - how ‘aliases’ should be interpreted. Users of GridFS who would like to - store alternate filenames for use in their applications are encouraged - to add an ‘aliases’ field to the ‘metadata’ document instead of using - the deprecated top-level ‘aliases’ field. - -What happened to the put and get methods from earlier drafts? - Upload and download are more idiomatic names that more clearly indicate - their purpose. Get and put are often associated with getting and setting - properties of a class, and using them instead of download and upload was - confusing. - -Why aren't there methods to upload and download byte arrays? - We assume that GridFS files are usually quite large and therefore that - the GridFS API must support streaming. Most languages have easy ways to - wrap a stream around a byte array. Drivers are free to add helper - methods that directly support uploading and downloading GridFS files as - byte arrays. - -Should drivers report an error if a stored file has extra chunks? - The length and the chunkSize fields of the files collection document - together imply exactly how many chunks a stored file should have. If the - chunks collection has any extra chunks the stored file is in an - inconsistent state. Ideally we would like to report that as an error, - but this is an extremely unlikely state and we don't want to pay a - performance penalty checking for an error that is almost never there. - Therefore, drivers MAY ignore extra chunks. - -Why have we changed our mind about requiring the file id to be an ObjectId? - This spec originally required the file id for all new GridFS files to be - an ObjectId and specified that the driver itself would be the one to - generate the ObjectId when a new file was uploaded. While this sounded like - a good idea, it has since become evident that there are valid use cases - for an application to want to generate its own file id, and that an - application wouldn't necessarily want to use ObjectId as the type of - the file id. The most common case where an application would want to use - a custom file id is when the chunks collection is to be sharded and the - application wants to use a custom file id that is suitable for sharding. - Accordingly, we have relaxed this spec to allow an application - to supply a custom file id (of any type) when uploading a new file. - -How can we maintain backward compatibility while supporting custom file ids? - For most methods supporting custom file ids is as simple as relaxing the - type of the id parameter from ObjectId to something more general like object - or BSON value (or to a type parameter like in languages that - support generic methods). In a few cases new methods were added to support custom file ids. - The original upload_from_stream method returned an ObjectId, and support for - custom file ids is implemented by adding a new method that takes the custom - file id as an additional parameter. Drivers should continue to support the original - method if possible to maintain backward compatibility. This spec does - not attempt to completely mandate how each driver should maintain backward - compatibility, as different languages have different approaches and - capabilities for maintaining backward compatibility. - -Backwards Compatibility -======================= - -This spec presents a new API for GridFS systems, which may break -existing functionality for some drivers. The following are suggestions -for ways to mitigate these incompatibilities. - -File revisions - This document presents a basic API that does not support specifying - specific revisions of a stored file, and an advanced API that does. - Drivers MAY choose to implement whichever API is closest to the - functionality they now support. Note that the methods for file insertion - are the same whether specifying specific revisions is supported or not. - -Method names - If drivers provide methods that conform to the functionality outlined in - this document, drivers MAY continue to provide those methods under their - existing names. In this case, drivers SHOULD make it clear in their - documentation that these methods have equivalents defined in the spec - under a different name. - -ContentType field - Drivers MAY continue to create a ‘contentType'’ field within files - collection documents, so that applications depending on this field - continue to work. However, drivers SHOULD make it clear in their - documentation that this field is deprecated, and is not used at all in - driver code. Documentation SHOULD encourage users to store contentType - in the ‘metadata’ document instead. - -Aliases field - Drivers MAY continue to create an ‘aliases’ field within files - collection documents, so that applications depending on this field - continue to work. However, drivers SHOULD make it clear in their - documentation that this field is deprecated, and is not used at all in - driver code. Documentation SHOULD encourage users to store aliases in - the ‘metadata’ document instead. - -Reference Implementation -======================== - -TBD - -Future work -=========== - -Changes to the GridFS data model are out-of-scope for this spec, but may -be considered for the future. - -The ability to alter or append to existing GridFS files has been cited -as something that would greatly improve the system. While this -functionality is not in-scope for this spec (see ‘Why can’t I alter -documents once they are in the system?’) it is a potential area of -growth for the future. - -Changes -======= - -- 2016-05-10 Support custom file ids -- 2016-10-07 Drivers SHOULD handle any numeric type of length and chunkSize -- 2016-10-07 Added ReadConcern to the GridFS spec -- 2016-10-07 Modified a JSON test that was testing optional behavior -- 2018-01-31 Deprecated MD5, and specified an option to disable MD5 until removed -- 2018-07-05 Must not use 'filemd5' +.. note:: + This specification has been converted to Markdown and renamed to + `gridfs-spec.md `_. diff --git a/source/gridfs/tests/README.md b/source/gridfs/tests/README.md new file mode 100644 index 0000000000..64600a9a29 --- /dev/null +++ b/source/gridfs/tests/README.md @@ -0,0 +1,28 @@ +# GridFS Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +GridFS. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +## Conventions for Expressing Binary Data + +The unified test format allows binary stream data to be expressed and matched with `$$hexBytes` (for uploads) and +`$$matchesHexBytes` (for downloads), respectively; however, those operators are not supported in all contexts, such as +`insertData` and `outcome`. When binary data must be expressed as a base64-encoded string +([Extended JSON](../../extended-json/extended-json.md) for a BSON binary type), the test SHOULD include a comment noting +the equivalent value in hexadecimal for human-readability. For example: + +```yaml +data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex 11223344 +``` + +Creating the base64-encoded string for a sequence of hexadecimal bytes is left as an exercise to the developer. Consider +the following PHP one-liner: + +```shell-session +$ php -r 'echo base64_encode(hex2bin('11223344')), "\n";' +ESIzRA== +``` diff --git a/source/gridfs/tests/README.rst b/source/gridfs/tests/README.rst deleted file mode 100644 index 69fdbd844e..0000000000 --- a/source/gridfs/tests/README.rst +++ /dev/null @@ -1,66 +0,0 @@ -GridFS Tests -============ - -The YAML and JSON files in this directory are platform-independent tests -meant to exercise a driver's implementation of GridFS. - -Format -====== - -Each test file has two top level sections: - -1. data -2. tests - -The data section defines the initial contents of the files and chunks -collections for all tests in that file. - -The tests section defines the tests to be run. The format of the tests -section will vary slightly depending on what tests are being defined. -In general, they will have the following sections: - -1. description -2. arrange -3. act -4. assert - -The arrange section, if present, defines changes to be made to the -initial contents of the files and chunks collections (as defined by -the data section) before this particular test is run. These changes -are described in the form of write commands that can be sent directly -to MongoDB. - -The act section defines what operation (with which arguments) should -be performed. The arguments section in act contains a source object, which has -a $hex key. $hex is not a BSON type and indicates that the corresponding value -should be parsed as a series of hex bytes (e.g. "12ab" should be parsed into -two bytes: "0x12" and "0xab") that should be uploaded using GridFS. - -The assert section defines what should be true at the end of the test. -This includes checking the return value of the operation, as well as -checking the expected contents of the files and chunks collections. The -expected contents of the files and chunks collections are described -in the form of write commands that modify collections named -expected.files and expected.chunks. Before running these commands, -load the initial files and chunks documents into the expected.files -and expected.chunks collections and then run the commands. At that point -you can assert that fs.files and expected.files are the same, and that -expected.chunks and fs.chunks are the same. - -For operations that are expected to succeed the assert section contains -a "result" element describing the expected result. For operations -that are expected to fail the assert section contains an "error" -element describing the expected failure. - -The "result" element is either the expected result when it is possible to -know the result in advance, or it is the special value "&result" -which means that we expect a result (not a failure) but the actual -value of the result could be anything. The notation "&result" is -modeled after YAML syntax for defining an anchor, and the -result value may be referenced later in the assert section as -"*result". - -Another special notation in the assert section is "*actual", which -is used when the value of a field cannot be known in advance of the -test, so the assert logic should accept whatever the actual value -ended up being. diff --git a/source/gridfs/tests/delete.json b/source/gridfs/tests/delete.json index d74e49284b..9a9b22fc1e 100644 --- a/source/gridfs/tests/delete.json +++ b/source/gridfs/tests/delete.json @@ -1,291 +1,739 @@ { - "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": {} + "description": "gridfs-delete", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" } - ], - "chunks": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" + }, + { + "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" + }, + "filename": "length-0", + "metadata": {} }, - "n": 0, - "data": { - "$hex": "" - } - }, - { - "_id": { - "$oid": "000000000000000000000002" + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} }, - "files_id": { - "$oid": "000000000000000000000003" + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} }, - "n": 0, - "data": { - "$hex": "1122" + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "metadata": {} } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000004" + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } }, - "files_id": { - "$oid": "000000000000000000000004" + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } }, - "n": 1, - "data": { - "$hex": "55667788" + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } } - } - ] - }, + ] + } + ], "tests": [ { - "description": "Delete when length is 0", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000001" + "description": "delete when length is 0", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } } } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000001" - } - }, - "limit": 1 + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "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", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000002" + "description": "delete when length is 0 and there is one extra empty chunk", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000002" + } } } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000002" - } - }, - "limit": 1 + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } } - ] - }, - { - "delete": "expected.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000002" - } - }, - "limit": 0 + }, + { + "_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", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000004" + "description": "delete when length is 8", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } } } - }, - "assert": { - "result": "void", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } } - ] - }, - { - "delete": "expected.chunks", - "deletes": [ - { - "q": { - "files_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 0 + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } } - ] - } - ] - } + } + ] + } + ] }, { - "description": "Delete when files entry does not exist", - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000000" + "description": "delete when files entry does not exist", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000000" + } + }, + "expectError": { + "isClientError": true } } - }, - "assert": { - "error": "FileNotFound" - } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "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", - "arrange": { - "data": [ - { - "delete": "fs.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 + "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 } - ] - }, - "act": { - "operation": "delete", - "arguments": { - "id": { - "$oid": "000000000000000000000004" + }, + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } + }, + "expectError": { + "isClientError": true } } - }, - "assert": { - "error": "FileNotFound", - "data": [ - { - "delete": "expected.files", - "deletes": [ - { - "q": { - "_id": { - "$oid": "000000000000000000000004" - } - }, - "limit": 1 + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "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/source/gridfs/tests/delete.yml b/source/gridfs/tests/delete.yml index ef02359391..70593ac279 100644 --- a/source/gridfs/tests/delete.yml +++ b/source/gridfs/tests/delete.yml @@ -1,123 +1,186 @@ -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" } } +description: "gridfs-delete" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-0" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-0-with-empty-chunk" + metadata: {} + - &file3 + _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-2" + metadata: {} + - &file4 + _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-8" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk0 + _id: { "$oid": "000000000000000000000002" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "ESI=", "subType": "00" } } # hex: 1122 + - &file4_chunk0 + _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "ESIzRA==", "subType": "00" } } # hex: 11223344 + - &file4_chunk1 + _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 1 + data: { "$binary": { "base64": "VWZ3iA==", "subType": "00" } } # 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 } - ] } + - description: "delete when length is 0" + operations: + - name: delete + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000001" } + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - *file2 + - *file3 + - *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 + - *file4_chunk0 + - *file4_chunk1 + - description: "delete when length is 0 and there is one extra empty chunk" + operations: + - name: delete + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000002" } + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - *file1 + - *file3 + - *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file3_chunk0 + - *file4_chunk0 + - *file4_chunk1 + - description: "delete when length is 8" + operations: + - name: delete + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000004" } + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - *file1 + - *file2 + - *file3 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 + - description: "delete when files entry does not exist" + operations: + - name: delete + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000000" } + expectError: { isClientError: true } # FileNotFound + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - *file1 + - *file2 + - *file3 + - *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 + - *file4_chunk0 + - *file4_chunk1 + - 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: { isClientError: true } # FileNotFound + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - *file1 + - *file2 + - *file3 + # Orphaned chunks are still deleted even if fs.files + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 diff --git a/source/gridfs/tests/deleteByName.json b/source/gridfs/tests/deleteByName.json new file mode 100644 index 0000000000..884d0300ce --- /dev/null +++ b/source/gridfs/tests/deleteByName.json @@ -0,0 +1,230 @@ +{ + "description": "gridfs-deleteByName", + "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" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "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": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "delete when multiple revisions of the file exist", + "operations": [ + { + "name": "deleteByName", + "object": "bucket0", + "arguments": { + "filename": "filename" + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when file name does not exist", + "operations": [ + { + "name": "deleteByName", + "object": "bucket0", + "arguments": { + "filename": "missing-file" + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/deleteByName.yml b/source/gridfs/tests/deleteByName.yml new file mode 100644 index 0000000000..b63b15633a --- /dev/null +++ b/source/gridfs/tests/deleteByName.yml @@ -0,0 +1,102 @@ +description: "gridfs-deleteByName" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file3 + _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file4 + _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "otherfilename" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk0 + _id: { "$oid": "000000000000000000000002" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk1 + _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk0 + _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + +tests: + - description: "delete when multiple revisions of the file exist" + operations: + - name: deleteByName + object: *bucket0 + arguments: + filename: filename + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - <<: *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file4_chunk0 + - description: "delete when file name does not exist" + operations: + - name: deleteByName + object: *bucket0 + arguments: + filename: missing-file + expectError: { isClientError: true } # FileNotFound diff --git a/source/gridfs/tests/download.json b/source/gridfs/tests/download.json index 5092fba981..67658ac512 100644 --- a/source/gridfs/tests/download.json +++ b/source/gridfs/tests/download.json @@ -1,467 +1,540 @@ { - "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": {} + "description": "gridfs-download", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" } - ], - "chunks": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$hex": "" - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" + }, + { + "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" + }, + "filename": "length-0", + "metadata": {} }, - "n": 0, - "data": { - "$hex": "1122" - } - }, - { - "_id": { - "$oid": "000000000000000000000003" + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-0-with-empty-chunk", + "metadata": {} }, - "files_id": { - "$oid": "000000000000000000000004" + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-2", + "metadata": {} }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000004" + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-8", + "metadata": {} }, - "files_id": { - "$oid": "000000000000000000000004" + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 10, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "length-10", + "metadata": {} }, - "n": 1, - "data": { - "$hex": "55667788" + { + "_id": { + "$oid": "000000000000000000000006" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "metadata": {} } - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } }, - "n": 0, - "data": { - "$hex": "11223344" - } - }, - { - "_id": { - "$oid": "000000000000000000000006" + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } }, - "files_id": { - "$oid": "000000000000000000000005" + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } }, - "n": 1, - "data": { - "$hex": "55667788" - } - }, - { - "_id": { - "$oid": "000000000000000000000007" + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } }, - "files_id": { - "$oid": "000000000000000000000005" + { + "_id": { + "$oid": "000000000000000000000005" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } }, - "n": 2, - "data": { - "$hex": "99aa" - } - }, - { - "_id": { - "$oid": "000000000000000000000008" + { + "_id": { + "$oid": "000000000000000000000006" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VWZ3iA==", + "subType": "00" + } + } }, - "files_id": { - "$oid": "000000000000000000000006" + { + "_id": { + "$oid": "000000000000000000000007" + }, + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2, + "data": { + "$binary": { + "base64": "mao=", + "subType": "00" + } + } }, - "n": 0, - "data": { - "$hex": "1122" + { + "_id": { + "$oid": "000000000000000000000008" + }, + "files_id": { + "$oid": "000000000000000000000006" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESI=", + "subType": "00" + } + } } - } - ] - }, + ] + } + ], "tests": [ { - "description": "Download when length is zero", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000001" + "description": "download when length is zero", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "" + "expectResult": { + "$$matchesHexBytes": "" + } } - } + ] }, { - "description": "Download when length is zero and there is one empty chunk", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000002" + "description": "download when length is zero and there is one empty chunk", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000002" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "" + "expectResult": { + "$$matchesHexBytes": "" + } } - } + ] }, { - "description": "Download when there is one chunk", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000003" + "description": "download when there is one chunk", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000003" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122" + "expectResult": { + "$$matchesHexBytes": "1122" + } } - } + ] }, { - "description": "Download when there are two chunks", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000004" + "description": "download when there are two chunks", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000004" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122334455667788" + "expectResult": { + "$$matchesHexBytes": "1122334455667788" + } } - } + ] }, { - "description": "Download when there are three chunks", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" + "description": "download when there are three chunks", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "112233445566778899aa" + "expectResult": { + "$$matchesHexBytes": "112233445566778899aa" + } } - } + ] }, { - "description": "Download when files entry does not exist", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - }, - "options": {} + "description": "download when files entry does not exist", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000000" + } + }, + "expectError": { + "isClientError": true + } } - }, - "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 - } - ] + "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 } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isClientError": true } } - }, - "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 - } - ] + "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 } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isClientError": true } } - }, - "assert": { - "error": "ChunkIsMissing" - } + ] }, { - "description": "Download when an intermediate chunk is the wrong size", - "arrange": { - "data": [ - { - "update": "fs.chunks", - "updates": [ + "description": "download when an intermediate chunk is the wrong size", + "operations": [ + { + "name": "bulkWrite", + "object": "bucket0_chunks_collection", + "arguments": { + "requests": [ { - "q": { - "files_id": { - "$oid": "000000000000000000000005" + "updateOne": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 1 }, - "n": 1 - }, - "u": { - "$set": { - "data": { - "$hex": "556677" + "update": { + "$set": { + "data": { + "$binary": { + "base64": "VWZ3", + "subType": "00" + } + } } } } }, { - "q": { - "files_id": { - "$oid": "000000000000000000000005" + "updateOne": { + "filter": { + "files_id": { + "$oid": "000000000000000000000005" + }, + "n": 2 }, - "n": 2 - }, - "u": { - "$set": { - "data": { - "$hex": "8899aa" + "update": { + "$set": { + "data": { + "$binary": { + "base64": "iJmq", + "subType": "00" + } + } } } } } ] + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2 } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isClientError": true } } - }, - "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" - } + "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 } - ] - }, - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000005" + }, + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + }, + "expectError": { + "isClientError": true } } - }, - "assert": { - "error": "ChunkIsWrongSize" - } + ] }, { - "description": "Download legacy file with no name", - "act": { - "operation": "download", - "arguments": { - "id": { - "$oid": "000000000000000000000006" + "description": "download legacy file with no name", + "operations": [ + { + "name": "download", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000006" + } }, - "options": {} - } - }, - "assert": { - "result": { - "$hex": "1122" + "expectResult": { + "$$matchesHexBytes": "1122" + } } - } + ] } ] } diff --git a/source/gridfs/tests/download.yml b/source/gridfs/tests/download.yml index d6aeef2b7e..b9b36a5f48 100644 --- a/source/gridfs/tests/download.yml +++ b/source/gridfs/tests/download.yml @@ -1,192 +1,223 @@ -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" } } +description: "gridfs-download" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-0" + metadata: {} + - _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-0-with-empty-chunk" + metadata: {} + - _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-2" + metadata: {} + - _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-8" + metadata: {} + - _id: { "$oid": "000000000000000000000005" } + length: 10 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "length-10" + metadata: {} + - _id: { "$oid": "000000000000000000000006" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + # filename is intentionally omitted + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + 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" } } # hex: 1122 + - _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "ESIzRA==", "subType": "00" } } # hex: 11223344 + - _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 1 + data: { "$binary": { "base64": "VWZ3iA==", "subType": "00" } } # hex: 55667788 + - _id: { "$oid": "000000000000000000000005" } + files_id: { "$oid": "000000000000000000000005" } + n: 0 + data: { "$binary": { "base64": "ESIzRA==", "subType": "00" } } # hex: 11223344 + - _id: { "$oid": "000000000000000000000006" } + files_id: { "$oid": "000000000000000000000005" } + n: 1 + data: { "$binary": { "base64": "VWZ3iA==", "subType": "00" } } # hex: 55667788 + - _id: { "$oid": "000000000000000000000007" } + files_id: { "$oid": "000000000000000000000005" } + n: 2 + data: { "$binary" : { "base64": "mao=", "subType" : "00" } } # hex: 99aa + - _id: { "$oid": "000000000000000000000008" } + files_id: { "$oid": "000000000000000000000006" } + n: 0 + data: { "$binary": { "base64": "ESI=", "subType": "00" } } # 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" } + - 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: { isClientError: true } # FileNotFound + - 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: { isClientError: true } # ChunkIsMissing + - 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: { isClientError: true } # ChunkIsMissing + - 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" } } } # hex: 556677 + - updateOne: + filter: + files_id: { $oid: "000000000000000000000005" } + n: 2 + update: + $set: { data: { "$binary": { "base64": "iJmq", "subType": "00" } } } # hex: 8899aa + expectResult: + matchedCount: 2 + modifiedCount: 2 + - name: download + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000005" } + expectError: { isClientError: true } # ChunkIsWrongSize + - 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" } } } # hex: 99 + expectResult: + matchedCount: 1 + modifiedCount: 1 + - name: download + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000005" } + expectError: { isClientError: true } # ChunkIsWrongSize + - description: "download legacy file with no name" + operations: + - name: download + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000006" } + expectResult: { $$matchesHexBytes: "1122" } diff --git a/source/gridfs/tests/downloadByName.json b/source/gridfs/tests/downloadByName.json new file mode 100644 index 0000000000..45abaf7b42 --- /dev/null +++ b/source/gridfs/tests/downloadByName.json @@ -0,0 +1,315 @@ +{ + "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" + }, + "filename": "abc", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-02T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-03T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-04T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000005" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-05T00:00:00.000Z" + }, + "filename": "abc", + "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": { + "isClientError": true + } + } + ] + }, + { + "description": "downloadByName when revision does not exist", + "operations": [ + { + "name": "downloadByName", + "object": "bucket0", + "arguments": { + "filename": "abc", + "revision": 999 + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/downloadByName.yml b/source/gridfs/tests/downloadByName.yml new file mode 100644 index 0000000000..26d1301a61 --- /dev/null +++ b/source/gridfs/tests/downloadByName.yml @@ -0,0 +1,144 @@ +description: "gridfs-downloadByName" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - _id: { $oid: "000000000000000000000001" } + length: 1 + chunkSize: 4 + uploadDate: { $date: "1970-01-01T00:00:00.000Z" } + filename: "abc" + metadata: {} + - _id: { $oid: "000000000000000000000002" } + length: 1 + chunkSize: 4 + uploadDate: { $date: "1970-01-02T00:00:00.000Z" } + filename: "abc" + metadata: {} + - _id: { $oid: "000000000000000000000003" } + length: 1 + chunkSize: 4 + uploadDate: { $date: "1970-01-03T00:00:00.000Z" } + filename: "abc" + metadata: {} + - _id: { $oid: "000000000000000000000004" } + length: 1 + chunkSize: 4 + uploadDate: { $date: "1970-01-04T00:00:00.000Z" } + filename: "abc" + metadata: {} + - _id: { $oid: "000000000000000000000005" } + length: 1 + chunkSize: 4 + uploadDate: { $date: "1970-01-05T00:00:00.000Z" } + filename: "abc" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - _id: { $oid: "000000000000000000000001" } + files_id: { $oid: "000000000000000000000001" } + n: 0 + data: { "$binary": { "base64": "EQ==", "subType": "00" } } # hex: 11 + - _id: { $oid: "000000000000000000000002" } + files_id: { $oid: "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "Ig==", "subType": "00" } } # hex: 22 + - _id: { $oid: "000000000000000000000003" } + files_id: { $oid: "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "Mw==", "subType": "00" } } # hex: 33 + - _id: { $oid: "000000000000000000000004" } + files_id: { $oid: "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "RA==", "subType": "00" } } # hex: 44 + - _id: { $oid: "000000000000000000000005" } + files_id: { $oid: "000000000000000000000005" } + n: 0 + data: { "$binary": { "base64": "VQ==", "subType": "00" } } # hex: 55 + +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: { isClientError: true } # FileNotFound + - description: "downloadByName when revision does not exist" + operations: + - name: downloadByName + object: *bucket0 + arguments: + filename: "abc" + revision: 999 + expectError: { isClientError: true } # RevisionNotFound diff --git a/source/gridfs/tests/download_by_name.json b/source/gridfs/tests/download_by_name.json deleted file mode 100644 index ecc8c9e2cc..0000000000 --- a/source/gridfs/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/source/gridfs/tests/download_by_name.yml b/source/gridfs/tests/download_by_name.yml deleted file mode 100644 index 6e90c13e33..0000000000 --- a/source/gridfs/tests/download_by_name.yml +++ /dev/null @@ -1,113 +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/source/gridfs/tests/rename.json b/source/gridfs/tests/rename.json new file mode 100644 index 0000000000..08064d4a5c --- /dev/null +++ b/source/gridfs/tests/rename.json @@ -0,0 +1,179 @@ +{ + "description": "gridfs-rename", + "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" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "rename by id", + "operations": [ + { + "name": "rename", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + }, + "newFilename": "newfilename" + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "rename when file id does not exist", + "operations": [ + { + "name": "rename", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000003" + }, + "newFilename": "newfilename" + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/rename.yml b/source/gridfs/tests/rename.yml new file mode 100644 index 0000000000..2203532998 --- /dev/null +++ b/source/gridfs/tests/rename.yml @@ -0,0 +1,78 @@ +description: "gridfs-rename" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + +tests: + - description: "rename by id" + operations: + - name: rename + object: *bucket0 + arguments: + id: { "$oid": "000000000000000000000001" } + newFilename: newfilename + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - <<: *file1 + filename: newfilename + - <<: *file2 + filename: filename + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - description: "rename when file id does not exist" + operations: + - name: rename + object: *bucket0 + arguments: + id: { "$oid": "000000000000000000000003" } + newFilename: newfilename + expectError: { isClientError: true } # FileNotFound diff --git a/source/gridfs/tests/renameByName.json b/source/gridfs/tests/renameByName.json new file mode 100644 index 0000000000..26f04fb9e0 --- /dev/null +++ b/source/gridfs/tests/renameByName.json @@ -0,0 +1,313 @@ +{ + "description": "gridfs-renameByName", + "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" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "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": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "rename when multiple revisions of the file exist", + "operations": [ + { + "name": "renameByName", + "object": "bucket0", + "arguments": { + "filename": "filename", + "newFilename": "newfilename" + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "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": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "rename when file name does not exist", + "operations": [ + { + "name": "renameByName", + "object": "bucket0", + "arguments": { + "filename": "missing-file", + "newFilename": "newfilename" + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/renameByName.yml b/source/gridfs/tests/renameByName.yml new file mode 100644 index 0000000000..f628ca9c5f --- /dev/null +++ b/source/gridfs/tests/renameByName.yml @@ -0,0 +1,113 @@ +description: "gridfs-renameByName" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file3 + _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file4 + _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "otherfilename" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk0 + _id: { "$oid": "000000000000000000000002" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk0 + _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk1 + _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 1 + data: { "$binary": { "base64": "", "subType": "00" } } + +tests: + - description: "rename when multiple revisions of the file exist" + operations: + - name: renameByName + object: *bucket0 + arguments: + filename: filename + newFilename: newfilename + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - <<: *file1 + filename: newfilename + - <<: *file2 + filename: newfilename + - <<: *file3 + filename: newfilename + - <<: *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 + - *file4_chunk0 + - *file4_chunk1 + - description: "rename when file name does not exist" + operations: + - name: renameByName + object: *bucket0 + arguments: + filename: missing-file + newFilename: newfilename + expectError: { isClientError: true } # FileNotFound diff --git a/source/gridfs/tests/upload-disableMD5.json b/source/gridfs/tests/upload-disableMD5.json new file mode 100644 index 0000000000..d5a9d6f4ab --- /dev/null +++ b/source/gridfs/tests/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/source/gridfs/tests/upload-disableMD5.yml b/source/gridfs/tests/upload-disableMD5.yml new file mode 100644 index 0000000000..4f77f5a1a2 --- /dev/null +++ b/source/gridfs/tests/upload-disableMD5.yml @@ -0,0 +1,92 @@ +description: "gridfs-upload-disableMD5" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: [] + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: [] + +# Note: these tests utilize the transitional "disableMD5" option. Drivers that +# do not support the option should skip this file. +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 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" } } # hex 11 diff --git a/source/gridfs/tests/upload.json b/source/gridfs/tests/upload.json index ecec0c5488..3c1644653a 100644 --- a/source/gridfs/tests/upload.json +++ b/source/gridfs/tests/upload.json @@ -1,466 +1,547 @@ { - "data": { - "files": [], - "chunks": [] - }, - "tests": [ + "description": "gridfs-upload", + "schemaVersion": "1.0", + "createEntities": [ { - "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" - } - ] - } - ] + "client": { + "id": "client0" } }, { - "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" - } - } - ] - } - ] + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" } }, { - "description": "Upload when length is 3", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "112233" - }, - "options": { + "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": [] } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 3, - "chunkSize": 4, - "uploadDate": "*actual", - "md5": "bafae3a174ab91fc70db7a6aa50f4f52", - "filename": "filename" - } - ] + ] + }, + { + "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": {} }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "112233" + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" } } - ] - } - ] - } + } + ] + } + ] }, { - "description": "Upload when length is 4", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11223344" - }, - "options": { + "description": "upload when length is 3", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "112233" + }, "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" + "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 5", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "1122334455" - }, - "options": { + "description": "upload when length is 4", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11223344" + }, "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" - } + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "length": 4, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" }, - { - "_id": "*actual", - "files_id": "*result", - "n": 1, - "data": { - "$hex": "55" + "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 8", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "1122334455667788" - }, - "options": { + "description": "upload when length is 5", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + }, "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" + "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" }, - { - "_id": "*actual", - "files_id": "*result", - "n": 1, - "data": { - "$hex": "55667788" + "n": 1, + "data": { + "$binary": { + "base64": "VQ==", + "subType": "00" } } - ] - } - ] - } + } + ] + } + ] }, { - "description": "Upload when contentType is provided", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" + "description": "upload when length is 8", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455667788" + }, + "chunkSizeBytes": 4 }, - "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" - } - ] + "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 + } }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" + "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 metadata is provided", - "act": { - "operation": "upload", - "arguments": { - "filename": "filename", - "source": { - "$hex": "11" - }, - "options": { + "description": "upload when metadata is provided", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "11" + }, "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": "" + "expectResult": { + "$$type": "objectId" }, - "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" + "saveResultAsEntity": "uploadedObjectId" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {} }, - "options": { - "chunkSizeBytes": 4, - "disableMD5": true - } - } - }, - "assert": { - "result": "&result", - "data": [ - { - "insert": "expected.files", - "documents": [ - { - "_id": "*result", - "length": 1, - "chunkSize": 4, - "uploadDate": "*actual", - "filename": "filename" + "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": {} }, - { - "insert": "expected.chunks", - "documents": [ - { - "_id": "*actual", - "files_id": "*result", - "n": 0, - "data": { - "$hex": "11" + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "uploadedObjectId" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" } } - ] - } - ] - } + } + ] + } + ] } ] } diff --git a/source/gridfs/tests/upload.yml b/source/gridfs/tests/upload.yml index cb07e2a9d6..96fc5e9863 100644 --- a/source/gridfs/tests/upload.yml +++ b/source/gridfs/tests/upload.yml @@ -1,192 +1,256 @@ -data: - files: [] - chunks: [] +description: "gridfs-upload" -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" } +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks - ] } - - - { 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" } } - ] } +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: [] + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: [] + +# Note: Uploaded files and chunks include ObjectIds, which we cannot match with +# "outcome" since it does not allow operators. Instead, these tests will use +# find operations to assert the contents of uploaded files and chunks. +tests: + - description: "upload when length is 0" + operations: + - name: upload + object: *bucket0 + arguments: + filename: "filename" + source: { $$hexBytes: "" } + chunkSizeBytes: 4 + expectResult: { $$type: objectId } + saveResultAsEntity: &uploadedObjectId uploadedObjectId + - name: find + object: *bucket0_files_collection + arguments: + filter: {} + expectResult: + - _id: { $$matchesEntity: *uploadedObjectId } + length: 0 + chunkSize: 4 + uploadDate: { $$type: date } + # The md5 field is deprecated so some drivers do not calculate it when uploading files. + 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" } } # hex 11 + - 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" } } # hex 112233 + - 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" } } # hex 11223344 + - 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 to ensure chunks are returned in a deterministic order + sort: { n: 1 } + expectResult: + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *uploadedObjectId } + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex 11223344 + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *uploadedObjectId } + n: 1 + data: { $binary: { base64: "VQ==", subType: "00" } } # hex 55 + - 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 to ensure chunks are returned in a deterministic order + sort: { n: 1 } + expectResult: + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *uploadedObjectId } + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex 11223344 + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *uploadedObjectId } + n: 1 + data: { $binary: { base64: "VWZ3iA==", subType: "00" } } # hex 55667788 + - 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" } } # hex 11 diff --git a/source/index-management.rst b/source/index-management.rst deleted file mode 100644 index 96b93d408e..0000000000 --- a/source/index-management.rst +++ /dev/null @@ -1,724 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -================ -Index Management -================ - -:Spec: 79 -:Title: Index Management -:Authors: Durran Jordan -:Status: Approved -:Type: Standards -:Minimum Server Version: 2.4 -:Last Modified: Oct 11, 2016 -:Version: 1.5 - -.. contents:: - --------- - -Specification -============= - -The index management spec defines a set of behaviour in the drivers for creating, removing and viewing indexes in a collection. It defines implementation details when required but also provides flexibilty in the driver in that one or both of 2 unique APIs can be chosen to be implemented. - - ------------ -Definitions ------------ - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. - - -Terms ------ - -Collection - The term ``Collection`` references the object in the driver that represents a collection on the server. - -Cursor - The term ``Cursor`` references the driver's cursor object. - -Iterable - The term ``Iterable`` is to describe an object that is a sequence of elements that can be iterated over. - -Document - The term ``Document`` refers to the implementation in the driver's language of a BSON document. - -Result - The term ``Result`` references the object that is normally returned by the driver as the result of a command execution. In the case of situations where an actual command is not executed, rather an insert or a query, an object that adheres to the same interface must be returned with as much information as possible that could be obtained from the operation. - --------- -Guidance --------- - -Documentation -------------- - -The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation for the driver. - - -Operations ----------- - -All drivers MUST offer at least one of the sections of operations, the Standard API or the Index View API. The driver MAY elect to have both. Implementation details are noted in the comments when a specific implementation is required. Within each API, all methods are REQUIRED unless noted otherwise in the comments. - - -Operation Parameters --------------------- - -All drivers MUST include the specified parameters in each operation, with the exception of the options parameter which is OPTIONAL. As of 3.4 (see https://jira.mongodb.org/browse/SERVER-769) the server validates options passed to the createIndexes command -- drivers should be aware when testing that passing arbitrary options when the driver does not validate them could fail on the server. - -Naming ------- - -All drivers MUST name operations and parameters as defined in the following sections. Exceptions to this rule are noted in the appropriate section. Class and interface names may vary according to the driver and language best practices. - -Deviations -********** - -Acceptable naming deviations should fall within the basic style of the language. For example, ``createIndex`` would be a required name in Java, where camel-case method names are used, but in Ruby ``create_index`` would be acceptable. - - -Index Name Generation ---------------------- - -When the client generates a name for an index based on the keys, The driver MUST generate the name as key-direction pairs, separated by underscores. For example, the key ``{ name: 1, dob: -1 }`` MUST generate an index name of ``name_1_dob_-1``. - -Note there is one exception to this rule on the ``_id`` field. The server uses an index name with no direction, ``_id_``, which cannot be overridden. - ------------- -Standard API ------------- - -.. code:: typescript - - interface Collection { - - /** - * This is a convenience method for creating a single index. This MUST call the - * createIndexes method and pass the provided specification document in a - * sequence to that method with the same options. - * - * @return The name of the created index. - * - * @note Drivers MAY opt to implement this method signature, the signature that - * takes an IndexModel as a parameter, or for those languages with method - * overloading MAY decide to implement both. - */ - createIndex(keys: Document, options: IndexOptions): String; - - /** - * @see Comments above. - */ - createIndex(model: IndexModel): String - - /** - * Creates multiple indexes in the collection. - * - * For MongoDB 2.6 and higher this method MUST execute a createIndexes command. - * - * For MongoDB 2.4 this method MUST insert the index specifications directly into - * the system.indexes collection. The write concern provided provided to the server - * MUST be { w: 1 }. - * - * The driver MAY choose NOT to support creating indexes on 2.4 and if so, MUST - * document the method as such. - * - * Note that in MongoDB server versions >= 3.0.0, the server will create the - * indexes in parallel. - * - * As of 3.4 (see https://jira.mongodb.org/browse/SERVER-769) the server validates - * options passed to the createIndexes command. - * - * @return The names of all the indexes that were created. - */ - createIndexes(models: Iterable): Iterable; - - /** - * Drops a single index from the collection by the index name. - * - * In all server versions this MUST execute a dropIndexes command. - * - * @note If the string passed is '*', the driver MUST raise an error since - * more than one index would be dropped. - */ - dropIndex(name: String): Result; - - /** - * Attempts to drop a single index from the collection given the keys and options. - * - * In all server versions this MUST execute a dropIndexes command. - * - * This is OPTIONAL until partial indexes are implemented. - * - * @note Drivers MAY opt to implement this method signature, the signature that - * takes an IndexModel as a parameter, or for those languages with method - * overloading MAY decide to implement both. - */ - dropIndex(keys: Document, options: IndexOptions): Result; - - /** - * @see Comments above. - */ - dropIndex(model: IndexModel): Result; - - /** - * Drops all indexes in the collection. - */ - dropIndexes(): Result; - - /** - * Gets index information for all indexes in the collection. This should be - * implemented as specified in the Enumerate Indexes specification: - * - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst - * - * @note Where the enumerate indexes spec gives the option of returning a - * Cursor or Array for backwards compatibility - here the driver MUST always - * return a Cursor. - */ - listIndexes(): Cursor; - } - - -Examples --------- - -Create an index in a collection. - -Ruby: - -.. code:: ruby - - collection.create_index({ name: 1 }, { unique: true }) - -Java: - -.. code:: java - - collection.createIndex(new Document("name", 1), new IndexOptions().unique(true)); - -Produces the shell equivalent (>= 2.6.0) of: - -.. code:: javascript - - db.runCommand({ - createIndexes: "users", - indexes: [ - { key: { name: 1 }, name: "name_1", unique: true } - ] - }); - -Create multiple indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.create_indexes([ - { key: { name: 1 }, unique: true }, - { key: { age: -1 }, name: "age" } - ]) - -Java: - -.. code:: java - - collection.createIndexes(asList( - new IndexModel(new Document("name", 1), new IndexOptions().unique(true)), - new IndexModel(new Document("age", -1), new IndexOptions().name("age")) - )); - -Produces the shell equivalent (>= 2.6.0) of: - -.. code:: javascript - - db.runCommand({ - createIndexes: "users", - indexes: [ - { key: { name: 1 }, name: "name_1", unique: true }, - { key: { age: -1 }, name: "age" } - ] - }); - -Drop an index in a collection. - -Ruby: - -.. code:: ruby - - collection.drop_index("age") - -Java: - -.. code:: java - - collection.dropIndex("age"); - -Produces the shell equivalent of: - -.. code:: javascript - - db.runCommand({ dropIndexes: "users", index: "age" }); - -Drop all indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.drop_indexes - -Java: - -.. code:: java - - collection.dropIndexes(); - -Produces the shell equivalent of: - -.. code:: javascript - - db.runCommand({ dropIndexes: "users", index: "*" }); - -List all indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.list_indexes - -Java: - -.. code:: java - - collection.listIndexes(); - -Produces the shell equivalent (>= 3.0.0) of: - -.. code:: javascript - - db.runCommand({ listIndexes: "users" }); - --------------- -Index View API --------------- - -.. code:: typescript - - interface Collection { - - /** - * Returns the index view for this collection. - */ - indexes(): IndexView; - } - - interface IndexView extends Iterable { - - /** - * Enumerates the index information for all indexes in the collection. This should be - * implemented as specified in the Enumerate Indexes specification, although the naming - * requirement is dropped in favor of the driver language standard for handling iteration - * over a sequence of objects. - * - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst - * - * @note For drivers that cannot make the IndexView iterable, they MUST implement a list - * method. See below. - */ - iterator(): Iterator; - - /** - * For drivers that cannot make IndexView iterable, they MUST implement this method to - * return a list of indexes. In the case of async drivers, this MAY return a Future - * or language/implementation equivalent. - * - * @note Where the enumerate indexes spec gives the option of returning a - * Cursor or Array for backwards compatibility - here the driver MUST always - * return a Cursor. - */ - list(): Cursor; - - /** - * This is a convenience method for creating a single index. This MUST call the - * createMany method and pass the provided specification document in a - * sequence to that method with the same options. - * - * @return The name of the created index. - * - * @note Drivers MAY opt to implement this method signature, the signature that - * takes an IndexModel as a parameter, or for those languages with method - * overloading MAY decide to implement both. - */ - createOne(keys: Document, options: IndexOptions): String; - - /** - * @see Comments above. - */ - createOne(model: IndexModel): String - - /** - * Creates multiple indexes in the collection. - * - * For MongoDB 2.6 and higher this method MUST execute a createIndexes command. - * - * For MongoDB 2.4 this method MUST insert the index specifications directly into - * the system.indexes collection. The write concern provided provided to the server - * MUST be { w: 1 }. - * - * The driver MAY choose NOT to support creating indexes on 2.4 and if so, MUST - * document the method as such. - * - * @return The names of the created indexes. - * - * @note Each specification document becomes the "key" field in the document that - * is inserted or the command. - * - * Note that in MongoDB server versions >= 3.0.0, the server will create the - * indexes in parallel. - */ - createMany(models: Iterable): Iterable; - - /** - * Drops a single index from the collection by the index name. - * - * In all server versions this MUST execute a dropIndexes command. - * - * @note If the string passed is '*', the driver MUST raise an error since - * more than one index would be dropped. - */ - dropOne(name: String): Result; - - /** - * Attempts to drop a single index from the collection given the keys and options. - * This is OPTIONAL until partial indexes are implemented. - * - * In all server versions this MUST execute a dropIndexes command. - * - * @note Drivers MAY opt to implement this method signature, the signature that - * takes an IndexModel as a parameter, or for those languages with method - * overloading MAY decide to implement both. - */ - dropOne(keys: Document, options: IndexOptions): Result; - - /** - * @see Comments above. - */ - dropOne(model: IndexModel): Result; - - /** - * Drops all indexes in the collection. - */ - dropAll(): Result; - } - - -Examples --------- - -Create an index in a collection. - -Ruby: - -.. code:: ruby - - collection.indexes.create_one({ name: 1 }, { unique: true }) - -Java: - -.. code:: java - - collection.indexes().createOne(new Document("name", 1), new IndexOptions().unique(true)); - -Produces the shell equivalent (>= 2.6.0) of: - -.. code:: javascript - - db.runCommand({ - createIndexes: "users", - indexes: [ - { key: { name: 1 }, name: "name_1", unique: true } - ] - }); - -Create multiple indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.indexes.create_many([ - { key: { name: 1 }, unique: true }, - { key: { age: -1 }, name: "age" } - ]) - -Java: - -.. code:: java - - collection.indexes().createMany(asList( - new IndexModel(new Document("name", 1), new IndexOptions().unique(true), - new IndexModel(new Document("age", -1), new IndexOptions().name("age") - )); - -Produces the shell equivalent (>= 2.6.0) of: - -.. code:: javascript - - db.runCommand({ - createIndexes: "users", - indexes: [ - { key: { name: 1 }, name: "name_1", unique: true }, - { key: { age: -1 }, name: "age" } - ] - }); - -Drop an index in a collection. - -Ruby: - -.. code:: ruby - - collection.indexes.drop_one("age") - -Java: - -.. code:: java - - collection.indexes().dropOne("age"); - -Produces the shell equivalent of: - -.. code:: javascript - - db.runCommand({ dropIndexes: "users", index: "age" }); - -Drop all indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.indexes.drop_all - -Java: - -.. code:: java - - collection.indexes().dropAll(); - -Produces the shell equivalent of: - -.. code:: javascript - - db.runCommand({ dropIndexes: "users", index: "*" }); - -List all indexes in a collection. - -Ruby: - -.. code:: ruby - - collection.indexes.each do |document| - p document - end - -Java: - -.. code:: java - - for (BsonDocument document: collection.indexes()) { - /* ... */ - } - -Produces the shell equivalent (>= 3.0.0) of: - -.. code:: javascript - - var indexes = db.runCommand({ listIndexes: "users" }); - for (index in indexes) { - console.log(index); - } - - ---------------------- -Common API Components ---------------------- - -.. code:: typescript - - interface IndexModel { - - /** - * Contains the required keys for the index. - */ - keys: Document; - - /** - * Contains the options for the index. - */ - options: IndexOptions; - } - - interface IndexOptions { - - /** - * Optionally tells the server to build the index in the background and not block - * other tasks. - */ - background: Boolean; - - /** - * Optionally specifies the length in time, in seconds, for documents to remain in - * a collection. - */ - expireAfter: Int32; - - /** - * Optionally specify a specific name for the index outside of the default generated - * name. If none is provided then the name is generated in the format "[field]_[direction]". - * - * Note that if an index is created for the same key pattern with different collations, - * a name must be provided by the user to avoid ambiguity. - * - * @example For an index of name: 1, age: -1, the generated name would be "name_1_age_-1". - */ - name: String; - - /** - * Optionally tells the index to only reference documents with the specified field in - * the index. - */ - sparse: Boolean; - - /** - * Optionally used only in MongoDB 3.0.0 and higher. Specifies the storage engine - * to store the index in. - */ - storageEngine: String; - - /** - * Optionally forces the index to be unique. - */ - unique: Boolean; - - /** - * Optionally specifies the index version number, either 0 or 1. - */ - version: Int32; - - /** - * Optionally specifies the default language for text indexes. - * Is english if none is provided. - */ - defaultLanguage: String; - - /** - * Optionally Specifies the field in the document to override the language. - */ - languageOverride: String; - - /** - * Optionally provides the text index version number. - * - * MongoDB 2.4 can only support version 1. - * - * MongoDB 2.6 and higher may support version 1 or 2. - */ - textVersion: Int32; - - /** - * Optionally specifies fields in the index and their corresponding weight values. - */ - weights: Document; - - /** - * Optionally specifies the 2dsphere index version number. - * - * MongoDB 2.4 can only support version 1. - * - * MongoDB 2.6 and higher may support version 1 or 2. - */ - sphereVersion: Int32; - - /** - * Optionally specifies the precision of the stored geo hash in the 2d index, from 1 to 32. - */ - bits: Int32; - - /** - * Optionally sets the maximum boundary for latitude and longitude in the 2d index. - */ - max: Double; - - /** - * Optionally sets the minimum boundary for latitude and longitude in the index in a - * 2d index. - */ - min: Double; - - /** - * Optionally specifies the number of units within which to group the location values - * in a geo haystack index. - */ - bucketSize: Int32; - - /** - * Optionally specifies a filter for use in a partial index. Only documents that match the - * filter expression are included in the index. - */ - partialFilterExpression: Document; - - /** - * Optionally specifies a collation to use for the index in MongoDB 3.4 and higher. - * If not specified, no collation is sent and the default collation of the collection - * server-side is used. - */ - collation: Document; - } - - ----------------------- -Backwards Compatibilty ----------------------- - -This specification makes no attempts to be backwards compatible as the target drivers to implement this spec are all next generation. - - ---------- -Q & A ---------- - -Q: Where is write concern? - The createIndexes and dropIndexes commands take a write concern that indicates how the write is acknowledged. Since all operations defined in this specification are performed on a collection, it's uncommon that two different index operations on the same collection would use a different write concern. As such, the most natural place to indicate write concern is on the client, the database, or the collection itself and not the operations within it. - - However, it might be that a driver needs to expose write concern to a user per operation for various reasons. It is permitted to allow a write concern option, but the driver may need to provide a separate parameter for some helpers, since the writeConcern is a top-level command option, not part of an indexModel's indexOptions. For example, whereas the write concern could possibly be included in the indexOptions parameter for createIndex() and extracted in the method implementation, it would be ambiguous to specify write concern for one or more models passed to createIndexes(). The driver would therefore most likely choose to allow the option as a separate parameter for createIndexes(). - -Q: Do the index operations support maxTimeMS? - The listIndexes(), createIndexes() and dropIndexes() commands allow the maxTimeMS option, though supporting it as an option is not addressed by this specification. As is discussed above for write concern, the driver may choose to expose this top-level command option; however, for some helpers, the driver may need a separate command options parameter. For other helpers, it may choose to extract maxTimeMS from the indexOptions. - -Changelog ---------- - -17 SEP 2015: - - Added ``partialFilterExpression`` attribute to ``IndexOptions`` in order to support partial indexes. - - Fixed "provides" typo. -19 MAY 2016: - - Added ``collation`` attribute to ``IndexOptions`` in order to support setting a collation on an index. -8 AUG 2016: - - Fixed ``collation`` language to not mention a collection default. -11 OCT 2016: - - Added note on 3.4 servers validation options passed to ``createIndexes``. -11 OCT 2016: - - Add note on server generated name for the _id index. -31 MAY 2017: - - Add Q & A addressing write concern and maxTimeMS option. -7 JUN 2017: - - Include listIndexes() in Q&A about maxTimeMS. diff --git a/source/index-management/index-management.md b/source/index-management/index-management.md new file mode 100644 index 0000000000..6e1872ebde --- /dev/null +++ b/source/index-management/index-management.md @@ -0,0 +1,1249 @@ +# Index Management + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Specification + +The index management spec defines a set of behaviour in the drivers for creating, removing and viewing indexes in a +collection. It defines implementation details when required but also provides flexibility in the driver in that one or +both of 2 unique APIs can be chosen to be implemented. + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +#### Terms + +**Collection** + +The term `Collection` references the object in the driver that represents a collection on the server. + +**Cursor** + +The term `Cursor` references the driver's cursor object. + +**Iterable** + +The term `Iterable` is to describe an object that is a sequence of elements that can be iterated over. + +**Document** + +The term `Document` refers to the implementation in the driver's language of a BSON document. + +**Result** + +The term `Result` references the object that is normally returned by the driver as the result of a command execution. In +the case of situations where an actual command is not executed, rather an insert or a query, an object that adheres to +the same interface must be returned with as much information as possible that could be obtained from the operation. + +### Guidance + +#### Documentation + +The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation +for the driver. + +#### Operations + +All drivers MUST offer at least one of the sections of operations, the Standard API or the Index View API. The driver +MAY elect to have both. Implementation details are noted in the comments when a specific implementation is required. +Within each API, all methods are REQUIRED unless noted otherwise in the comments. + +#### Operation Parameters + +All drivers MUST include the specified parameters in each operation. This does not preclude a driver from offering more. +A driver SHOULD NOT require a user to specify the options parameter if they wish to use the server defaults. + +As of 3.4 (see ) the server validates options passed to the `createIndexes` +command -- drivers should be aware when testing that passing arbitrary options when the driver does not validate them +could fail on the server. + +#### Deviations + +A non-exhaustive list of acceptable deviations are as follows: + +- Using named parameters in place of an options hash or class. For instance, + `collection.create_index({x: 1}, commit_quorum="majority")`. +- When using an `Options` class, if multiple `Options` classes are structurally equatable, it is permissible to + consolidate them into one with a clear name. For instance, it would be permissible to use the name + `CreateIndexOptions` as the options for `createIndex` and `createIndexes`. + +#### Naming + +All drivers MUST name operations and parameters as defined in the following sections. Exceptions to this rule are noted +in the appropriate section. Class and interface names may vary according to the driver and language best practices. + +#### Naming Deviations + +When deviating from a defined name, an author should consider if the altered name is recognizable and discoverable to +the user of another driver. + +A non-exhaustive list of acceptable naming deviations are as follows: + +- Using "maxTimeMS" as an example, .NET would use "MaxTime" where it's type is a TimeSpan structure that includes units. + However, calling it "MaximumTime" would not be acceptable. +- Using "CreateIndexOptions" as an example, Javascript wouldn't need to name it while other drivers might prefer to call + it "CreateIndexArgs" or "CreateIndexParams". +- Acceptable naming deviations should fall within the basic style of the language. For example, `createIndex` would be a + required name in Java, where camel-case method names are used, but in Ruby `create_index` would be acceptable. + +#### Index Name Generation + +When the client generates a name for an index based on the keys, the driver MUST generate the name as key-direction +pairs, separated by underscores. For example, the key `{ name: 1, dob: -1 }` MUST generate an index name of +`name_1_dob_-1`. + +Note there is one exception to this rule on the `_id` field. The server uses an index name with no direction, `_id_`, +which cannot be overridden. + +#### Timeouts + +Drivers MUST enforce timeouts for all operations per the +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. All +operations that return cursors MUST support the timeout options documented in the +[Cursors](../client-side-operations-timeout/client-side-operations-timeout.md#cursors) section of that specification. + +### Standard API + +```typescript +interface Collection { + + /** + * This is a convenience method for creating a single index. This MUST call the + * createIndexes method and pass the provided specification document in a + * sequence to that method with the same options. + * + * @return The name of the created index. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an IndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the two options types into a single one. If the options are + * explicitly typed, the combined options type MUST be named CreateIndexOptions or an acceptable + * variation. + */ + createIndex(keys: Document, indexOptions: Optional, options: Optional): String; + + /** + * @see Comments above. + */ + createIndex(model: IndexModel, options: Optional): String + + /** + * Creates multiple indexes in the collection. + * + * In all server versions, this MUST execute a createIndexes command. + * + * @return The names of all the indexes that were created. + */ + createIndexes(models: Iterable, options: Optional): Iterable; + + /** + * Drops a single index from the collection by the index name. + * + * In all server versions this MUST execute a dropIndexes command. + * + * @note If the string passed is '*', the driver MUST raise an error since + * more than one index would be dropped. + */ + dropIndex(name: String, options: Optional): Result; + + /** + * Attempts to drop a single index from the collection given the keys and options. + * + * In all server versions this MUST execute a dropIndexes command. + * + * This is OPTIONAL until partial indexes are implemented. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an IndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the two options types into a single one. If the options are + * explicitly typed, the combined options type MUST be named DropIndexOptions or an acceptable + * variation. + */ + dropIndex(keys: Document, indexOptions: IndexOptions, options: Optional): Result; + + /** + * @see Comments above. + */ + dropIndex(model: IndexModel, options: Optional): Result; + + /** + * Drops all indexes in the collection. + */ + dropIndexes(options: Optional): Result; + + /** + * Gets index information for all indexes in the collection. The behavior for + * enumerating indexes is described in the :ref:`Enumerating Indexes` section. + * + */ + listIndexes(options: Optional): Cursor; +} + +interface CreateIndexOptions { + /** + * Specifies how many data-bearing members of a replica set, including the primary, must + * complete the index builds successfully before the primary marks the indexes as ready. + * + * This option accepts the same values for the "w" field in a write concern plus "votingMembers", + * which indicates all voting data-bearing nodes. + * + * This option is only supported by servers >= 4.4. Drivers MUST manually raise an error if this option + * is specified when creating an index on a pre 4.4 server. See the Q&A section for the rationale behind this. + * + * @note This option is sent only if the caller explicitly provides a value. The default is to not send a value. + * + * @since MongoDB 4.4 + */ + commitQuorum: Optional; + + /** + * The maximum amount of time to allow the index build to take before returning an error. + * + * @note This option is sent only if the caller explicitly provides a value. The default is to not send a value. + */ + maxTimeMS: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; + + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/createIndexes/ + * + * @since MongoDB 4.4 + */ + comment: Optional; +} + +interface CreateIndexesOptions { + // same as CreateIndexOptions +} + +interface DropIndexOptions { + /** + * The maximum amount of time to allow the index drop to take before returning an error. + * + * @note This option is sent only if the caller explicitly provides a value. The default is to not send a value. + */ + maxTimeMS: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; + +/** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * @see https://www.mongodb.com/docs/manual/reference/command/dropIndexes/ + * + * @since MongoDB 4.4 + */ + comment: Optional; +} + +interface DropIndexesOptions { + // same as DropIndexOptions +} +``` + +#### Examples + +Create an index in a collection. + +Ruby: + +```ruby +collection.create_index({ name: 1 }, { unique: true }) +``` + +Java: + +```java +collection.createIndex(new Document("name", 1), new IndexOptions().unique(true)); +``` + +Produces the shell equivalent (>= 2.6.0) of: + +```javascript +db.runCommand({ + createIndexes: "users", + indexes: [ + { key: { name: 1 }, name: "name_1", unique: true } + ] +}); +``` + +Create multiple indexes in a collection. + +Ruby: + +```ruby +collection.create_indexes([ + { key: { name: 1 }, unique: true }, + { key: { age: -1 }, name: "age" } +]) +``` + +Java: + +```java +collection.createIndexes(asList( + new IndexModel(new Document("name", 1), new IndexOptions().unique(true)), + new IndexModel(new Document("age", -1), new IndexOptions().name("age")) +)); +``` + +Produces the shell equivalent (>= 2.6.0) of: + +```javascript +db.runCommand({ + createIndexes: "users", + indexes: [ + { key: { name: 1 }, name: "name_1", unique: true }, + { key: { age: -1 }, name: "age" } + ] +}); +``` + +Drop an index in a collection. + +Ruby: + +```ruby +collection.drop_index("age") +``` + +Java: + +```java +collection.dropIndex("age"); +``` + +Produces the shell equivalent of: + +```javascript +db.runCommand({ dropIndexes: "users", index: "age" }); +``` + +Drop all indexes in a collection. + +Ruby: + +```ruby +collection.drop_indexes +``` + +Java: + +```java +collection.dropIndexes(); +``` + +Produces the shell equivalent of: + +```javascript +db.runCommand({ dropIndexes: "users", index: "*" }); +``` + +List all indexes in a collection. + +Ruby: + +```ruby +collection.list_indexes +``` + +Java: + +```java +collection.listIndexes(); +``` + +Produces the shell equivalent (>= 3.0.0) of: + +```javascript +db.runCommand({ listIndexes: "users" }); +``` + +### Index View API + +```typescript +interface Collection { + + /** + * Returns the index view for this collection. + */ + indexes(options: Optional): IndexView; +} + +interface IndexView extends Iterable { + + /** + * Enumerates the index information for all indexes in the collection. This should be + * implemented as described in the :ref:`Enumerate Indexes` section, although the naming + * requirement is dropped in favor of the driver language standard for handling iteration + * over a sequence of objects. + * + * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.md + * + * @note For drivers that cannot make the IndexView iterable, they MUST implement a list + * method. See below. + */ + iterator(): Iterator; + + /** + * For drivers that cannot make IndexView iterable, they MUST implement this method to + * return a list of indexes. In the case of async drivers, this MAY return a Future + * or language/implementation equivalent. + * + * If drivers are unable to make the IndexView iterable, they MAY opt to provide the options for + * listing search indexes via the `list` method instead of the `Collection.indexes` method. + + */ + list(): Cursor; + + /** + * This is a convenience method for creating a single index. This MUST call the + * createMany method and pass the provided specification document in a + * sequence to that method with the same options. + * + * @return The name of the created index. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an IndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the two options types into a single one. If the options are + * explicitly typed, the combined options type MUST be named CreateOneIndexOptions or an acceptable + * variation. + */ + createOne(keys: Document, indexOptions: IndexOptions, options: Optional): String; + + /** + * @see Comments above. + */ + createOne(model: IndexModel, options: Optional): String + + /** + * Creates multiple indexes in the collection. + * + * For all server versions this method MUST execute a createIndexes command. + * + * @return The names of the created indexes. + * + * @note Each specification document becomes the "key" field in the document that + * is inserted or the command. + * + */ + createMany(models: Iterable, options: Optional): Iterable; + + /** + * Drops a single index from the collection by the index name. + * + * In all server versions this MUST execute a dropIndexes command. + * + * @note If the string passed is '*', the driver MUST raise an error since + * more than one index would be dropped. + */ + dropOne(name: String, options: Optional): Result; + + /** + * Attempts to drop a single index from the collection given the keys and options. + * This is OPTIONAL until partial indexes are implemented. + * + * In all server versions this MUST execute a dropIndexes command. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an IndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the two options types into a single one. If the options are + * explicitly typed, the combined options type MUST be named DropOneIndexOptions or an acceptable + * variation. + */ + dropOne(keys: Document, indexOptions: IndexOptions, options: Optional): Result; + + /** + * @see Comments above. + */ + dropOne(model: IndexModel, options: Optional): Result; + + /** + * Drops all indexes in the collection. + */ + dropAll(options: Optional): Result; +} + +interface CreateOneIndexOptions { + // same as CreateIndexOptions in the Standard API +} + +interface CreateManyIndexesOptions { + // same as CreateIndexesOptions in the Standard API +} + +interface DropOneIndexOptions { + // same as DropIndexOptions in the Standard API +} + +interface DropAllIndexesOptions { + // same as DropIndexesOptions in the Standard API +} +``` + +#### Examples + +Create an index in a collection. + +Ruby: + +```ruby +collection.indexes.create_one({ name: 1 }, { unique: true }) +``` + +Java: + +```java +collection.indexes().createOne(new Document("name", 1), new IndexOptions().unique(true)); +``` + +Produces the shell equivalent (>= 2.6.0) of: + +```javascript +db.runCommand({ + createIndexes: "users", + indexes: [ + { key: { name: 1 }, name: "name_1", unique: true } + ] +}); +``` + +Create multiple indexes in a collection. + +Ruby: + +```ruby +collection.indexes.create_many([ + { key: { name: 1 }, unique: true }, + { key: { age: -1 }, name: "age" } +]) +``` + +Java: + +```java +collection.indexes().createMany(asList( + new IndexModel(new Document("name", 1), new IndexOptions().unique(true), + new IndexModel(new Document("age", -1), new IndexOptions().name("age") +)); +``` + +Produces the shell equivalent (>= 2.6.0) of: + +```javascript +db.runCommand({ + createIndexes: "users", + indexes: [ + { key: { name: 1 }, name: "name_1", unique: true }, + { key: { age: -1 }, name: "age" } + ] +}); +``` + +Drop an index in a collection. + +Ruby: + +```ruby +collection.indexes.drop_one("age") +``` + +Java: + +```java +collection.indexes().dropOne("age"); +``` + +Produces the shell equivalent of: + +```javascript +db.runCommand({ dropIndexes: "users", index: "age" }); +``` + +Drop all indexes in a collection. + +Ruby: + +```ruby +collection.indexes.drop_all +``` + +Java: + +```java +collection.indexes().dropAll(); +``` + +Produces the shell equivalent of: + +```javascript +db.runCommand({ dropIndexes: "users", index: "*" }); +``` + +List all indexes in a collection. + +Ruby: + +```ruby +collection.indexes.each do |document| + p document +end +``` + +Java: + +```java +for (BsonDocument document: collection.indexes()) { + /* ... */ +} +``` + +Produces the shell equivalent (>= 3.0.0) of: + +```javascript +var indexes = db.runCommand({ listIndexes: "users" }); +for (index in indexes) { + console.log(index); +} +``` + +### Common API Components + +```typescript +interface IndexModel { + + /** + * Contains the required keys for the index. + */ + keys: Document; + + /** + * Contains the options for the index. + */ + options: IndexOptions; +} + +interface IndexOptions { + + /** + * Optionally tells the server to build the index in the background and not block + * other tasks. + * + * @note Starting in MongoDB 4.2, this option is ignored by the server. + * @see https://www.mongodb.com/docs/manual/reference/command/createIndexes/ + * @deprecated 4.2 + */ + background: Boolean; + + /** + * Optionally specifies the length in time, in seconds, for documents to remain in + * a collection. + */ + expireAfterSeconds: Int32; + + /** + * Optionally specify a specific name for the index outside of the default generated + * name. If none is provided then the name is generated in the format "[field]_[direction]". + * + * Note that if an index is created for the same key pattern with different collations, + * a name must be provided by the user to avoid ambiguity. + * + * @example For an index of name: 1, age: -1, the generated name would be "name_1_age_-1". + */ + name: String; + + /** + * Optionally tells the index to only reference documents with the specified field in + * the index. + */ + sparse: Boolean; + + /** + * Optionally used only in MongoDB 3.0.0 and higher. Allows users to configure the storage + * engine on a per-index basis when creating an index. + */ + storageEngine: Document; + + /** + * Optionally forces the index to be unique. + */ + unique: Boolean; + + /** + * Optionally specifies the index version number, either 0 or 1. + */ + version: Int32; + + /** + * Optionally specifies the default language for text indexes. + * Is 'english' if none is provided. + */ + defaultLanguage: String; + + /** + * Optionally Specifies the field in the document to override the language. + */ + languageOverride: String; + + /** + * Optionally provides the text index version number. + * + * MongoDB 2.4 can only support version 1. + * + * MongoDB 2.6 and higher may support version 1 or 2. + */ + textIndexVersion: Int32; + + /** + * Optionally specifies fields in the index and their corresponding weight values. + */ + weights: Document; + + /** + * Optionally specifies the 2dsphere index version number. + * + * MongoDB 2.4 can only support version 1. + * + * MongoDB 2.6 and higher may support version 1 or 2. + */ + 2dsphereIndexVersion: Int32; + + /** + * Optionally specifies the precision of the stored geo hash in the 2d index, from 1 to 32. + */ + bits: Int32; + + /** + * Optionally sets the maximum boundary for latitude and longitude in the 2d index. + */ + max: Double; + + /** + * Optionally sets the minimum boundary for latitude and longitude in the index in a + * 2d index. + */ + min: Double; + + /** + * Optionally specifies the number of units within which to group the location values + * in a geo haystack index. + */ + bucketSize: Int32; + + /** + * Optionally specifies a filter for use in a partial index. Only documents that match the + * filter expression are included in the index. New in MongoDB 3.2. + */ + partialFilterExpression: Document; + + /** + * Optionally specifies a collation to use for the index in MongoDB 3.4 and higher. + * If not specified, no collation is sent and the default collation of the collection + * server-side is used. + */ + collation: Document; + + /** + * Optionally specifies the wildcard projection of a wildcard index. + */ + wildcardProjection: Document; + + /** + * Optionally specifies that the index should exist on the target collection but should not be used by the query + * planner when executing operations. + * + * This option is only supported by servers >= 4.4. + */ + hidden: Boolean; + + /** + * Optionally specifies that this index is clustered. This is not a valid option to provide to + * 'createIndexes', but can appear in the options returned for an index via 'listIndexes'. To + * create a clustered index, create a new collection using the 'clusteredIndex' option. + * + * This options is only supported by servers >= 6.0. + */ + clustered: Boolean; +} + +interface ListIndexesOptions { + /** + * Enables users to specify an arbitrary comment to help trace the operation through + * the database profiler, currentOp and logs. The default is to not send a value. + * + * If a comment is provided, drivers MUST attach this comment to all + * subsequent getMore commands run on the same cursor. + * + * @see https://www.mongodb.com/docs/manual/reference/command/listIndexes/ + * + * @since MongoDB 4.4 + */ + comment: Optional; + + /** + * Configures the batch size of the cursor returned from the ``listIndexes`` command. + * + * @note drivers MAY chose to support batchSize on the ListIndexesOptions. + */ + batchSize: Optional; + + /** + * This option MAY be implemented by drivers that need to grant access to underlying namespaces + * for time-series collections. Drivers SHOULD NOT implement this option unless asked to do so. + * + * This option is intended for internal use by MongoDB teams and should be discouraged for + * general application use. It may be changed or removed in any release without notice. + * + * Drivers SHOULD implement this option in a way that discourages customer use, such as: + * - Marking it as deprecated, experimental, or internal in their language's idioms + * - Excluding it from primary documentation + * + * @note This option MUST NOT be sent when connected to pre-8.2 servers. + * + * @since MongoDB 8.2 + */ + rawData: Optional; +} +``` + +### Enumerating Indexes + +For all server versions, drivers MUST run a `listIndexes` command when enumerating indexes. + +Drivers SHOULD use the method name `listIndexes` for a method that returns all indexes with a cursor return type. +Drivers MAY use an idiomatic variant that fits the language the driver is for. An exception is made for drivers +implementing the index view API. + +In MongoDB 4.4, the `ns` field was removed from the index specifications returned from the `listIndexes` command. + +- For drivers that report those index specifications in the form of documents or dictionaries, no special handling is + necessary, but any documentation of the contents of the documents/dictionaries MUST indicate that the `ns` field + will no longer be present in MongoDB 4.4+. If the contents of the documents/dictionaries are undocumented, then no + special mention of the `ns` field is necessary. +- For drivers that report those index specifications in the form of statically defined models, the driver MUST manually + populate the `ns` field of the models with the appropriate namespace if the server does not report it in the + `listIndexes` command response. The `ns` field is not required to be a part of the models, however. + + + +#### Getting Index Names + +Drivers MAY implement a method to enumerate all indexes, and return only the index names. The helper operates the same +as the following example: + +Example: + +```javascript +> a = []; +[ ] +> db.runCommand( { listIndexes: 'poiConcat' } ).indexes.forEach(function(i) { a.push(i.name); } ); +> a +[ "_id_", "ty_1", "l_2dsphere", "ts_1" ] +``` + +### Search Indexes + +Server 7.0 introduced three new server commands and a new aggregation stage to facilitate management of search indexes. +Drivers MUST provide an API similar to the existing index management API specifically for search indexes. Drivers MAY +choose to implement either the standard API or the index view API. + +#### Search Index Management Helper Options + +There are currently no supported options for any of the search index management commands. To future proof drivers +implementations so that any options added in the future do not constitute a breaking change to drivers, empty options +structs have been added as placeholders. If a driver's language has a mechanism to add options in a non-breaking manner +(i.e., method overloading) drivers MAY omit the empty options structs from their search index management helpers. + +`listSearchIndexes` is implemented using an aggregation pipeline. The list helper MUST support a driver's aggregation +options as outline in the [CRUD specification](../crud/crud.md#read). Drivers MAY combine the aggregation options with +any future `listSearchIndexes` stage options, if that is idiomatic for a driver's language. + +#### Asynchronicity + +The search index commands are asynchronous and return from the server before the index is successfully updated, created +or dropped. In order to determine when an index has been created / updated, users are expected to run the +`listSearchIndexes` repeatedly until index changes appear. + +An example, from Javascript: + +```typescript +const name = await collection.createSearchIndex({ definition: { ... fill out definition } }) +while (!(await collection.listSearchIndexes({ name }).hasNext())) { + await setTimeout(1000); +} +``` + +#### Where are read concern and write concern? + +These commands internally proxy the search index management commands to a separate process that runs alongside an Atlas +cluster. As such, read concern and write concern are not relevant for the search index management commands. + +Drivers MUST NOT apply a read concern or write concern to the commands. Atlas search index management commands return an +error if a `readConcern` or `writeConcern` field is present in the command. + +#### Consistency with Existing APIs + +Drivers SHOULD strive for a search index management API that is as consistent as possible with their existing index +management API. + +#### NamespaceNotFound Errors + +Some drivers suppress NamespaceNotFound errors for CRUD helpers. Drivers MAY suppress NamespaceNotFound errors from the +search index management helpers. + +Drivers MUST suppress NamespaceNotFound errors for the `dropSearchIndex` helper. Drop operations should be idempotent: + +```typescript +await collection.dropSearchIndex('my-test-index'); +// subsequent calls should behave the same for the user as the first call +await collection.dropSearchIndex('my-test-index'); +await collection.dropSearchIndex('my-test-index'); +``` + +#### Common Interfaces + +```typescript +interface SearchIndexModel { + /** + * Document describing the index to create. + * + * The definition syntax depends on whether you create a standard search index + * or a vector search index. + * + * @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/ + */ + definition: Document; + + /** + * Contains the options for the index. + */ + options: SearchIndexOptions; +} + +interface SearchIndexOptions { + /** + * Name of the search index to create. + * + * The server will use "default" if this option is not specified. + */ + name: Optional; + + /** + * Type of search index to create. Defaults to "search" if not provided. + * + * Specify "search" for a standard search index or "vectorSearch" for a vector + * search index. + * + * The server will use "search" if this option is not specified. + */ + type: Optional; +} + +/** + * The following interfaces are empty but are provided as placeholders for drivers that cannot + * add options in a non-breaking manner, if options are added in the future. + */ +interface CreateSearchIndexOptions {} +interface UpdateSearchIndexOptions {} +interface ListSearchIndexOptions {} +interface DropSearchIndexOptions {} +``` + +#### Standard API for Search Indexes + +```typescript +interface Collection { + /** + * Convenience method for creating a single search index. + * + * @return The name of the created search index + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an SearchIndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the `indexOptions` with the `createSearchIndexOptions`, if that is idiomatic for their language. + */ + createSearchIndex(definition: Document, indexOptions: Optional, createSearchIndexOptions: Optional): String; + + /** + * Convenience method for creating a single index. + * + * @return The name of the created search index + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an name and a definition as parameters, or for those languages with method + * overloading MAY decide to implement both. + */ + createSearchIndex(model: SearchIndexModel, options: Optional): String; + + /** + * Creates multiple search indexes on the collection. + * + * @return An iterable of the newly created index names. + */ + createSearchIndexes(models: Iterable, options: CreateSearchIndexOptions): Iterable; + + /** + * Updates the search index with the given name to use the provided + * definition. + */ + updateSearchIndex(name: String, definition: Document, options: Optional): void; + + /** + * Drops the search index with the given name. + */ + dropSearchIndex(name: String, options: Optional): void; + + /** + * Gets index information for one or more search indexes in the collection. + * + * If name is not specified, information for all indexes on the specified collection will be returned. + */ + listSearchIndexes(name: Optional, aggregationOptions: Optional, listIndexOptions: Optional): Cursor; +} +``` + +#### Index View API for Search Indexes + +```typescript +interface Collection { + /** + * Returns the search index view for this collection. + */ + searchIndexes(name: Optional, aggregateOptions: Optional, options: Optional): SearchIndexView; +} + +interface SearchIndexView extends Iterable { + /** + * Enumerates the index information for all search indexes in the collection. + * + * @note For drivers that cannot make the IndexView iterable, they MUST implement a list + * method. See below. + */ + iterator(): Iterator; + + /** + * For drivers that cannot make SearchIndexView iterable, they MUST implement this method to + * return a list of indexes. In the case of async drivers, this MAY return a Future + * or language/implementation equivalent. + * + * If drivers are unable to make the SearchIndexView iterable, they MAY opt to provide the options for + * listing search indexes via the `list` method instead of the `Collection.searchIndexes` method. + */ + list(): Cursor; + + + /** + * This is a convenience method for creating a single index. + * + * @return The name of the created index. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an SearchIndexModel as a parameter, or for those languages with method + * overloading MAY decide to implement both. + * + * @note Drivers MAY combine the `indexOptions` with the `createSearchIndexOptions`, if that is idiomatic for their language. + */ + createOne(definition: Document, indexOptions: Optional, createSearchIndexOptions: Optional): String; + + /** + * This is a convenience method for creating a single index. + * + * @return The name of the created index. + * + * @note Drivers MAY opt to implement this method signature, the signature that + * takes an name and a definition as parameters, or for those languages with method + * overloading MAY decide to implement both. + */ + createOne(model: SearchIndexModel, options: Optional): String; + + /** + * Creates multiple search indexes in the collection. + * + * @return The names of the created indexes. + */ + createMany(models: Iterable, options: Optional): Iterable; + + /** + * Drops a single search index from the collection by the index name. + */ + dropOne(name: String, options: Optional): Result; + + /** + * Updates a single search index from the collection by the index name. + */ + updateOne(name: String, definition: Document, options: Optional): Result; +} +``` + +### Q & A + +Q: Where is write concern? + +The `createIndexes` and `dropIndexes` commands take a write concern that indicates how the write is acknowledged. Since +all operations defined in this specification are performed on a collection, it's uncommon that two different index +operations on the same collection would use a different write concern. As such, the most natural place to indicate write +concern is on the client, the database, or the collection itself and not the operations within it. + +However, it might be that a driver needs to expose write concern to a user per operation for various reasons. It is +permitted to allow a write concern option, but since writeConcern is a top-level command option, it MUST NOT be +specified as part of an `IndexModel` passed into the helper. It SHOULD be specified via the options parameter of the +helper. For example, it would be ambiguous to specify write concern for one or more models passed to `createIndexes()`, +but it would not be to specify it via the `CreateIndexesOptions`. + +Q: What does the commitQuorum option do? + +Prior to MongoDB 4.4, secondaries would simply replicate index builds once they were completed on the primary. Building +indexes requires an exclusive lock on the collection being indexed, so the secondaries would be blocked from replicating +all other operations while the index build took place. This would introduce replication lag correlated to however long +the index build took. + +Starting in MongoDB 4.4, secondaries build indexes simultaneously with the primary, and after starting an index build, +the primary will wait for a certain number of data-bearing nodes, including itself, to have completed the build before +it commits the index. `commitQuorum` configures this node requirement. Once the index is committed, all the secondaries +replicate the commit too. If a secondary had already completed the index build, the commit will be quick, and no new +replication lag would be introduced. If a secondary had not finished building the index before the primary committed it +(e.g. if `commitQuorum: 0` was used), then that secondary may lag behind the primary while it finishes building and +committing the index. + +The server-default value for `commitQuorum` is "votingMembers", which means the primary will wait for all voting +data-bearing nodes to complete building the index before it commits it. + +Q: Why would a user want to specify a non-default `commitQuorum` + +Like `w: "majority"`, `commitQuorum: "votingMembers"` doesn't consider non-voting data-bearing nodes such as analytics +nodes. If a user wanted to ensure these nodes didn't lag behind, then they would specify +`commitQuorum: `. Alternatively, if they wanted to +ensure only specific non-voting nodes didn't lag behind, they could specify a +[custom getLastErrorMode based on the nodes' tag sets](https://www.mongodb.com/docs/manual/reference/replica-configuration/#rsconf.settings.getLastErrorModes) +(e.g. `commitQuorum: `). + +Additionally, if a user has a high tolerance for replication lag, they can set a lower value for `commitQuorum`. This is +useful for situations where certain secondaries take longer to build indexes than the primaries, and the user doesn't +care if they lag behind. + +Q: What is the difference between write concern and `commitQuorum`? + +While these two options share a lot in terms of how they are specified, they configure entirely different things. +`commitQuorum` determines how much new replication lag an index build can tolerably introduce, but it says nothing of +durability. Write concern specifies the durability requirements of an index build, but it makes no guarantees about +introducing replication lag. + +For instance, an index built with `writeConcern: { w: 1 }, commitQuorum: "votingMembers"` could possibly be rolled back, +but it will not introduce any new replication lag. Likewise, an index built with +`writeConcern: { w: "majority", j: true }, commitQuorum: 0` will not be rolled back, but it may cause the secondaries to +lag. To ensure the index is both durable and will not introduce replication lag on any data-bearing voting secondary, +`writeConcern: { w: "majority", j: true }, commitQuorum: "votingMembers"` must be used. + +Also note that, since indexes are built simultaneously, higher values of `commitQuorum` are not as expensive as higher +values of `writeConcern`. + +Q: Why does the driver manually throw errors if the `commitQuorum` option is specified against a pre 4.4 server?
+Starting in 3.4, the server validates all options passed to the `createIndexes` command, but due to a bug in versions +4.2.0-4.2.5 of the server (SERVER-47193), specifying `commitQuorum` does not result in an error. The option is used +internally by the server on those versions, and its value could have adverse effects on index builds. To prevent users +from mistakenly specifying this option, drivers manually verify it is only sent to 4.4+ servers. + +#### Changelog + +- 2025-12-18: Search index tests only assert `latestDefinition.mappings.dynamic` values. + +- 2025-09-09: Clarify that `rawData` is for internal use only. + +- 2025-06-27: Added `rawData` option to CreateIndexOptions, DropIndexOptions and ListIndexesOptions. + +- 2024-09-05: Moved options in SearchIndexModel to SearchIndexOptions for consistency with IndexModel and IndexOptions. + +- 2024-03-06: Added `type` option to SearchIndexOptions. + +- 2024-03-05: Migrated from reStructuredText to Markdown. + +- 2023-11-08: Clarify that `readConcern` and `writeConcern` must not be applied to search index management commands. + +- 2023-07-27: Add search index management clarifications. + +- 2023-05-18: Add the search index management API. + +- 2023-05-10: Merge index enumeration and index management specs and get rid of references to legacy server versions. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-04-18: Added the `clustered` attribute to `IndexOptions` in order to support clustered collections. + +- 2022-02-10: Specified that `getMore` command must explicitly send inherited comment. + +- 2022-02-01: Added comment field to helper methods. + +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec. + +- 2020-03-30: Added options types to various helpers. Introduced `commitQuorum` option. Added deprecation message for + `background` option. + +- 2019-04-24: Added `wildcardProjection` attribute to `IndexOptions` in order to support setting a wildcard projection + on a wildcard index. + +- 2017-06-07: Include listIndexes() in Q&A about maxTimeMS. + +- 2017-05-31: Add Q & A addressing write concern and maxTimeMS option. + +- 2016-10-11: Added note on 3.4 servers validation options passed to `createIndexes`. Add note on server generated name + for the `_id` index. + +- 2016-08-08: Fixed `collation` language to not mention a collection default. + +- 2016-05-19: Added `collation` attribute to `IndexOptions` in order to support setting a collation on an index. + +- 2015-09-17: Added `partialFilterExpression` attribute to `IndexOptions` in order to support partial indexes. Fixed + "provides" typo. diff --git a/source/index-management/index-management.rst b/source/index-management/index-management.rst new file mode 100644 index 0000000000..accbd5c431 --- /dev/null +++ b/source/index-management/index-management.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `index-management.md `_. diff --git a/source/index-management/tests/README.md b/source/index-management/tests/README.md new file mode 100644 index 0000000000..30404b470d --- /dev/null +++ b/source/index-management/tests/README.md @@ -0,0 +1,338 @@ +# Index Management Tests + +______________________________________________________________________ + +## Test Plan + +These prose tests are ported from the legacy enumerate-indexes spec. + +### Configurations + +- standalone node +- replica set primary node +- replica set secondary node +- mongos node + +### Preparation + +For each of the configurations: + +- Create a (new) database +- Create a collection +- Create a single column index, a compound index, and a unique index +- Insert at least one document containing all the fields that the above indicated indexes act on + +### Tests + +- Run the driver's method that returns a list of index names, and: + - verify that *all* index names are represented in the result + - verify that there are no duplicate index names + - verify there are no returned indexes that do not exist +- Run the driver's method that returns a list of index information records, and: + - verify all the indexes are represented in the result + - verify the "unique" flags show up for the unique index + - verify there are no duplicates in the returned list + - if the result consists of statically defined index models that include an `ns` field, verify that its value is + accurate + +### Search Index Management Helpers + +These tests are intended to smoke test the search management helpers end-to-end against a live Atlas cluster. + +The search index management commands are asynchronous and mongod/mongos returns before the changes to a clusters' search +indexes have completed. When these prose tests specify "waiting for the changes", drivers should repeatedly poll the +cluster with `listSearchIndexes` until the changes are visible. Each test specifies the condition that is considered +"ready". For example, when creating a new search index, waiting until the inserted index has a status `queryable: true` +indicates that the index was successfully created. + +The commands tested in these prose tests take a while to successfully complete. Drivers should raise the timeout for +each test to avoid timeout errors if the test timeout is too low. 5 minutes is a sufficiently large timeout that any +timeout that occurs indicates a real failure, but this value is not required and can be tweaked per-driver. + +There is a server-side limitation that prevents multiple search indexes from being created with the same name, +definition and collection name. This limitation does not take into account collection uuid. Because these commands are +asynchronous, any cleanup code that may run after a test (cleaning a database or dropping search indexes) may not have +completed by the next iteration of the test (or the next test run, if running locally). To address this issue, each test +uses a randomly generated collection name. Drivers may generate this collection name however they like, but a suggested +implementation is a hex representation of an ObjectId (`new ObjectId().toHexString()` in Node). + +#### Setup + +These tests must run against an Atlas cluster with a 7.0+ server. +[Scripts are available](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/atlas) in +drivers-evergreen-tools which can setup and teardown Atlas clusters. To ensure that the Atlas cluster is cleaned up +after each CI run, drivers should configure evergreen to run these tests as a part of a task group. Be sure that the +cluster gets torn down! + +When working locally on these tests, the same Atlas setup and teardown scripts can be used locally to provision a +cluster for development. + +#### Case 1: Driver can successfully create and list search indexes + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + ``` + +3. Assert that the command returns the name of the index: `"test-search-index"`. + +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied and store the + value in a variable `index`: + + - An index with the `name` of `test-search-index` is present and the index has a field `queryable` with a value of + `true`. + +5. Assert that `index.latestDefinition.mappings.dynamic` has the value `false`. + +#### Case 2: Driver can successfully create multiple indexes in batch + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create two new search indexes on `coll0` with the `createSearchIndexes` helper. Use the following definitions when + creating the indexes. These definitions are referred to as `indexDefinitions`. + + ```typescript + { + name: 'test-search-index-1', + definition: { + mappings: { dynamic: false } + } + } + + { + name: 'test-search-index-2', + definition: { + mappings: { dynamic: false } + } + } + ``` + +3. Assert that the command returns an array containing the new indexes' names: + `["test-search-index-1", "test-search-index-2"]`. + +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following conditions are satisfied. + + - An index with the `name` of `test-search-index-1` is present and index has a field `queryable` with the value of + `true`. Store result in `index1`. + - An index with the `name` of `test-search-index-2` is present and index has a field `queryable` with the value of + `true`. Store result in `index2`. + +5. Assert that `index1.latestDefinition.mappings.dynamic` and `index2.latestDefinition.mappings.dynamic` have the value + `false`. + +#### Case 3: Driver can successfully drop search indexes + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create a new search index on `coll0` with the following definition: + + ```typescript + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + ``` + +3. Assert that the command returns the name of the index: `"test-search-index"`. + +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied: + + - An index with the `name` of `test-search-index` is present and index has a field `queryable` with the value of + `true`. + +5. Run a `dropSearchIndex` on `coll0`, using `test-search-index` for the name. + +6. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until `listSearchIndexes` returns an empty array. + +This test fails if it times out waiting for the deletion to succeed. + +#### Case 4: Driver can update a search index + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create a new search index on `coll0` with the following definition: + + ```typescript + { + name: 'test-search-index', + definition: { + mappings: { dynamic: false } + } + } + ``` + +3. Assert that the command returns the name of the index: `"test-search-index"`. + +4. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied: + + - An index with the `name` of `test-search-index` is present and index has a field `queryable` with the value of + `true`. + +5. Run a `updateSearchIndex` on `coll0`, using the following definition. + + ```typescript + { + name: 'test-search-index', + definition: { + mappings: { dynamic: true } + } + } + ``` + +6. Assert that the command does not error and the server responds with a success. + +7. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following conditions are satisfied: + + - An index with the `name` of `test-search-index` is present. This index is referred to as `index`. + - The index has a field `queryable` with a value of `true` and has a field `status` with the value of `READY`. + +8. Assert that an index is present with the name `test-search-index` and `index.latestDefinition.mappings.dynamic` has + the value `true`. + +#### Case 5: `dropSearchIndex` suppresses namespace not found errors + +1. Create a driver-side collection object for a randomly generated collection name. Do not create this collection on the + server. +2. Run a `dropSearchIndex` command and assert that no error is thrown. + +#### Case 6: Driver can successfully create and list search indexes with non-default readConcern and writeConcern + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Apply a write concern `WriteConcern(w=1)` and a read concern with `ReadConcern(level="majority")` to `coll0`. + +3. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + { + name: 'test-search-index-case6', + definition: { + mappings: { dynamic: false } + } + } + ``` + +4. Assert that the command returns the name of the index: `"test-search-index-case6"`. + +5. Run `coll0.listSearchIndexes()` repeatedly every 5 seconds until the following condition is satisfied and store the + value in a variable `index`: + + - An index with the `name` of `test-search-index-case6` is present and the index has a field `queryable` with a value + of `true`. + +6. Assert that `index.latestDefinition.mappings.dynamic` has the value `false`. + +#### Case 7: Driver can successfully handle search index types when creating indexes + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + + { + name: 'test-search-index-case7-implicit', + definition: { + mappings: { dynamic: false } + } + } + ``` + +3. Assert that the command returns the name of the index: `"test-search-index-case7-implicit"`. + +4. Run `coll0.listSearchIndexes('test-search-index-case7-implicit')` repeatedly every 5 seconds until the following + condition is satisfied and store the value in a variable `index1`: + + - An index with the `name` of `test-search-index-case7-implicit` is present and the index has a field `queryable` + with a value of `true`. + +5. Assert that `index1` has a property `type` whose value is `search`. + +6. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + + { + name: 'test-search-index-case7-explicit', + type: 'search', + definition: { + mappings: { dynamic: false } + } + } + ``` + +7. Assert that the command returns the name of the index: `"test-search-index-case7-explicit"`. + +8. Run `coll0.listSearchIndexes('test-search-index-case7-explicit')` repeatedly every 5 seconds until the following + condition is satisfied and store the value in a variable `index2`: + + - An index with the `name` of `test-search-index-case7-explicit` is present and the index has a field `queryable` + with a value of `true`. + +9. Assert that `index2` has a property `type` whose value is `search`. + +10. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + + { + name: 'test-search-index-case7-vector', + type: 'vectorSearch', + definition: { + fields: [ + { + type: 'vector', + path: 'plot_embedding', + numDimensions: 1536, + similarity: 'euclidean', + }, + ] + } + } + ``` + +11. Assert that the command returns the name of the index: `"test-search-index-case7-vector"`. + +12. Run `coll0.listSearchIndexes('test-search-index-case7-vector')` repeatedly every 5 seconds until the following + condition is satisfied and store the value in a variable `index3`: + + - An index with the `name` of `test-search-index-case7-vector` is present and the index has a field `queryable` with + a value of `true`. + +13. Assert that `index3` has a property `type` whose value is `vectorSearch`. + +#### Case 8: Driver requires explicit type to create a vector search index + +1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`). + +2. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition: + + ```typescript + + { + name: 'test-search-index-case8-error', + definition: { + fields: [ + { + type: 'vector', + path: 'plot_embedding', + numDimensions: 1536, + similarity: 'euclidean', + }, + ] + } + } + ``` + +3. Assert that the command throws an exception containing the string "Attribute mappings missing" due to the `mappings` + field missing. diff --git a/source/index-management/tests/createSearchIndex.json b/source/index-management/tests/createSearchIndex.json new file mode 100644 index 0000000000..f4f2a6c661 --- /dev/null +++ b/source/index-management/tests/createSearchIndex.json @@ -0,0 +1,210 @@ +{ + "description": "createSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.5", + "maxServerVersion": "7.0.99", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "7.2.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "no name provided for an index definition", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "name provided for an index definition", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index", + "type": "search" + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index", + "type": "search" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "create a vector search index", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "fields": [ + { + "type": "vector", + "path": "plot_embedding", + "numDimensions": 1536, + "similarity": "euclidean" + } + ] + }, + "name": "test index", + "type": "vectorSearch" + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "fields": [ + { + "type": "vector", + "path": "plot_embedding", + "numDimensions": 1536, + "similarity": "euclidean" + } + ] + }, + "name": "test index", + "type": "vectorSearch" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/createSearchIndex.yml b/source/index-management/tests/createSearchIndex.yml new file mode 100644 index 0000000000..8d05ee5042 --- /dev/null +++ b/source/index-management/tests/createSearchIndex.yml @@ -0,0 +1,92 @@ +description: "createSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + # Skip server versions without fix of SERVER-83107 to avoid error message "BSON field 'createSearchIndexes.indexes.type' is an unknown field." + # SERVER-83107 was not backported to 7.1. + - minServerVersion: "7.0.5" + maxServerVersion: "7.0.99" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + - minServerVersion: "7.2.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "no name provided for an index definition" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { mappings: { dynamic: true } } , type: 'search' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, type: 'search'} ] + $db: *database0 + + - description: "name provided for an index definition" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index', type: 'search' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index', type: 'search' } ] + $db: *database0 + + - description: "create a vector search index" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { fields: [ {"type": "vector", "path": "plot_embedding", "numDimensions": 1536, "similarity": "euclidean"} ] } + , name: 'test index', type: 'vectorSearch' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index', type: 'vectorSearch' } ] + $db: *database0 diff --git a/source/index-management/tests/createSearchIndexes.json b/source/index-management/tests/createSearchIndexes.json new file mode 100644 index 0000000000..01300b1b7f --- /dev/null +++ b/source/index-management/tests/createSearchIndexes.json @@ -0,0 +1,248 @@ +{ + "description": "createSearchIndexes", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.5", + "maxServerVersion": "7.0.99", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "7.2.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "empty index definition array", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [] + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "no name provided for an index definition", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "name provided for an index definition", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index", + "type": "search" + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + }, + "name": "test index", + "type": "search" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "create a vector search index", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [ + { + "definition": { + "fields": [ + { + "type": "vector", + "path": "plot_embedding", + "numDimensions": 1536, + "similarity": "euclidean" + } + ] + }, + "name": "test index", + "type": "vectorSearch" + } + ] + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "fields": [ + { + "type": "vector", + "path": "plot_embedding", + "numDimensions": 1536, + "similarity": "euclidean" + } + ] + }, + "name": "test index", + "type": "vectorSearch" + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/createSearchIndexes.yml b/source/index-management/tests/createSearchIndexes.yml new file mode 100644 index 0000000000..56ee5ff208 --- /dev/null +++ b/source/index-management/tests/createSearchIndexes.yml @@ -0,0 +1,114 @@ +description: "createSearchIndexes" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + # Skip server versions without fix of SERVER-83107 to avoid error message "BSON field 'createSearchIndexes.indexes.type' is an unknown field." + # SERVER-83107 was not backported to 7.1. + - minServerVersion: "7.0.5" + maxServerVersion: "7.0.99" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + - minServerVersion: "7.2.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "empty index definition array" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [] + $db: *database0 + + + - description: "no name provided for an index definition" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [ { definition: &definition { mappings: { dynamic: true } } , type: 'search' } ] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, type: 'search'} ] + $db: *database0 + + - description: "name provided for an index definition" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [ { definition: &definition { mappings: { dynamic: true } } , name: 'test index' , type: 'search' } ] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index', type: 'search' } ] + $db: *database0 + + - description: "create a vector search index" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [ { definition: &definition { fields: [ {"type": "vector", "path": "plot_embedding", "numDimensions": 1536, "similarity": "euclidean"} ] }, + name: 'test index' , type: 'vectorSearch' } ] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition, name: 'test index', type: 'vectorSearch' } ] + $db: *database0 diff --git a/source/index-management/tests/dropSearchIndex.json b/source/index-management/tests/dropSearchIndex.json new file mode 100644 index 0000000000..d8957a2227 --- /dev/null +++ b/source/index-management/tests/dropSearchIndex.json @@ -0,0 +1,74 @@ +{ + "description": "dropSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "sends the correct command", + "operations": [ + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "dropSearchIndex": "collection0", + "name": "test index", + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/dropSearchIndex.yml b/source/index-management/tests/dropSearchIndex.yml new file mode 100644 index 0000000000..8a8e829454 --- /dev/null +++ b/source/index-management/tests/dropSearchIndex.yml @@ -0,0 +1,43 @@ +description: "dropSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "sends the correct command" + operations: + - name: dropSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + dropSearchIndex: *collection0 + name: *indexName + $db: *database0 diff --git a/source/index-management/tests/index-rawdata.json b/source/index-management/tests/index-rawdata.json new file mode 100644 index 0000000000..791a643a0f --- /dev/null +++ b/source/index-management/tests/index-rawdata.json @@ -0,0 +1,186 @@ +{ + "description": "index management-rawData", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "tests": [ + { + "description": "index management with rawData option", + "runOnRequirements": [ + { + "minServerVersion": "8.2.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "keys": { + "x": 1 + }, + "rawData": true + } + }, + { + "object": "collection0", + "name": "listIndexes", + "arguments": { + "rawData": true + } + }, + { + "object": "collection0", + "name": "dropIndex", + "arguments": { + "name": "x_1", + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createIndexes": "collection0", + "indexes": [ + { + "name": "x_1", + "key": { + "x": 1 + } + } + ], + "rawData": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "collection0", + "rawData": true + } + } + }, + { + "commandStartedEvent": { + "command": { + "dropIndexes": "collection0", + "index": "x_1", + "rawData": true + } + } + } + ] + } + ] + }, + { + "description": "index management with rawData option on less than 8.2.0 - ignore argument", + "runOnRequirements": [ + { + "maxServerVersion": "8.1.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "keys": { + "x": 1 + }, + "rawData": true + } + }, + { + "object": "collection0", + "name": "listIndexes", + "arguments": { + "rawData": true + } + }, + { + "object": "collection0", + "name": "dropIndex", + "arguments": { + "name": "x_1", + "rawData": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createIndexes": "collection0", + "indexes": [ + { + "name": "x_1", + "key": { + "x": 1 + } + } + ], + "rawData": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "collection0", + "rawData": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "dropIndexes": "collection0", + "index": "x_1", + "rawData": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/index-rawdata.yml b/source/index-management/tests/index-rawdata.yml new file mode 100644 index 0000000000..b0b692b664 --- /dev/null +++ b/source/index-management/tests/index-rawdata.yml @@ -0,0 +1,95 @@ +description: "index management-rawData" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name collection0 + +tests: + - description: "index management with rawData option" + runOnRequirements: + - minServerVersion: "8.2.0" + operations: + - object: *collection0 + name: createIndex + arguments: + keys: &key { x: 1 } + rawData: true + - object: *collection0 + name: listIndexes + arguments: + rawData: true + - object: *collection0 + name: dropIndex + arguments: + name: &name x_1 + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createIndexes: *collection0 + indexes: + - name: x_1 + key: *key + rawData: true + - commandStartedEvent: + command: + listIndexes: *collection0 + rawData: true + - commandStartedEvent: + command: + dropIndexes: *collection0 + index: *name + rawData: true + - description: "index management with rawData option on less than 8.2.0 - ignore argument" + runOnRequirements: + - maxServerVersion: "8.1.99" + operations: + - object: *collection0 + name: createIndex + arguments: + keys: &key { x: 1 } + rawData: true + - object: *collection0 + name: listIndexes + arguments: + rawData: true + - object: *collection0 + name: dropIndex + arguments: + name: &name x_1 + rawData: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createIndexes: *collection0 + indexes: + - name: x_1 + key: *key + rawData: + $$exists: false + - commandStartedEvent: + command: + listIndexes: *collection0 + rawData: + $$exists: false + - commandStartedEvent: + command: + dropIndexes: *collection0 + index: *name + rawData: + $$exists: false diff --git a/source/index-management/tests/listSearchIndexes.json b/source/index-management/tests/listSearchIndexes.json new file mode 100644 index 0000000000..a8cef42f7a --- /dev/null +++ b/source/index-management/tests/listSearchIndexes.json @@ -0,0 +1,156 @@ +{ + "description": "listSearchIndexes", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "when no name is provided, it does not populate the filter", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": {} + } + ] + } + } + } + ] + } + ] + }, + { + "description": "when a name is provided, it is present in the filter", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": { + "name": "test index" + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + }, + { + "description": "aggregation cursor options are supported", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "arguments": { + "name": "test index", + "aggregationOptions": { + "batchSize": 10 + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "cursor": { + "batchSize": 10 + }, + "pipeline": [ + { + "$listSearchIndexes": { + "name": "test index" + } + } + ], + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/listSearchIndexes.yml b/source/index-management/tests/listSearchIndexes.yml new file mode 100644 index 0000000000..f05a368585 --- /dev/null +++ b/source/index-management/tests/listSearchIndexes.yml @@ -0,0 +1,88 @@ +description: "listSearchIndexes" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "when no name is provided, it does not populate the filter" + operations: + - name: listSearchIndexes + object: *collection0 + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $listSearchIndexes: {} + + - description: "when a name is provided, it is present in the filter" + operations: + - name: listSearchIndexes + object: *collection0 + arguments: + name: &indexName "test index" + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $listSearchIndexes: { name: *indexName } + $db: *database0 + + - description: aggregation cursor options are supported + operations: + - name: listSearchIndexes + object: *collection0 + arguments: + name: &indexName "test index" + aggregationOptions: + batchSize: 10 + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + cursor: { batchSize: 10 } + pipeline: + - $listSearchIndexes: { name: *indexName } + $db: *database0 diff --git a/source/index-management/tests/searchIndexIgnoresReadWriteConcern.json b/source/index-management/tests/searchIndexIgnoresReadWriteConcern.json new file mode 100644 index 0000000000..edf71b7b7e --- /dev/null +++ b/source/index-management/tests/searchIndexIgnoresReadWriteConcern.json @@ -0,0 +1,252 @@ +{ + "description": "search index operations ignore read and write concern", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "createSearchIndex ignores read and write concern", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + } + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [ + { + "definition": { + "mappings": { + "dynamic": true + } + } + } + ], + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "createSearchIndexes ignores read and write concern", + "operations": [ + { + "name": "createSearchIndexes", + "object": "collection0", + "arguments": { + "models": [] + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createSearchIndexes": "collection0", + "indexes": [], + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "dropSearchIndex ignores read and write concern", + "operations": [ + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "dropSearchIndex": "collection0", + "name": "test index", + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "listSearchIndexes ignores read and write concern", + "operations": [ + { + "name": "listSearchIndexes", + "object": "collection0", + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$listSearchIndexes": {} + } + ], + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "updateSearchIndex ignores the read and write concern", + "operations": [ + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "updateSearchIndex": "collection0", + "name": "test index", + "definition": {}, + "$db": "database0", + "writeConcern": { + "$$exists": false + }, + "readConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/searchIndexIgnoresReadWriteConcern.yml b/source/index-management/tests/searchIndexIgnoresReadWriteConcern.yml new file mode 100644 index 0000000000..d1a3e93759 --- /dev/null +++ b/source/index-management/tests/searchIndexIgnoresReadWriteConcern.yml @@ -0,0 +1,147 @@ +description: "search index operations ignore read and write concern" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + # Set a non-default read and write concern. + uriOptions: + readConcernLevel: local + w: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "createSearchIndex ignores read and write concern" + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: &definition { mappings: { dynamic: true } } } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [ { definition: *definition } ] + $db: *database0 + # Expect no writeConcern or readConcern to be sent. + writeConcern: { $$exists: false } + readConcern: { $$exists: false } + + - description: "createSearchIndexes ignores read and write concern" + operations: + - name: createSearchIndexes + object: *collection0 + arguments: + models: [] + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + createSearchIndexes: *collection0 + indexes: [] + $db: *database0 + # Expect no writeConcern or readConcern to be sent. + writeConcern: { $$exists: false } + readConcern: { $$exists: false } + + - description: "dropSearchIndex ignores read and write concern" + operations: + - name: dropSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + dropSearchIndex: *collection0 + name: *indexName + $db: *database0 + # Expect no writeConcern or readConcern to be sent. + writeConcern: { $$exists: false } + readConcern: { $$exists: false } + + - description: "listSearchIndexes ignores read and write concern" + operations: + - name: listSearchIndexes + object: *collection0 + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $listSearchIndexes: {} + # Expect no writeConcern or readConcern to be sent. + writeConcern: { $$exists: false } + readConcern: { $$exists: false } + + - description: "updateSearchIndex ignores the read and write concern" + operations: + - name: updateSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + definition: &definition {} + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + updateSearchIndex: *collection0 + name: *indexName + definition: *definition + $db: *database0 + # Expect no writeConcern or readConcern to be sent. + writeConcern: { $$exists: false } + readConcern: { $$exists: false } + diff --git a/source/index-management/tests/updateSearchIndex.json b/source/index-management/tests/updateSearchIndex.json new file mode 100644 index 0000000000..76a5962146 --- /dev/null +++ b/source/index-management/tests/updateSearchIndex.json @@ -0,0 +1,76 @@ +{ + "description": "updateSearchIndex", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "sends the correct command", + "operations": [ + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "updateSearchIndex": "collection0", + "name": "test index", + "definition": {}, + "$db": "database0" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/index-management/tests/updateSearchIndex.yml b/source/index-management/tests/updateSearchIndex.yml new file mode 100644 index 0000000000..2c56e75ef6 --- /dev/null +++ b/source/index-management/tests/updateSearchIndex.yml @@ -0,0 +1,46 @@ +description: "updateSearchIndex" +schemaVersion: "1.4" +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: *database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: *collection0 + +runOnRequirements: + - minServerVersion: "7.0.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + +tests: + - description: "sends the correct command" + operations: + - name: updateSearchIndex + object: *collection0 + arguments: + name: &indexName 'test index' + definition: &definition {} + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + updateSearchIndex: *collection0 + name: *indexName + definition: *definition + $db: *database0 + diff --git a/source/index.md b/source/index.md new file mode 100644 index 0000000000..31c797b580 --- /dev/null +++ b/source/index.md @@ -0,0 +1,65 @@ +# MongoDB Specifications + +- [Atlas Secure Frontend Processor (SFP) Testing](atlas-sfp-testing/atlas-sfp-testing.md) +- [Atlas Serverless Tests](serverless-testing/README.md) +- [Authentication](auth/auth.md) +- [BSON Binary Encrypted](bson-binary-encrypted/binary-encrypted.md) +- [BSON Binary Subtype 9 - Vector](bson-binary-vector/bson-binary-vector.md) +- [BSON Binary UUID](bson-binary-uuid/uuid.md) +- [BSON Corpus](bson-corpus/bson-corpus.md) +- [BSON Decimal128](bson-decimal128/decimal128.md) +- [BSON ObjectID](bson-objectid/objectid.md) +- [Bulk Write](crud/bulk-write.md) +- [CRUD API](crud/crud.md) +- [Causal Consistency Specification](causal-consistency/causal-consistency.md) +- [Change Streams](change-streams/change-streams.md) +- [Client Backpressure](client-backpressure/client-backpressure.md) +- [Client Side Encryption](client-side-encryption/client-side-encryption.md) +- [Client Side Operations Timeout](client-side-operations-timeout/client-side-operations-timeout.md) +- [Collation](collation/collation.md) +- [Command Logging and Monitoring](command-logging-and-monitoring/command-logging-and-monitoring.md) +- [Connection Monitoring and Pooling](connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) +- [Connection String Spec](connection-string/connection-string-spec.md) +- [Convenient API for Transactions](transactions-convenient-api/transactions-convenient-api.md) +- [DBRef](dbref/dbref.md) +- [Driver Mantras](./driver-mantras.md) +- [Enumerating Collections](enumerate-collections/enumerate-collections.md) +- [Enumerating Databases](enumerate-databases/enumerate-databases.md) +- [Extended JSON](extended-json/extended-json.md) +- [FaaS Automated Testing](faas-automated-testing/faas-automated-testing.md) +- [Find, getMore and killCursors commands](find_getmore_killcursors_commands/find_getmore_killcursors_commands.md) +- [GridFS Spec](gridfs/gridfs-spec.md) +- [Index Management](index-management/index-management.md) +- [Initial DNS Seedlist Discovery](initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) +- [Load Balancer Support](load-balancers/load-balancers.md) +- [Logging](logging/logging.md) +- [Max Staleness](max-staleness/max-staleness.md) +- [Max Staleness Tests](max-staleness/max-staleness-tests.md) +- [MongoDB Handshake](mongodb-handshake/handshake.md) +- [OCSP Support](ocsp-support/ocsp-support.md) +- [ODM Performance Benchmarking](benchmarking/odm-benchmarking.md) +- [OP_MSG](message/OP_MSG.md) +- [OpenTelemetry](open-telemetry/open-telemetry.md) +- [Performance Benchmarking](benchmarking/benchmarking.md) +- [Polling SRV Records for mongos Discovery](polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md) +- [Read and Write Concern](read-write-concern/read-write-concern.md) +- [Retryable Reads](retryable-reads/retryable-reads.md) +- [Retryable Writes](retryable-writes/retryable-writes.md) +- [Run Command](run-command/run-command.md) +- [SDAM Logging and Monitoring Specification](server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md) +- [SOCKS5 Support](socks5-support/socks5.md) +- [Server Discovery And Monitoring](server-discovery-and-monitoring/server-discovery-and-monitoring.md) +- [Server Discovery And Monitoring -- Summary](server-discovery-and-monitoring/server-discovery-and-monitoring-summary.md) +- [Server Discovery And Monitoring -- Test Plan](server-discovery-and-monitoring/server-discovery-and-monitoring-tests.md) +- [Server Monitoring](server-discovery-and-monitoring/server-monitoring.md) +- [Server Selection](server-selection/server-selection.md) +- [Server Selection Test Plan](server-selection/server-selection-tests.md) +- [Server Wire version and Feature List](wireversion-featurelist/wireversion-featurelist.md) +- [Sessions Specification](sessions/driver-sessions.md) +- [Snapshot Reads Specification](sessions/snapshot-sessions.md) +- [Stable API For Drivers](versioned-api/versioned-api.md) +- [Transactions Specification](transactions/transactions.md) +- [URI Options Specification](uri-options/uri-options.md) +- [Unified Test Format](unified-test-format/unified-test-format.md) +- [Wire Compression in Drivers](compression/OP_COMPRESSED.md) +- [Write Commands Specification](server_write_commands/server_write_commands.md) diff --git a/source/index.rst b/source/index.rst new file mode 100644 index 0000000000..0257a1cdb0 --- /dev/null +++ b/source/index.rst @@ -0,0 +1,17 @@ +MongoDB Specifications +====================== + +.. toctree:: + :maxdepth: 1 + :caption: Specifications: + :glob: + + **/* + * + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md new file mode 100644 index 0000000000..92d4de9e77 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md @@ -0,0 +1,331 @@ +# Initial DNS Seedlist Discovery + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +Presently, seeding a driver with an initial list of ReplicaSet or MongoS addresses is somewhat cumbersome, requiring a +comma-delimited list of host names to attempt connections to. A standardized answer to this problem exists in the form +of SRV records, which allow administrators to configure a single SRV record to return a list of host names. Supporting +this feature would assist our users by decreasing maintenance load, primarily by removing the need to maintain seed +lists at an application level. + +This specification builds on the [Connection String](../connection-string/connection-string-spec.md) specification. It +adds a new protocol scheme and modifies how the +[Host Information](../connection-string/connection-string-spec.md#host-information) is interpreted. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Connection String Format + +The connection string parser in the driver is extended with a new protocol `mongodb+srv` as a logical pre-processing +step before it considers the connection string and SDAM specifications. In this protocol, the comma separated list of +host names is replaced with a single host name. The format is: + +```text +mongodb+srv://{hostname}/{options} +``` + +`{options}` refers to the optional elements from the [Connection String](../connection-string/connection-string-spec.md) +specification following the `Host Information`. This includes the `Auth database` and `Connection Options`. + +For the purposes of this document, `{hostname}` will be divided using the following terminology. If an SRV `{hostname}` +has: + +1. Three or more `.` separated parts, then the left-most part is the `{subdomain}` and the remaining portion is the + `{domainname}`. + + - Examples: + - `{hostname}` = `cluster_1.tests.mongodb.co.uk` + + - `{subdomain}` = `cluster_1` + - `{domainname}` = `tests.mongodb.co.uk` + + - `{hostname}` = `hosts_34.example.com` + + - `{subdomain}` = `hosts_34` + - `{domainname}` = `example.com` + +2. One or two `.` separated part(s), then the `{hostname}` is equivalent to the `{domainname}`, and there is no + subdomain. + + - Examples: + - `{hostname}` = `{domainname}` = `localhost` + - `{hostname}` = `{domainname}` = `mongodb.local` + +Only `{domainname}` is used during SRV record verification and `{subdomain}` is ignored. + +### MongoClient Configuration + +#### srvMaxHosts + +This option is used to limit the number of mongos connections that may be created for sharded topologies. This option +limits the number of SRV records used to populate the seedlist during initial discovery, as well as the number of +additional hosts that may be added during +[SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md). This option +requires a non-negative integer and defaults to zero (i.e. no limit). This option MUST only be configurable at the level +of a `MongoClient`. + +#### srvServiceName + +This option specifies a valid SRV service name according to +[RFC 6335](https://datatracker.ietf.org/doc/html/rfc6335#section-5.1), with the exception that it may exceed 15 +characters as long as the 63rd (62nd with prepended underscore) character DNS query limit is not surpassed. This option +requires a string value and defaults to "mongodb". This option MUST only be configurable at the level of a +`MongoClient`. + +#### URI Validation + +The driver MUST report an error if either the `srvServiceName` or `srvMaxHosts` URI options are specified with a non-SRV +URI (i.e. scheme other than `mongodb+srv`). The driver MUST allow specifying the `srvServiceName` and `srvMaxHosts` URI +options with an SRV URI (i.e. `mongodb+srv` scheme). + +If `srvMaxHosts` is a positive integer, the driver MUST throw an error in the following cases: + +- The connection string contains a `replicaSet` option. +- The connection string contains a `loadBalanced` option with a value of `true`. + +When validating URI options, the driver MUST first do the SRV and TXT lookup and then perform the validation. For +drivers that do SRV lookup asynchronously this may result in a `MongoClient` being instantiated but erroring later +during operation execution. + +### Seedlist Discovery + +#### Validation Before Querying DNS + +It is an error to specify a port in a connection string with the `mongodb+srv` protocol, and the driver MUST raise a +parse error and MUST NOT do DNS resolution or contact hosts. + +It is an error to specify more than one host name in a connection string with the `mongodb+srv` protocol, and the driver +MUST raise a parse error and MUST NOT do DNS resolution or contact hosts. + +If `mongodb+srv` is used, a driver MUST implicitly also enable TLS. Clients can turn this off by passing `tls=false` in +either the Connection String, or options passed in as parameters in code to the MongoClient constructor (or equivalent +API for each driver), but not through a TXT record (discussed in a later section). + +#### Querying DNS + +In this preprocessing step, the driver will query the DNS server for SRV records on the hostname, prefixed with the SRV +service name and protocol. The SRV service name is provided in the `srvServiceName` URI option and defaults to +`mongodb`. The protocol is always `tcp`. After prefixing, the URI should look like: `_{srvServiceName}._tcp.{hostname}`. +This DNS query is expected to respond with one or more SRV records. + +The priority and weight fields in returned SRV records MUST be ignored. + +If the DNS result returns no SRV records, or no records at all, or a DNS error happens, an error MUST be raised +indicating that the URI could not be used to find hostnames. The error SHALL include the reason why they could not be +found. + +A driver MUST verify that the host names returned through SRV records share the original SRV's `{domainname}`. In +addition, SRV records with fewer than three `.` separated parts, the returned hostname MUST have at least one more +domain level than the SRV record hostname. Drivers MUST raise an error and MUST NOT initiate a connection to any +returned hostname which does not fulfill these requirements. + +The driver MUST NOT attempt to connect to any hosts until the DNS query has returned its results. + +If `srvMaxHosts` is zero or greater than or equal to the number of hosts in the DNS result, the driver MUST populate the +seedlist with all hosts. + +If `srvMaxHosts` is greater than zero and less than the number of hosts in the DNS result, the driver MUST randomly +select that many hosts and use them to populate the seedlist. Drivers SHOULD use the +[Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) for +randomization. + +### Default Connection String Options + +As a second preprocessing step, a Client MUST also query the DNS server for TXT records on `{hostname}`. If available, a +TXT record provides default connection string options. The maximum length of a TXT record string is 255 characters, but +there can be multiple strings per TXT record. A Client MUST support multiple TXT record strings and concatenate them as +if they were one single string in the order they are defined in each TXT record. The order of multiple character strings +in each TXT record is guaranteed. A Client MUST NOT allow multiple TXT records for the same host name and MUST raise an +error when multiple TXT records are encountered. + +Information returned within a TXT record is a simple URI string, just like the `{options}` in a connection string. + +A Client MUST only support the `authSource`, `replicaSet`, and `loadBalanced` options through a TXT record, and MUST +raise an error if any other option is encountered. Although using `mongodb+srv://` implicitly enables TLS, a Client MUST +NOT allow the `ssl` option to be set through a TXT record option. + +TXT records MAY be queried either before, in parallel, or after SRV records. Clients MUST query both the SRV and the TXT +records before attempting any connection to MongoDB. + +A Client MUST use options specified in the Connection String, and options passed in as parameters in code to the +MongoClient constructor (or equivalent API for each driver), to override options provided through TXT records. + +If any connection string option in a TXT record is incorrectly formatted, a Client MUST throw a parse exception. + +This specification does not change the behaviour of handling unknown keys or incorrect values as is set out in the +[Connection String spec](../connection-string/connection-string-spec.md#defining-connection-options). Unknown keys or +incorrect values in default options specified through TXT records MUST be handled in the same way as unknown keys or +incorrect values directly specified through a Connection String. For example, if a driver that does not support the +`authSource` option finds `authSource=db` in a TXT record, it MUST handle the unknown option according to the rules in +the Connection String spec. + +### CNAME not supported + +The use of DNS CNAME records is not supported. Clients MUST NOT check for a CNAME record on `{hostname}`. A system's DNS +resolver could transparently handle CNAME, but because of how clients validate records returned from SRV queries, use of +CNAME could break validation. Seedlist discovery therefore does not recommend or support the use of CNAME records in +concert with SRV or TXT records. + +## Example + +If we provide the following URI: + +```text +mongodb+srv://server.mongodb.com/ +``` + +The driver needs to request the DNS server for the SRV record `_mongodb._tcp.server.mongodb.com`. This could return: + +```dns +Record TTL Class Priority Weight Port Target +_mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27317 mongodb1.mongodb.com. +_mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27017 mongodb2.mongodb.com. +``` + +The returned host names (`mongodb1.mongodb.com` and `mongodb2.mongodb.com`) must share the same domainname +(`mongodb.com`) as the provided host name (`server.mongodb.com`). + +The driver also needs to request the DNS server for the TXT records on `server.mongodb.com`. This could return: + +```dns +Record TTL Class Text +server.mongodb.com. 86400 IN TXT "replicaSet=replProduction&authSource=authDB" +``` + +From the DNS results, the driver now MUST treat the host information as if the following URI was used instead: + +```text +mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=authDB +``` + +If we provide the following URI with the same DNS (SRV and TXT) records: + +```text +mongodb+srv://server.mongodb.com/?authSource=otherDB +``` + +Then the default in the TXT record for `authSource` is not used as the value in the connection string overrides it. The +Client MUST treat the host information as if the following URI was used instead: + +```text +mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=otherDB +``` + +## Test Plan + +### Prose Tests + +See README.md in the accompanying [test directory](tests/README.md). + +### Spec Tests + +See README.md in the accompanying [test directory](tests/README.md). + +Additionally, see the `mongodb+srv` test `invalid-uris.yml` in the +[Connection String Spec tests](../connection-string/tests/README.md). + +## Motivation + +Several of our users have asked for this through tickets: + +- +- +- + +## Design Rationale + +The design specifically calls for a pre-processing stage of the processing of connection URLs to minimize the impact on +existing functionality. + +## Justifications + +### Why Are Multiple Key-Value Pairs Allowed in One TXT Record? + +One could imagine an alternative design in which each TXT record would allow only one URI option. No `&` character would +be allowed as a delimiter within TXT records. + +In this spec we allow multiple key-value pairs within one TXT record, delimited by `&`, because it will be common for +all options to fit in a single 255-character TXT record, and it is much more convenient to configure one record in this +case than to configure several. + +Secondly, in some cases the order in which options occur is important. For example, readPreferenceTags can appear both +multiple times, and the order in which they appear is significant. Because DNS servers may return TXT records in any +order, it is only possible to guarantee the order in which readPreferenceTags keys appear by having them in the same TXT +record. + +### Why Is There No Mention of UTF-8 Characters? + +Although DNS TXT records allow any octet to exist in its value, many DNS providers do not allow non-ASCII characters to +be configured. As it is unlikely that any option names or values in the connection string have non-ASCII characters, we +left the behaviour of supporting UTF-8 characters as unspecified. + +## Reference Implementation + +None yet. + +## Backwards Compatibility + +There are no backwards compatibility concerns. + +## Future Work + +In the future we could consider using the priority and weight fields of the SRV records. + +## ChangeLog + +- 2024-09-24: Removed requirement for URI to have three '.' separated parts; these SRVs have stricter parent domain + matching requirements for security. Create terminology section. Remove usage of term `{TLD}`. The `{hostname}` now + refers to the entire hostname, not just the `{subdomain}`. + +- 2024-03-06: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Revise spec front matter and reformat changelog. + +- 2021-10-14: Add `srvMaxHosts` MongoClient option and restructure Seedlist Discovery section. Improve documentation for + the `srvServiceName` MongoClient option and add a new URI Validation section. + +- 2021-09-15: Clarify that service name only defaults to `mongodb`, and should be defined by the `srvServiceName` URI + option. + +- 2021-04-15: Adding in behaviour for load balancer mode. + +- 2019-03-07: Clarify that CNAME is not supported + +- 2018-02-08: Clarify that `{options}}` in the [Specification](#specification) section includes all the optional + elements from the Connection String specification. + +- 2017-11-21: Add clause that using `mongodb+srv://` implies enabling TLS. Add restriction that only `authSource` and + `replicaSet` are allows in TXT records. Add restriction that only one TXT record is supported share the same parent + domain name as the given host name. + +- 2017-11-17: Add new rule that indicates that host names in returned SRV records MUST share the same parent domain name + as the given host name. Remove language and tests for non-ASCII characters. + +- 2017-11-07: Clarified that all parts of listable options such as readPreferenceTags are ignored if they are also + present in options to the MongoClient constructor. Clarified which host names to use for SRV and TXT DNS queries. + +- 2017-11-01: Clarified that individual TXT records can have multiple strings. + +- 2017-10-31: Added a clause that specifying two host names with a `mongodb+srv://` URI is not allowed. Added a few more + test cases. + +- 2017-10-18: Removed prohibition of raising DNS related errors when parsing the URI. + +- 2017-10-04: Removed from [Future Work](#future-work) the line about multiple MongoS discovery. The current + specification already allows for it, as multiple host names which are all MongoS servers is already allowed under + SDAM. And this specification does not modify SDAM. Added support for connection string options through TXT records. + +- 2017-09-19: Clarify that host names in `mongodb+srv://` URLs work like normal host specifications. + +- 2017-09-01: Updated test plan with YAML tests, and moved prose tests for URI parsing into invalid-uris.yml in the + Connection String Spec tests. diff --git a/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst index 5d3ec16159..8c7de7927e 100644 --- a/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst +++ b/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst @@ -1,316 +1,4 @@ -.. role:: javascript(code) - :language: javascript -============================== -Initial DNS Seedlist Discovery -============================== - -:Spec-ticket: SPEC-878, SPEC-937 -:Title: Initial DNS Seedlist Discovery -:Authors: Derick Rethans -:Status: Draft -:Type: Standards -:Last Modified: 2018-02-08 -:Version: 1.3.1 -:Spec Lead: Matt Broadstone -:Advisory Group: \A. Jesse Jiryu Davis -:Approver(s): Bernie Hackett, David Golden, Jeff Yemin, Matt Broadstone, A. Jesse Jiryu Davis - - -.. contents:: - --------- - -Abstract -======== - -Presently, seeding a driver with an initial list of ReplicaSet or MongoS -addresses is somewhat cumbersome, requiring a comma-delimited list of host -names to attempt connections to. A standardized answer to this problem exists -in the form of SRV records, which allow administrators to configure a single -domain to return a list of host names. Supporting this feature would assist -our users by decreasing maintenance load, primarily by removing the need to -maintain seed lists at an application level. - -This specification builds on the `Connection String`_ specification. It adds a -new protocol scheme and modifies how the `Host Information`_ is interpreted. - -.. _`Connection String`: ../connection-string/connection-string-spec.rst -.. _`Host Information`: ../connection-string/connection-string-spec.rst#host-information - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -The connection string parser in the driver is extended with a new protocol -``mongodb+srv`` as a logical pre-processing step before it considers the -connection string and SDAM specifications. In this protocol, the comma -separated list of host names is replaced with a single host name. The -format is:: - - mongodb+srv://{hostname}.{domainname}/{options} - - -``{options}`` refers to the optional elements from the `Connection String`_ -specification following the ``Host Information``. This includes the ``Auth -database`` and ``Connection Options``. - -Seedlist Discovery ------------------- - -In this preprocessing step, the driver will query the DNS server for SRV -records on ``{hostname}.{domainname}``, prefixed with ``_mongodb._tcp.``: -``_mongodb._tcp.{hostname}.{domainname}``. This DNS query is expected to -respond with one or more SRV records. From the DNS result, the driver now MUST -behave the same as if an ``mongodb://`` URI was provided with all the host -names and port numbers that were returned as part of the DNS SRV query result. - -The priority and weight fields in returned SRV records MUST be ignored. - -If ``mongodb+srv`` is used, a driver MUST implicitly also enable TLS. Clients -can turn this off by passing ``ssl=false`` in either the Connection String, -or options passed in as parameters in code to the MongoClient constructor (or -equivalent API for each driver), but not through a TXT record (discussed in -the next section). - -A driver MUST verify that in addition to the ``{hostname}``, the -``{domainname}`` consists of at least two parts: the domain name, and a TLD. -Drivers MUST raise an error and MUST NOT contact the DNS server to obtain SRV -(or TXT records) if the full URI does not consists of at least three parts. - -A driver MUST verify that the host names returned through SRV records have the -same parent ``{domainname}``. Drivers MUST raise an error and MUST NOT -initiate a connection to any returned host name which does not share the same -``{domainname}``. - -It is an error to specify a port in a connection string with the -``mongodb+srv`` protocol, and the driver MUST raise a parse error and MUST NOT -do DNS resolution or contact hosts. - -It is an error to specify more than one host name in a connection string with -the ``mongodb+srv`` protocol, and the driver MUST raise a parse error and MUST -NOT do DNS resolution or contact hosts. - -The driver MUST NOT attempt to connect to any hosts until the DNS query has -returned its results. - -If the DNS result returns no SRV records, or no records at all, or a DNS error -happens, an error MUST be raised indicating that the URI could not be used to -find hostnames. The error SHALL include the reason why they could not be -found. - -Default Connection String Options ---------------------------------- - -As a second preprocessing step, a Client MUST also query the DNS server for -TXT records on ``{hostname}.{domainname}``. If available, a TXT record -provides default connection string options. The maximum length of a TXT record -string is 255 -characters, but there can be multiple strings per TXT record. A Client MUST -support multiple TXT record strings and concatenate them as if they were one -single string in the order they are defined in each TXT record. The order of -multiple character strings in each TXT record is guaranteed. -A Client MUST NOT allow multiple TXT records for the same host name and MUST -raise an error when multiple TXT records are encountered. - -Information returned within a TXT record is a simple URI string, just like -the ``{options}`` in a connection string. - -A Client MUST only support the ``authSource`` and ``replicaSet`` options -through a TXT record, and MUST raise an error if any other option is -encountered. Although using ``mongodb+srv://`` implicitly enables TLS, a -Client MUST NOT allow the ``ssl`` option to be set through a TXT record -option. - -TXT records MAY be queried either before, in parallel, or after SRV records. -Clients MUST query both the SRV and the TXT records before attempting any -connection to MongoDB. - -A Client MUST use options specified in the Connection String, and options -passed in as parameters in code to the MongoClient constructor (or equivalent -API for each driver), to override options provided through TXT records. - -.. _`Connection String spec`: ../connection-string/connection-string-spec.rst#defining-connection-options - -If any connection string option in a TXT record is incorrectly formatted, a -Client MUST throw a parse exception. - -This specification does not change the behaviour of handling unknown keys or -incorrect values as is set out in the `Connection String spec`_. Unknown keys -or incorrect values in default options specified through TXT records MUST be -handled in the same way as unknown keys or incorrect values directly specified -through a Connection String. For example, if a driver that does not support -the ``authSource`` option finds ``authSource=db`` in a TXT record, it MUST handle -the unknown option according to the rules in the Connection String spec. - -Example -======= - -If we provide the following URI:: - - mongodb+srv://server.mongodb.com/ - -The driver needs to request the DNS server for the SRV record -``_mongodb._tcp.server.mongodb.com``. This could return:: - - Record TTL Class Priority Weight Port Target - _mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27317 mongodb1.mongodb.com. - _mongodb._tcp.server.mongodb.com. 86400 IN SRV 0 5 27017 mongodb2.mongodb.com. - -The returned host names (``mongodb1.mongodb.com`` and -``mongodb2.mongodb.com``) must share the same parent domain name -(``mongodb.com``) as the provided host name (``server.mongodb.com``). - -The driver also needs to request the DNS server for the TXT records on -``server.mongodb.com``. This could return:: - - Record TTL Class Text - server.mongodb.com. 86400 IN TXT "replicaSet=replProduction&authSource=authDB" - -From the DNS results, the driver now MUST treat the host information as if the -following URI was used instead:: - - mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=authDB - -If we provide the following URI with the same DNS (SRV and TXT) records:: - - mongodb+srv://server.mongodb.com/?authSource=otherDB - -Then the default in the TXT record for ``authSource`` is not used as -the value in the connection string overrides it. The Client MUST treat the host -information as if the following URI was used instead:: - - mongodb://mongodb1.mongodb.com:27317,mongodb2.mongodb.com:27107/?ssl=true&replicaSet=replProduction&authSource=otherDB - -Test Plan -========= - -See README.rst in the accompanying `test directory`_. - -.. _`test directory`: tests - -Additionally, see the ``mongodb+srv`` test ``invalid-uris.yml`` in the `Connection -String Spec tests`_. - -.. _`Connection String Spec tests`: ../connection-string/tests - -Motivation -========== - -Several of our users have asked for this through tickets: - -* ``_ -* ``_ -* ``_ - -Design Rationale -================ - -The design specifically calls for a pre-processing stage of the processing of -connection URLs to minimize the impact on existing functionality. - -Justifications -============== - -Why Are Multiple Key-Value Pairs Allowed in One TXT Record? ------------------------------------------------------------ - -One could imagine an alternative design in which each TXT record would allow -only one URI option. No ``&`` character would be allowed as a delimiter within -TXT records. - -In this spec we allow multiple key-value pairs within one TXT record, -delimited by ``&``, because it will be common for all options to fit in a -single 255-character TXT record, and it is much more convenient to configure -one record in this case than to configure several. - -Secondly, in some cases the order in which options occur is important. For -example, readPreferenceTags can appear both multiple times, and the order in -which they appear is significant. Because DNS servers may return TXT records -in any order, it is only possible to guarantee the order in which -readPreferenceTags keys appear by having them in the same TXT record. - -Why Is There No Mention of UTF-8 Characters? --------------------------------------------- - -Although DNS TXT records allow any octet to exist in its value, many DNS -providers do not allow non-ASCII characters to be configured. As it is -unlikely that any option names or values in the connection string have -non-ASCII characters, we left the behaviour of supporting UTF-8 characters as -unspecified. - -Reference Implementation -======================== - -None yet. - -Backwards Compatibility -======================= - -There are no backwards compatibility concerns. - -Future Work -=========== - -In the future we could consider using the priority and weight fields of the -SRV records. - -ChangeLog -========= - -2018-02-08 — 1.3.1 - Clarify that ``{options}}`` in the Specification_ section includes all the - optional elements from the Connection String specification. - -2017-11-21 — 1.3.0 - Add clause that using ``mongodb+srv://`` implies enabling TLS. Add - restriction that only ``authSource`` and ``replicaSet`` are allows in TXT - records. Add restriction that only one TXT record is supported share - the same parent domain name as the given host name. - -2017-11-17 — 1.2.0 - Add new rule that indicates that host names in returned SRV records MUST - share the same parent domain name as the given host name. - -2017-11-17 — 1.1.6 - Remove language and tests for non-ASCII characters. - -2017-11-07 — 1.1.5 - Clarified that all parts of listable options such as readPreferenceTags - are ignored if they are also present in options to the MongoClient - constructor. - - Clarified which host names to use for SRV and TXT DNS queries. - -2017-11-01 — 1.1.4 - Clarified that individual TXT records can have multiple strings. - -2017-10-31 — 1.1.3 - Added a clause that specifying two host names with a `mongodb+srv://`` URI - is not allowed. Added a few more test cases. - -2017-10-18 — 1.1.2 - Removed prohibition of raising DNS related errors when parsing the URI. - -2017-10-04 — 1.1.1 - Removed from `Future Work`_ the line about multiple MongoS discovery. The - current specification already allows for it, as multiple host names which - are all MongoS servers is already allowed under SDAM. And this - specification does not modify SDAM. - -2017-10-04 — 1.1 - Added support for connection string options through TXT records. - -2017-09-19 - Clarify that host names in `mongodb+srv://` URLs work like normal host - specifications. - -2017-09-01 - Updated test plan with YAML tests, and moved prose tests for URI parsing - into invalid-uris.yml in the Connection String Spec tests. +.. note:: + This specification has been converted to Markdown and renamed to + `initial-dns-seedlist-discovery.md `_. diff --git a/source/initial-dns-seedlist-discovery/tests/README.md b/source/initial-dns-seedlist-discovery/tests/README.md new file mode 100644 index 0000000000..19e5fdd2e4 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/README.md @@ -0,0 +1,182 @@ +# Initial DNS Seedlist Discovery tests + +This directory contains platform-independent tests that drivers can use to prove their conformance to the Initial DNS +Seedlist Discovery spec. + +## Prose Tests + +For the following prose tests, it is assumed drivers are be able to stub DNS results to easily test invalid DNS +resolution results. + +### 1. Allow SRVs with fewer than 3 `.` separated parts + +When running validation on an SRV string before DNS resolution, do not throw a error due to number of SRV parts. + +- `mongodb+srv://localhost` +- `mongodb+srv://mongo.local` + +### 2. Throw when return address does not end with SRV domain + +When given a returned address that does NOT end with the original SRV's domain name, throw a runtime error. + +For this test, run each of the following cases: + +- the SRV `mongodb+srv://localhost` resolving to `localhost.mongodb` +- the SRV `mongodb+srv://mongo.local` resolving to `test_1.evil.local` +- the SRV `mongodb+srv://blogs.mongodb.com` resolving to `blogs.evil.com` + +Remember, the domain of an SRV with one or two `.` separated parts is the SRVs entire hostname. + +### 3. Throw when return address is identical to SRV hostname + +When given a returned address that is identical to the SRV hostname and the SRV hostname has fewer than three `.` +separated parts, throw a runtime error. + +For this test, run each of the following cases: + +- the SRV `mongodb+srv://localhost` resolving to `localhost` +- the SRV `mongodb+srv://mongo.local` resolving to `mongo.local` + +### 4. Throw when return address does not contain `.` separating shared part of domain + +When given a returned address that does NOT share the domain name of the SRV record because it's missing a `.`, throw a +runtime error. + +For this test, run each of the following cases: + +- the SRV `mongodb+srv://localhost` resolving to `test_1.cluster_1localhost` +- the SRV `mongodb+srv://mongo.local` resolving to `test_1.my_hostmongo.local` +- the SRV `mongodb+srv://blogs.mongodb.com` resolving to `cluster.testmongodb.com` + +## Test Setup + +The tests in the `replica-set` directory MUST be executed against a three-node replica set on localhost ports 27017, +27018, and 27019 with replica set name `repl0`. + +The tests in the `load-balanced` directory MUST be executed against a load-balanced sharded cluster with the mongos +servers running on localhost ports 27017 and 27018 and `--loadBalancerPort` 27050 and 27051, respectively (corresponding +to the script in +[drivers-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/run-load-balancer.sh)). +The load balancers, shard servers, and config servers may run on any open ports. + +The tests in the `sharded` directory MUST be executed against a sharded cluster with the mongos servers running on +localhost ports 27017 and 27018. Shard servers and config servers may run on any open ports. + +In all cases, the clusters MUST be started with SSL enabled. + +To run the tests that accompany this spec, you need to configure the SRV and TXT records with a real name server. The +following records are required for these tests: + +```dns +Record TTL Class Address +localhost.test.build.10gen.cc. 86400 IN A 127.0.0.1 +localhost.sub.test.build.10gen.cc. 86400 IN A 127.0.0.1 + +Record TTL Class Port Target +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +_mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +_mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_mongodb._tcp.test3.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test5.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test6.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test7.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test8.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test10.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test11.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test12.test.build.10gen.cc. 86400 IN SRV 27017 localhost.build.10gen.cc. +_mongodb._tcp.test13.test.build.10gen.cc. 86400 IN SRV 27017 test.build.10gen.cc. +_mongodb._tcp.test14.test.build.10gen.cc. 86400 IN SRV 27017 localhost.not-test.build.10gen.cc. +_mongodb._tcp.test15.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.not-build.10gen.cc. +_mongodb._tcp.test16.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.not-10gen.cc. +_mongodb._tcp.test17.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.not-cc. +_mongodb._tcp.test18.test.build.10gen.cc. 86400 IN SRV 27017 localhost.sub.test.build.10gen.cc. +_mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.evil.build.10gen.cc. +_mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test20.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test21.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_customname._tcp.test22.test.build.10gen.cc 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test23.test.build.10gen.cc. 86400 IN SRV 8000 localhost.test.build.10gen.cc. +_mongodb._tcp.test24.test.build.10gen.cc. 86400 IN SRV 8000 localhost.test.build.10gen.cc. + +Record TTL Class Text +test5.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0&authSource=thisDB" +test6.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0" +test6.test.build.10gen.cc. 86400 IN TXT "authSource=otherDB" +test7.test.build.10gen.cc. 86400 IN TXT "ssl=false" +test8.test.build.10gen.cc. 86400 IN TXT "authSource" +test10.test.build.10gen.cc. 86400 IN TXT "socketTimeoutMS=500" +test11.test.build.10gen.cc. 86400 IN TXT "replicaS" "et=rep" "l0" +test20.test.build.10gen.cc. 86400 IN TXT "loadBalanced=true" +test21.test.build.10gen.cc. 86400 IN TXT "loadBalanced=false" +test24.test.build.10gen.cc. 86400 IN TXT "loadBalanced=true" +``` + +Notes: + +- `test4` is omitted deliberately to test what happens with no SRV record. +- `test9` is missing because it was deleted during the development of the tests. +- The missing `test.` sub-domain in the SRV record target for `test12` is deliberate. +- `test22` is used to test a custom service name (`customname`). +- `test23` and `test24` point to port 8000 (HAProxy) and are used for load-balanced tests. + +In our tests we have used `localhost.test.build.10gen.cc` as the domain, and then configured +`localhost.test.build.10gen.cc` to resolve to 127.0.0.1. + +You need to adapt the records shown above to replace `test.build.10gen.cc` with your own domain name, and update the +"uri" field in the YAML or JSON files in this directory with the actual domain. + +## Test Format and Use + +These YAML and JSON files contain the following fields: + +- `uri`: a `mongodb+srv` connection string +- `seeds`: the expected set of initial seeds discovered from the SRV record +- `numSeeds`: the expected number of initial seeds discovered from the SRV record. This is mainly used to test + `srvMaxHosts`, since randomly selected hosts cannot be deterministically asserted. +- `hosts`: the discovered topology's list of hosts once SDAM completes a scan +- `numHosts`: the expected number of hosts discovered once SDAM completes a scan. This is mainly used to test + `srvMaxHosts`, since randomly selected hosts cannot be deterministically asserted. +- `options`: the parsed [URI options](../../uri-options/uri-options.md) as discovered from the + [Connection String](../../connection-string/connection-string-spec.md)'s "Connection Options" component and SRV + resolution (e.g. TXT records, implicit `tls` default). +- `parsed_options`: additional, parsed options from other + [Connection String](../../connection-string/connection-string-spec.md) components. This is mainly used for asserting + `UserInfo` (as `user` and `password`) and `Auth database` (as `auth_database`). +- `error`: indicates that the parsing of the URI, or the resolving or contents of the SRV or TXT records included + errors. +- `comment`: a comment to indicate why a test would fail. +- `ping`: if false, the test runner should not run a "ping" operation. + +For each YAML file: + +- Create a MongoClient initialized with the `mongodb+srv` connection string. +- Run a "ping" operation unless `ping` is false or `error` is true. + +Assertions: + +- If `seeds` is specified, drivers SHOULD verify that the set of hosts in the client's initial seedlist matches the list + in `seeds`. If `numSeeds` is specified, drivers SHOULD verify that the size of that set matches `numSeeds`. + +- If `hosts` is specified, drivers MUST verify that the set of ServerDescriptions in the client's TopologyDescription + eventually matches the list in `hosts`. If `numHosts` is specified, drivers MUST verify that the size of that set + matches `numHosts`. + +- If `options` is specified, drivers MUST verify each of the values under `options` match the MongoClient's parsed value + for that option. There may be other options parsed by the MongoClient as well, which a test does not verify. + +- If `parsed_options` is specified, drivers MUST verify that each of the values under `parsed_options` match the + MongoClient's parsed value for that option. Supported values include, but are not limited to, `user` and `password` + (parsed from `UserInfo`) and `auth_database` (parsed from `Auth database`). + +- If `error` is specified and `true`, drivers MUST verify that initializing the MongoClient throws an error. If `error` + is not specified or is `false`, both initializing the MongoClient and running a ping operation must succeed without + throwing any errors. + +- If `ping` is not specified or `true`, drivers MUST verify that running a "ping" operation using the initialized + MongoClient succeeds. If `ping` is `false`, drivers MUST NOT run a "ping" operation. + + > **Note:** These tests are expected to be run against MongoDB databases with and without authentication enabled. The + > "ping" operation does not require authentication so should succeed with URIs that contain no userinfo (i.e. no + > username and password). Tests with URIs that contain userinfo always set `ping` to `false` because some drivers will + > fail handshake on a connection if userinfo is provided but incorrect. diff --git a/source/initial-dns-seedlist-discovery/tests/README.rst b/source/initial-dns-seedlist-discovery/tests/README.rst deleted file mode 100644 index 6072deb738..0000000000 --- a/source/initial-dns-seedlist-discovery/tests/README.rst +++ /dev/null @@ -1,95 +0,0 @@ -==================================== -Initial DNS Seedlist Discovery tests -==================================== - -This directory contains platform-independent tests that drivers can use -to prove their conformance to the Initial DNS Seedlist Discovery spec. - -Test Setup ----------- - -Start a three-node replica set on localhost, on ports 27017, 27018, and 27019, -with replica set name "repl0". The replica set MUST be started with SSL -enabled. - -To run the tests that accompany this spec, you need to configure the SRV and -TXT records with a real name server. The following records are required for -these tests:: - - Record TTL Class Address - localhost.test.build.10gen.cc. 86400 IN A 127.0.0.1 - localhost.sub.test.build.10gen.cc. 86400 IN A 127.0.0.1 - - Record TTL Class Port Target - _mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. - _mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. - _mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. - _mongodb._tcp.test3.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test5.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test6.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test7.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test8.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test10.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test11.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - _mongodb._tcp.test12.test.build.10gen.cc. 86400 IN SRV 27017 localhost.build.10gen.cc. - _mongodb._tcp.test13.test.build.10gen.cc. 86400 IN SRV 27017 test.build.10gen.cc. - _mongodb._tcp.test14.test.build.10gen.cc. 86400 IN SRV 27017 localhost.not-test.build.10gen.cc. - _mongodb._tcp.test15.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.not-build.10gen.cc. - _mongodb._tcp.test16.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.not-10gen.cc. - _mongodb._tcp.test17.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.not-cc. - _mongodb._tcp.test18.test.build.10gen.cc. 86400 IN SRV 27017 localhost.sub.test.build.10gen.cc. - _mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.evil.build.10gen.cc. - _mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. - - Record TTL Class Text - test5.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0&authSource=thisDB" - test6.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0" - test6.test.build.10gen.cc. 86400 IN TXT "authSource=otherDB" - test7.test.build.10gen.cc. 86400 IN TXT "ssl=false" - test8.test.build.10gen.cc. 86400 IN TXT "authSource" - test10.test.build.10gen.cc. 86400 IN TXT "socketTimeoutMS=500" - test11.test.build.10gen.cc. 86400 IN TXT "replicaS" "et=rep" "l0" - -Note that ``test4`` is omitted deliberately to test what happens with no SRV -record. ``test9`` is missing because it was deleted during the development of -the tests. The missing ``test.`` sub-domain in the SRV record target for -``test12`` is deliberate. - -In our tests we have used ``localhost.test.build.10gen.cc`` as the domain, and -then configured ``localhost.test.build.10gen.cc`` to resolve to 127.0.0.1. - -You need to adapt the records shown above to replace ``test.build.10gen.cc`` -with your own domain name, and update the "uri" field in the YAML or JSON files -in this directory with the actual domain. - -Test Format and Use -------------------- - -These YAML and JSON files contain the following fields: - -- ``uri``: a mongodb+srv connection string -- ``seeds``: the expected set of initial seeds discovered from the SRV record -- ``hosts``: the discovered topology's list of hosts once SDAM completes a scan -- ``options``: the parsed connection string options as discovered from URI and - TXT records -- ``parsed_options``: additional options present in the `Connection String`_ - URI such as ``Userinfo`` (as ``user`` and ``password``), and ``Auth - database`` (as ``auth_database``). -- ``error``: indicates that the parsing of the URI, or the resolving or - contents of the SRV or TXT records included errors. -- ``comment``: a comment to indicate why a test would fail. - -.. _`Connection String`: ../../connection-string/connection-string-spec.rst - -For each file, create MongoClient initialized with the mongodb+srv connection -string. You SHOULD verify that the client's initial seed list matches the list of -seeds. You MUST verify that the set of ServerDescriptions in the client's -TopologyDescription eventually matches the list of hosts. You MUST verify that -each of the values of the Connection String Options under ``options`` match the -Client's parsed value for that option. There may be other options parsed by -the Client as well, which a test does not verify. In ``uri-with-auth`` the URI -contains a user/password set and additional options are provided in -``parsed_options`` so that tests can verify authentication is maintained when -evaluating URIs. You MUST verify that an error has been thrown if ``error`` is -present. diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json new file mode 100644 index 0000000000..8e459115c1 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.json @@ -0,0 +1,15 @@ +{ + "uri": "mongodb+srv://test24.test.build.10gen.cc/?directConnection=false", + "seeds": [ + "localhost.test.build.10gen.cc:8000" + ], + "hosts": [ + "localhost.test.build.10gen.cc:8000" + ], + "options": { + "loadBalanced": true, + "ssl": true, + "directConnection": false + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml new file mode 100644 index 0000000000..7a77944a30 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-directConnection.yml @@ -0,0 +1,14 @@ +# The TXT record for test24.test.build.10gen.cc contains loadBalanced=true. +# DRIVERS-1721 introduced this test as passing. +uri: "mongodb+srv://test24.test.build.10gen.cc/?directConnection=false" +seeds: + - localhost.test.build.10gen.cc:8000 +hosts: + # In LB mode, the driver does not do server discovery, so the hostname does + # not get resolved to localhost:8000. + - localhost.test.build.10gen.cc:8000 +options: + loadBalanced: true + ssl: true + directConnection: false +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json new file mode 100644 index 0000000000..7f49416aa3 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test4.test.build.10gen.cc/?loadBalanced=true", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because no SRV records are present for this URI." +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml new file mode 100644 index 0000000000..ee716e593e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-no-results.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test4.test.build.10gen.cc/?loadBalanced=true" +seeds: [] +hosts: [] +error: true +comment: Should fail because no SRV records are present for this URI. diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json new file mode 100644 index 0000000000..2133dee532 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test24.test.build.10gen.cc/?replicaSet=replset", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because loadBalanced=true is incompatible with replicaSet" +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml new file mode 100644 index 0000000000..896f7de7b5 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-replicaSet-errors.yml @@ -0,0 +1,6 @@ +# The TXT record for test24.test.build.10gen.cc contains loadBalanced=true. +uri: "mongodb+srv://test24.test.build.10gen.cc/?replicaSet=replset" +seeds: [] +hosts: [] +error: true +comment: Should fail because loadBalanced=true is incompatible with replicaSet diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json new file mode 100644 index 0000000000..f425c06b30 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?loadBalanced=true", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because loadBalanced is true but the SRV record resolves to multiple hosts" +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml new file mode 100644 index 0000000000..c8276c1b7e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-multiple-hosts.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test1.test.build.10gen.cc/?loadBalanced=true" +seeds: [] +hosts: [] +error: true +comment: Should fail because loadBalanced is true but the SRV record resolves to multiple hosts diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json new file mode 100644 index 0000000000..39bff5a23b --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.json @@ -0,0 +1,14 @@ +{ + "uri": "mongodb+srv://test24.test.build.10gen.cc/", + "seeds": [ + "localhost.test.build.10gen.cc:8000" + ], + "hosts": [ + "localhost.test.build.10gen.cc:8000" + ], + "options": { + "loadBalanced": true, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml new file mode 100644 index 0000000000..c373192f01 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/loadBalanced-true-txt.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test24.test.build.10gen.cc/" +seeds: + - localhost.test.build.10gen.cc:8000 +hosts: + # In LB mode, the driver does not do server discovery, so the hostname does + # not get resolved to localhost:8000. + - localhost.test.build.10gen.cc:8000 +options: + loadBalanced: true + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json new file mode 100644 index 0000000000..593a521c26 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test24.test.build.10gen.cc/?srvMaxHosts=1", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true (TXT)" +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml new file mode 100644 index 0000000000..01379cd205 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true-txt.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test24.test.build.10gen.cc/?srvMaxHosts=1" +seeds: [] +hosts: [] +error: true +comment: Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true (TXT) diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json new file mode 100644 index 0000000000..d03a174b1e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test3.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=1", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true" +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml new file mode 100644 index 0000000000..1175225c7a --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-conflicts_with_loadBalanced-true.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test3.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=1" +seeds: [] +hosts: [] +error: true +comment: Should fail because positive integer for srvMaxHosts conflicts with loadBalanced=true diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json new file mode 100644 index 0000000000..474a314fd7 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.json @@ -0,0 +1,15 @@ +{ + "uri": "mongodb+srv://test24.test.build.10gen.cc/?srvMaxHosts=0", + "seeds": [ + "localhost.test.build.10gen.cc:8000" + ], + "hosts": [ + "localhost.test.build.10gen.cc:8000" + ], + "options": { + "loadBalanced": true, + "srvMaxHosts": 0, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml new file mode 100644 index 0000000000..f223a8d558 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero-txt.yml @@ -0,0 +1,11 @@ +# loadBalanced=true (TXT) is permitted because srvMaxHosts is non-positive +uri: "mongodb+srv://test24.test.build.10gen.cc/?srvMaxHosts=0" +seeds: + - localhost.test.build.10gen.cc:8000 +hosts: + - localhost.test.build.10gen.cc:8000 +options: + loadBalanced: true + srvMaxHosts: 0 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json new file mode 100644 index 0000000000..dfc90dc96d --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.json @@ -0,0 +1,15 @@ +{ + "uri": "mongodb+srv://test23.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=0", + "seeds": [ + "localhost.test.build.10gen.cc:8000" + ], + "hosts": [ + "localhost.test.build.10gen.cc:8000" + ], + "options": { + "loadBalanced": true, + "srvMaxHosts": 0, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml new file mode 100644 index 0000000000..f8343e0e3e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/load-balanced/srvMaxHosts-zero.yml @@ -0,0 +1,11 @@ +# loadBalanced=true is permitted because srvMaxHosts is non-positive +uri: "mongodb+srv://test23.test.build.10gen.cc/?loadBalanced=true&srvMaxHosts=0" +seeds: + - localhost.test.build.10gen.cc:8000 +hosts: + - localhost.test.build.10gen.cc:8000 +options: + loadBalanced: true + srvMaxHosts: 0 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.json b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.json new file mode 100644 index 0000000000..b5fcfd2c07 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.json @@ -0,0 +1,19 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/some%2Cdb?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + }, + "parsed_options": { + "defaultDatabase": "some,db" + } +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.yml new file mode 100644 index 0000000000..c0719f0862 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas-escaped.yml @@ -0,0 +1,13 @@ +uri: "mongodb+srv://test1.test.build.10gen.cc/some%2Cdb?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true +parsed_options: + defaultDatabase: some,db diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.json b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.json new file mode 100644 index 0000000000..c1e85f4b99 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.json @@ -0,0 +1,19 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/some,db?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + }, + "parsed_options": { + "defaultDatabase": "some,db" + } +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.yml new file mode 100644 index 0000000000..2b9db7dc3e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/dbname-with-commas.yml @@ -0,0 +1,13 @@ +uri: "mongodb+srv://test1.test.build.10gen.cc/some,db?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true +parsed_options: + defaultDatabase: some,db diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json new file mode 100644 index 0000000000..3f14ff94e7 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "ssl": true, + "directConnection": false + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.yml new file mode 100644 index 0000000000..21de8dc771 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-false.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + ssl: true + directConnection: false +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json new file mode 100644 index 0000000000..ace6700106 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because directConnection=true is incompatible with SRV URIs." +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.yml new file mode 100644 index 0000000000..923e4e6753 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/direct-connection-true.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true" +seeds: [] +hosts: [] +error: true +comment: Should fail because directConnection=true is incompatible with SRV URIs. diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.json b/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.json new file mode 100644 index 0000000000..4493628be9 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.json @@ -0,0 +1,22 @@ +{ + "uri": "mongodb+srv://b*b%40f3tt%3D:%244to%40L8%3DMC@test3.test.build.10gen.cc/mydb%3F?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + }, + "parsed_options": { + "user": "b*b@f3tt=", + "password": "$4to@L8=MC", + "db": "mydb?" + }, + "ping": false, + "comment": "Encoded user, pass, and DB parse correctly" +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.yml new file mode 100644 index 0000000000..4b25a46db5 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/encoded-userinfo-and-db.yml @@ -0,0 +1,19 @@ +uri: "mongodb+srv://b*b%40f3tt%3D:%244to%40L8%3DMC@test3.test.build.10gen.cc/mydb%3F?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true +parsed_options: + user: "b*b@f3tt=" + password: "$4to@L8=MC" + db: "mydb?" +# Don't run a ping for URIs that include userinfo. Ping doesn't require authentication, so missing +# userinfo isn't a problem, but some drivers will fail handshake on a connection if userinfo is +# provided but incorrect. +ping: false +comment: Encoded user, pass, and DB parse correctly diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json b/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json new file mode 100644 index 0000000000..682d32a742 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test21.test.build.10gen.cc/", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "loadBalanced": false, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.yml new file mode 100644 index 0000000000..c7e31da131 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/loadBalanced-false-txt.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test21.test.build.10gen.cc/" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + loadBalanced: false + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json b/source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json index 9a8267eaeb..ebe3fe1e77 100644 --- a/source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.json @@ -12,5 +12,6 @@ "replicaSet": "repl0", "ssl": true }, + "ping": true, "comment": "Is correct, as returned host name shared the URI root \"test.build.10gen.cc\"." } diff --git a/source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.yml similarity index 96% rename from source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.yml index e77c4570d3..92b54ae57f 100644 --- a/source/initial-dns-seedlist-discovery/tests/longer-parent-in-return.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/longer-parent-in-return.yml @@ -8,4 +8,5 @@ hosts: options: replicaSet: repl0 ssl: true +ping: true comment: Is correct, as returned host name shared the URI root "test.build.10gen.cc". diff --git a/source/initial-dns-seedlist-discovery/tests/misformatted-option.json b/source/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/misformatted-option.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.json diff --git a/source/initial-dns-seedlist-discovery/tests/misformatted-option.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/misformatted-option.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/misformatted-option.yml diff --git a/source/initial-dns-seedlist-discovery/tests/no-results.json b/source/initial-dns-seedlist-discovery/tests/replica-set/no-results.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/no-results.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/no-results.json diff --git a/source/initial-dns-seedlist-discovery/tests/no-results.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/no-results.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/no-results.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/no-results.yml diff --git a/source/initial-dns-seedlist-discovery/tests/not-enough-parts.json b/source/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/not-enough-parts.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.json diff --git a/source/initial-dns-seedlist-discovery/tests/not-enough-parts.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/not-enough-parts.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/not-enough-parts.yml diff --git a/source/initial-dns-seedlist-discovery/tests/one-result-default-port.json b/source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json similarity index 93% rename from source/initial-dns-seedlist-discovery/tests/one-result-default-port.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json index cebb3b1ec3..9f7733de80 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-result-default-port.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.json @@ -11,5 +11,6 @@ "options": { "replicaSet": "repl0", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/one-result-default-port.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.yml similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/one-result-default-port.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.yml index 395bcdc968..748d4634ea 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-result-default-port.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-result-default-port.yml @@ -8,3 +8,4 @@ hosts: options: replicaSet: repl0 ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json similarity index 92% rename from source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json index 622668c351..1d740b1b59 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.json @@ -11,5 +11,6 @@ "options": { "replicaSet": "repl0", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.yml similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.yml index 90a702cdbe..2f353ffb74 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-txt-record-multiple-strings.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record-multiple-strings.yml @@ -8,3 +8,4 @@ hosts: options: replicaSet: repl0 ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/one-txt-record.json b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json similarity index 93% rename from source/initial-dns-seedlist-discovery/tests/one-txt-record.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json index 2385021ad4..ecdb0a7e2a 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-txt-record.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.json @@ -12,5 +12,6 @@ "replicaSet": "repl0", "authSource": "thisDB", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/one-txt-record.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.yml similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/one-txt-record.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.yml index 9356eaa2c2..3bc0f0405c 100644 --- a/source/initial-dns-seedlist-discovery/tests/one-txt-record.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/one-txt-record.yml @@ -9,3 +9,4 @@ options: replicaSet: repl0 authSource: thisDB ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.json diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch1.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch1.yml diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.json diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch2.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch2.yml diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.json diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch3.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch3.yml diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.json diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch4.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch4.yml diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.json diff --git a/source/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/parent-part-mismatch5.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/parent-part-mismatch5.yml diff --git a/source/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json b/source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/returned-parent-too-short.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.json diff --git a/source/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/returned-parent-too-short.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-too-short.yml diff --git a/source/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json b/source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/returned-parent-wrong.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.json diff --git a/source/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/returned-parent-wrong.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/returned-parent-wrong.yml diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.json new file mode 100644 index 0000000000..e320c2ca3e --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.json @@ -0,0 +1,17 @@ +{ + "uri": "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "ssl": true, + "srvServiceName": "customname" + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.yml new file mode 100644 index 0000000000..3bf9c2c676 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srv-service-name.yml @@ -0,0 +1,12 @@ +uri: "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + ssl: true + srvServiceName: "customname" +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json new file mode 100644 index 0000000000..6de1e37fa5 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=1", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because positive integer for srvMaxHosts conflicts with replicaSet option (TXT)" +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml new file mode 100644 index 0000000000..3ea5f3a47b --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet-txt.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=1" +seeds: [] +hosts: [] +error: true +comment: Should fail because positive integer for srvMaxHosts conflicts with replicaSet option (TXT) diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json new file mode 100644 index 0000000000..f968757502 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=1", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because positive integer for srvMaxHosts conflicts with replicaSet option" +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml new file mode 100644 index 0000000000..f67f114c23 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-conflicts_with_replicaSet.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=1" +seeds: [] +hosts: [] +error: true +comment: Should fail because positive integer for srvMaxHosts conflicts with replicaSet option diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json new file mode 100644 index 0000000000..70edacfd06 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.json @@ -0,0 +1,18 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2", + "numSeeds": 2, + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "srvMaxHosts": 2, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml new file mode 100644 index 0000000000..b483279614 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-equal_to_srv_records.yml @@ -0,0 +1,17 @@ +# When srvMaxHosts equals the number of SRV records, all hosts are added to the +# seed list. +# +# The replicaSet URI option is omitted to avoid a URI validation error. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2" +numSeeds: 2 +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + srvMaxHosts: 2 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json new file mode 100644 index 0000000000..72540ed408 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.json @@ -0,0 +1,17 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "srvMaxHosts": 3, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml new file mode 100644 index 0000000000..363dd4fd8b --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-greater_than_srv_records.yml @@ -0,0 +1,16 @@ +# When srvMaxHosts is greater than the number of SRV records, all hosts are +# added to the seed list. +# +# The replicaSet URI option is omitted to avoid a URI validation error. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + srvMaxHosts: 3 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json new file mode 100644 index 0000000000..a9d6dd6fd9 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.json @@ -0,0 +1,14 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1", + "numSeeds": 1, + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "srvMaxHosts": 1, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml new file mode 100644 index 0000000000..1143ab375f --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-less_than_srv_records.yml @@ -0,0 +1,16 @@ +# When srvMaxHosts is less than the number of SRV records, a random subset of +# hosts are added to the seed list. We cannot anticipate which hosts will be +# selected, so this test uses numSeeds instead of seeds. Since this is a replica +# set, all hosts should ultimately be discovered by SDAM. +# +# The replicaSet URI option is omitted to avoid a URI validation error. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1" +numSeeds: 1 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + srvMaxHosts: 1 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json new file mode 100644 index 0000000000..e232edb9eb --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.json @@ -0,0 +1,18 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=0", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "authSource": "thisDB", + "replicaSet": "repl0", + "srvMaxHosts": 0, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml new file mode 100644 index 0000000000..81e92a09b1 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero-txt.yml @@ -0,0 +1,16 @@ +# When srvMaxHosts is zero, all hosts are added to the seed list. +# +# replicaSet (TXT) is permitted because srvMaxHosts is non-positive. +uri: "mongodb+srv://test5.test.build.10gen.cc/?srvMaxHosts=0" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + authSource: thisDB + replicaSet: repl0 + srvMaxHosts: 0 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json new file mode 100644 index 0000000000..3421a35a3d --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.json @@ -0,0 +1,18 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=0", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "srvMaxHosts": 0, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml new file mode 100644 index 0000000000..d095b6d677 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/srvMaxHosts-zero.yml @@ -0,0 +1,16 @@ +# When srvMaxHosts is zero, all hosts are added to the seed list. +# +# replicaSet is permitted because srvMaxHosts is non-positive. +uri: "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0&srvMaxHosts=0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + srvMaxHosts: 0 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/two-results-default-port.json b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json similarity index 94% rename from source/initial-dns-seedlist-discovery/tests/two-results-default-port.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json index 66028310a6..43efcc6310 100644 --- a/source/initial-dns-seedlist-discovery/tests/two-results-default-port.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.json @@ -12,5 +12,6 @@ "options": { "replicaSet": "repl0", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/two-results-default-port.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.yml similarity index 96% rename from source/initial-dns-seedlist-discovery/tests/two-results-default-port.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.yml index 61d38b5e82..c3f7156dc7 100644 --- a/source/initial-dns-seedlist-discovery/tests/two-results-default-port.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-default-port.yml @@ -9,3 +9,4 @@ hosts: options: replicaSet: repl0 ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json similarity index 94% rename from source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json index 4900f7cff1..f6e8e415a7 100644 --- a/source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.json @@ -12,5 +12,6 @@ "options": { "replicaSet": "repl0", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.yml similarity index 96% rename from source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.yml index 7185f52cd6..fe66308111 100644 --- a/source/initial-dns-seedlist-discovery/tests/two-results-nonstandard-port.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/two-results-nonstandard-port.yml @@ -9,3 +9,4 @@ hosts: options: replicaSet: repl0 ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/two-txt-records.json b/source/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/two-txt-records.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.json diff --git a/source/initial-dns-seedlist-discovery/tests/two-txt-records.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/two-txt-records.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/two-txt-records.yml diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.json diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/txt-record-not-allowed-option.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-not-allowed-option.yml diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json similarity index 93% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json index 0ebc737bd5..3d84cfe446 100644 --- a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.json @@ -12,5 +12,6 @@ "replicaSet": "repl0", "authSource": "thisDB", "ssl": false - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.yml similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.yml index 2a922aa234..8d1a46c396 100644 --- a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-ssl-option.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-ssl-option.yml @@ -9,3 +9,4 @@ options: replicaSet: repl0 authSource: thisDB ssl: false +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json similarity index 93% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json index 2626ba6083..1a5a240680 100644 --- a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.json @@ -12,5 +12,6 @@ "replicaSet": "repl0", "authSource": "otherDB", "ssl": true - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.yml similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.yml index a9015599e7..200ac6803e 100644 --- a/source/initial-dns-seedlist-discovery/tests/txt-record-with-overridden-uri-option.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-overridden-uri-option.yml @@ -9,3 +9,4 @@ options: replicaSet: repl0 authSource: otherDB ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.json diff --git a/source/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/txt-record-with-unallowed-option.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/txt-record-with-unallowed-option.yml diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.json b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.json similarity index 95% rename from source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.json index 32710d75f7..c5513a0dad 100644 --- a/source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.json @@ -15,5 +15,6 @@ }, "parsed_options": { "auth_database": "adminDB" - } + }, + "ping": true } diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.yml similarity index 96% rename from source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.yml index fb714bde0e..012e9a023e 100644 --- a/source/initial-dns-seedlist-discovery/tests/uri-with-admin-database.yml +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-admin-database.yml @@ -11,3 +11,4 @@ options: ssl: true parsed_options: auth_database: adminDB +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-auth.json b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.json similarity index 83% rename from source/initial-dns-seedlist-discovery/tests/uri-with-auth.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.json index cc7257d85b..872f997cc7 100644 --- a/source/initial-dns-seedlist-discovery/tests/uri-with-auth.json +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.json @@ -9,9 +9,14 @@ "localhost:27018", "localhost:27019" ], + "options": { + "replicaSet": "repl0", + "ssl": true + }, "parsed_options": { "user": "auser", "password": "apass" }, + "ping": false, "comment": "Should preserve auth credentials" } diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.yml new file mode 100644 index 0000000000..95f9b96426 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-auth.yml @@ -0,0 +1,19 @@ +uri: "mongodb+srv://auser:apass@test1.test.build.10gen.cc/?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true +parsed_options: + user: auser + password: apass +# Don't run a ping for URIs that include userinfo. Ping doesn't require authentication, so missing +# userinfo isn't a problem, but some drivers will fail handshake on a connection if userinfo is +# provided but incorrect. +ping: false +comment: Should preserve auth credentials diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-port.json b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/uri-with-port.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.json diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-port.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/uri-with-port.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-port.yml diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.json rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.json diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.yml similarity index 100% rename from source/initial-dns-seedlist-discovery/tests/uri-with-two-hosts.yml rename to source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-two-hosts.yml diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.json b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.json new file mode 100644 index 0000000000..40579aa44c --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://TEST1.TEST.BUILD.10GEN.CC", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.yml b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.yml new file mode 100644 index 0000000000..57ca1bbb48 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/replica-set/uri-with-uppercase-hostname.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://TEST1.TEST.BUILD.10GEN.CC" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json new file mode 100644 index 0000000000..7d2f9a6bf8 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.json @@ -0,0 +1,17 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2", + "numSeeds": 2, + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "options": { + "srvMaxHosts": 2, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml new file mode 100644 index 0000000000..84aeac9aec --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-equal_to_srv_records.yml @@ -0,0 +1,14 @@ +# When srvMaxHosts equals the number of SRV records, all hosts are added to the +# seed list. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2" +numSeeds: 2 +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +options: + srvMaxHosts: 2 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json new file mode 100644 index 0000000000..452c7b54db --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "options": { + "srvMaxHosts": 3, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml new file mode 100644 index 0000000000..8195fd0d33 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-greater_than_srv_records.yml @@ -0,0 +1,13 @@ +# When srvMaxHosts is greater than the number of SRV records, all hosts are +# added to the seed list. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=3" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +options: + srvMaxHosts: 3 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json new file mode 100644 index 0000000000..cd3bf65117 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.json @@ -0,0 +1,10 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1", + "numSeeds": 1, + "numHosts": 1, + "options": { + "srvMaxHosts": 1, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml new file mode 100644 index 0000000000..e33429b67a --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-less_than_srv_records.yml @@ -0,0 +1,11 @@ +# When srvMaxHosts is less than the number of SRV records, a random subset of +# hosts are added to the seed list. We cannot anticipate which hosts will be +# selected, so this test uses numSeeds and numHosts instead of seeds and hosts, +# respectively. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=1" +numSeeds: 1 +numHosts: 1 +options: + srvMaxHosts: 1 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json new file mode 100644 index 0000000000..f289628c9c --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "options": { + "srvMaxHosts": 0, + "ssl": true + }, + "ping": true +} diff --git a/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml new file mode 100644 index 0000000000..5692ed09b8 --- /dev/null +++ b/source/initial-dns-seedlist-discovery/tests/sharded/srvMaxHosts-zero.yml @@ -0,0 +1,12 @@ +# When srvMaxHosts is zero, all hosts are added to the seed list. +uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +options: + srvMaxHosts: 0 + ssl: true +ping: true diff --git a/source/initial-dns-seedlist-discovery/tests/uri-with-auth.yml b/source/initial-dns-seedlist-discovery/tests/uri-with-auth.yml deleted file mode 100644 index 9ecfca73ea..0000000000 --- a/source/initial-dns-seedlist-discovery/tests/uri-with-auth.yml +++ /dev/null @@ -1,12 +0,0 @@ -uri: "mongodb+srv://auser:apass@test1.test.build.10gen.cc/?replicaSet=repl0" -seeds: - - localhost.test.build.10gen.cc:27017 - - localhost.test.build.10gen.cc:27018 -hosts: - - localhost:27017 - - localhost:27018 - - localhost:27019 -parsed_options: - user: auser - password: apass -comment: Should preserve auth credentials diff --git a/source/load-balancers/load-balancers.md b/source/load-balancers/load-balancers.md new file mode 100644 index 0000000000..15ece56c7c --- /dev/null +++ b/source/load-balancers/load-balancers.md @@ -0,0 +1,337 @@ +# Load Balancer Support + +- Status: Accepted +- Minimum Server Version: 5.0 + +______________________________________________________________________ + +## Abstract + +This specification defines driver behaviour when connected to MongoDB services through a load balancer. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### SDAM + +An abbreviated form of "Server Discovery and Monitoring", specification defined in +[Server Discovery and Monitoring Specification](../server-discovery-and-monitoring/server-discovery-and-monitoring.md). + +#### Service + +Any MongoDB service that can run behind a load balancer. + +### MongoClient Configuration + +#### loadBalanced + +To specify to the driver to operate in load balancing mode, a connection string option of `loadBalanced=true` MUST be +added to the connection string. This boolean option specifies whether or not the driver is connecting to a MongoDB +cluster through a load balancer. The default value MUST be false. This option MUST only be configurable at the level of +a `MongoClient`. + +#### URI Validation + +When `loadBalanced=true` is provided in the connection string, the driver MUST throw an exception in the following +cases: + +- The connection string contains more than one host/port. +- The connection string contains a `replicaSet` option. +- The connection string contains a `directConnection` option with a value of `true`. +- The connection string contains an `srvMaxHosts` option with a positive integer value. + +If a URI is provided with the `mongodb+srv` scheme, the driver MUST first do the SRV and TXT lookup and then perform the +validation. For drivers that do SRV lookup asynchronously this may result in a `MongoClient` being instantiated but +erroring later during operation execution. + +#### DNS Seedlist Discovery + +The connection string option for `loadBalanced=true` MUST be valid in a TXT record and when present MUST be validated as +defined in the URI Validation section. + +When a MongoClient is configured with an SRV URI and `loadBalanced=true`, the driver MUST NOT poll for changes in the +SRV record as is done for non-load balanced sharded clusters. + +### Server Discovery Logging and Monitoring + + + +#### Monitoring + +When `loadBalanced=true` is specified in the URI the topology MUST start in type `LoadBalanced` and MUST remain as +`LoadBalanced` indefinitely. The topology MUST contain 1 `ServerDescription` with a `ServerType` of - +Code:`LoadBalancer`. The "address" field of the `ServerDescription` MUST be set to the address field of the load +balancer. All other fields in the - Code:`ServerDescription` MUST remain unset. In this mode the driver MUST NOT start a +monitoring connection. The `TopologyDescription`'s `compatible` field MUST always be `true`. + +Although there is no monitoring connection in load balanced mode, drivers MUST emit the following series of SDAM events: + +- `TopologyOpeningEvent` when the topology is created. +- `TopologyDescriptionChangedEvent`. The `previousDescription` field MUST have `TopologyType` `Unknown` and no servers. + The `newDescription` MUST have `TopologyType` `LoadBalanced` and one server with `ServerType` `Unknown`. +- `ServerOpeningEvent` when the server representing the load balancer is created. +- `ServerDescriptionChangedEvent`. The `previousDescription` MUST have `ServerType` `Unknown`. The `newDescription` MUST + have `ServerType` `LoadBalancer`. +- `TopologyDescriptionChangedEvent`. The `newDescription` MUST have `TopologyType` `LoadBalanced` and one server with + `ServerType` `LoadBalancer`. + +Drivers MUST also emit a `ServerClosedEvent` followed by a `TopologyDescriptionChangedEvent` that transitions the +`Topology` to the `UNKNOWN` state and a `TopologyClosedEvent` when the topology is closed and MUST NOT emit any other +events when operating in this mode. + +#### Log Messages + +SDAM events details described in [Monitoring](#monitoring) apply to corresponding log messages. Please refer to the +[SDAM logging specification](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#log-messages) +for details on SDAM logging. Drivers MUST emit the relevant SDAM log messages, such as: + +- [Starting Topology Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#starting-topology-monitoring-log-message) +- [Stopped Topology Mmonitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#stopped-topology-monitoring-log-message) +- [Starting Server Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#starting-server-monitoring-log-message) +- [Stopped Server Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#stopped-server-monitoring-log-message) +- [Topology Description Changed](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#topology-description-changed-log-message) + +### Driver Sessions + +#### Session Support + +When the `TopologyType` is `LoadBalanced`, sessions are always supported. + +#### Session Expiration + +When in load balancer mode, drivers MUST ignore `logicalSessionTimeoutMinutes` and MUST NOT prune client sessions from +the session pool when implemented by the driver. + +#### Data-Bearing Server Type + +A `ServerType` of `LoadBalancer` MUST be considered a data-bearing server. + +### Server Selection + +A deployment of topology type Load Balanced contains one server of type `LoadBalancer`. + +For read and write operations, the single server in the topology MUST always be selected. + +During command construction, the LoadBalancer server MUST be treated like a mongos and drivers MUST add a +`$readPreference` field to the command when required by +[Passing read preference to mongos and load balancers](../server-selection/server-selection.md#passing-read-preference-to-mongos-and-load-balancers). + +### Connection Pooling + +#### Connection Establishment + +In the case of the driver having the `loadBalanced=true` connection string option specified, every pooled connection +MUST add a `loadBalanced` field to the - Code:`hello` command in its +[handshake](../mongodb-handshake/handshake.md#connection-handshake). The value of the field MUST be `true`. If +`loadBalanced=true` is specified then the `OP_MSG` protocol MUST be used for all steps of the connection handshake. + +Example: + +Driver connection string contains `loadBalanced=true`: + +```typescript +{ hello: 1, loadBalanced: true } +``` + +Driver connection string contains `loadBalanced=false` or no - Code:`loadBalanced` option: + +```typescript +{ hello: 1 } +``` + +When the server's hello response does not contain a `serviceId` field, the driver MUST throw an exception with the +message "Driver attempted to initialize in load balancing mode, but the server does not support this mode." + +For single threaded drivers that do not use a connection pool, the driver MUST have only 1 socket connection to the load +balancer in load balancing mode. + +#### Connection Pinning + +Some features in MongoDB such as cursors and transactions require sending multiple commands to the same mongos in a +sharded cluster. In load balanced mode, it is not possible to target the same mongos behind a load balancer when pooling +connections. To account for this, drivers MUST pin to a single connection for these features. When using a pinned +connection, the driver MUST emit only 1 - Code:`ConnectionCheckOutStartedEvent`, and only 1 `ConnectionCheckedOutEvent` +or `ConnectionCheckOutFailedEvent`. Similarly, the driver MUST only publish 1 - Code:`ConnectionCheckedInEvent`. + +#### Behaviour With Cursors + +When the driver is in load balancing mode and executing any cursor-initiating command, the driver MUST NOT check the +connection back into the pool unless the command fails or the server returns a cursor ID of `0` (i.e. all documents are +returned in a single batch). Otherwise, the driver MUST continue to use the same connection for all subsequent - +Code:`getMore` commands for the cursor. The driver MUST check the connection back into the pool if the server returns a +cursor ID of `0` in a `getMore` response (i.e. the cursor is drained). When the cursor's `close` method is invoked, +either explicitly or via an implicit resource cleanup mechanism, the driver MUST use the same connection to execute a +`killCursors` command if necessary and then check the connection back into the pool regardless of the result. + +For multi-threaded drivers, cursors with pinned connections MUST either document to the user that calling `next()` and +`close()` operations on the cursor concurrently is not permitted, or explicitly prevent cursors from executing those +operations simultaneously. + +If a `getMore` fails with a network error, drivers MUST leave the connection pinned to the cursor. When the cursor's +`close` method is invoked, drivers MUST NOT execute a `killCursors` command because the pinned connection is no longer +valid and MUST return the connection back to the pool. + +#### Behaviour With Transactions + +When executing a transaction in load balancing mode, drivers MUST follow the rules outlined in +[Sharded Transactions](../transactions/transactions.md#sharded-transactions) with one exception: drivers MUST use the +same connection for all commands in the transaction (excluding retries of commitTranscation and abortTransaction in some +cases). Pinning to a single connection ensures that all commands in the transaction target the same service behind the +load balancer. The rules for pinning to a connection and releasing a pinned connection are the same as those for server +pinning in non-load balanced sharded transactions as described in +[When to unpin](../transactions/transactions.md#when-to-unpin). Drivers MUST NOT use the same connection for two +concurrent transactions run under different sessions from the same client. + +#### Connection Tracking + +The driver connection pool MUST track the purpose for which connections are checked out in the following 3 categories: + +- Connections checked out for cursors +- Connections checked out for transactions +- Connections checked out for operations not falling under the previous 2 categories + +When the connection pool's `maxPoolSize` is reached and the pool times out waiting for a new connection the +`WaitQueueTimeoutError` MUST include a new detailed message, "Timeout waiting for connection from the connection pool. +maxPoolSize: n, connections in use by cursors: n, connections in use by transactions: n, connections in use by other +operations: n". + +### Error Handling + +#### Initial Handshake Errors + +When establishing a new connection in load balanced mode, drivers MUST NOT perform SDAM error handling for any errors +that occur before the MongoDB Handshake (i.e. `hello` command) is complete. Errors during the MongoDB Handshake MUST +also be ignored for SDAM error handling purposes. Once the initial handshake is complete, the connection MUST determine +its generation number based on the `serviceId` field in the handshake response. Any errors that occur during the rest of +connection establishment (e.g. errors during authentication commands) MUST go through the SDAM error handling flow but +MUST NOT mark the server as - Code:`Unknown` and when requiring the connection pool to be cleared, MUST only clear +connections for the `serviceId`. + +#### Post-Handshake Errors + +When the driver is operating in load balanced mode and an application operation receives a state change error, the +driver MUST NOT make any changes to the `TopologyDescription` or the `ServerDescription` of the load balancer (i.e. it +MUST NOT mark the load balancer as `Unknown`). If the error requires the connection pool to be cleared, the driver MUST +only clear connections with the same `serviceId` as the connection which errored. + +### Events + +When in load balancer mode the driver MUST now include the `serviceId` in the - Code:`CommandStartedEvent`, +`CommandSucceededEvent`, and - Code:`CommandFailedEvent`. The driver MAY decide how to expose this information. Drivers +that have a `ConnectionId` object for example, MAY choose to provide a - Code:`serviceId` in that object. The +`serviceId` field is only present when in load balancer mode and connected to a service that is behind a load balancer. + +Additionally the `PoolClearedEvent` MUST also contain a `serviceId` field. + +### Downstream Visible Behavioral Changes + +Services MAY add a command line option or other configuration parameter, that tells the service it is running behind a +load balancer. Services MAY also dynamically determine whether they are behind a load balancer. + +All services which terminate TLS MUST be configured to return a TLS certificate for a hostname which matches the +hostname the client is connecting to. + +All services behind a load balancer that have been started with the aforementioned option MUST add a top level +`serviceId` field to their response to the `hello` command. This field MUST be a BSON `ObjectId` and SHOULD NOT change +while the service is running. When a driver is configured to not be in load balanced mode and the service is configured +behind a load balancer, the service MAY return an error from the driver's `hello` command that the driver is not +configured to use it properly. + +All services that have the behaviour of reaping idle cursors after a specified period of time MAY also close the +connection associated with the cursor when the cursor is reaped. Conversely, those services MAY reap a cursor when the +connection associated with the cursor is closed. + +All services that have the behaviour of reaping idle transactions after a specified period of time MAY also close the +connection associated with the transaction when the transaction is reaped. Conversely, those services must abort a +transaction when the connection associated with the transaction is closed. + +Any applications that connect directly to services and not through the load balancer MUST connect via the regular +service port as they normally would and not the port specified by the `loadBalancerPort` option. The `loadBalanced=true` +URI option MUST be omitted in this case. + +### Q&A + +#### Why use a connection string option instead of a new URI scheme? + +Use of a connection string option would allow the driver to continue to use SRV records that pointed at a load balancer +instead of a replica set without needing to change the URI provided to the `MongoClient`. The SRV records could also +provide the default `loadBalanced=true` in the TXT records. + +#### Why explicitly opt-in to this behaviour instead of letting mongos inform the driver of the load balancer? + +Other versions of this design proposed a scheme in which the application does not have to opt-in to load balanced mode. +Instead, the server would send a special field in `hello` command responses to indicate that it was running behind a +load balancer and the driver would change its behavior accordingly. We opted to take an approach that required code +changes instead because load balancing changes driver behavior in ways that could cause unexpected application errors, +so it made sense to have applications consciously opt-in to this mode. For example, connection pinning creates new +stresses on connection pools because we go from a total of `numMongosServers * maxPoolSize` connections to simply +maxPoolSize. Furthermore, connections get pinned to open cursors and transactions, further straining resource +availability. Due to this change, applications may also need to increase the configured `maxPoolSize` when opting into +this mode. + +#### Why does this specification instruct drivers to not check connections back into the connection pool in some circumstances? + +In the case of a load balancer fronting multiple services, it is possible that a connection to the load balancer could +result in a connection behind the load balancer to a different service. In order to guarantee these operations execute +on the same service they need to be executed on the same socket - not checking a connection back into the pool for the +entire operation guarantees this. + +#### What reason has a client side connection reaper for idle cursors not been put into this specification? + +It was discussed as a potential solution for maxed out connection pools that the drivers could potentially behave +similar to the server and close long running cursors after a specified time period and return their connections to the +pool. Due to the high complexity of that solution it was determined that better error messaging when the connection pool +was maxed out would suffice in order for users to easily debug when the pool ran out of connections and fix their +applications or adjust their pool options accordingly. + +#### Why are we requiring mongos servers to add a new serviceId field in hello responses rather than reusing the existing topologyVersion.processId? + +This option was previously discussed, but we opted to add a new `hello` response field in order to not mix intentions. + +#### Why does this specification not address load balancer restarts or maintenance? + +The Layer 4 load balancers that would be in use for this feature lack the ability that a layer 7 load balancer could +potentially have to be able to understand the MongoDB wire protocol and respond to monitoring requests. + +### Design Rationales + +Services cannot dynamically switch from running behind a load balancer and not running behind a load balancer. Based on +that, this design forces the application to opt-in to this behaviour and make potential changes that require restarts to +their applications. If this were to change, see alternative designs below. + +### Alternative Designs + +#### Service PROXY Detection + +An alternative to the driver using a connection string option to put it into load balancing mode would be for the +service the driver is connected to to inform the driver it is behind a load balancer. A possible solution for this would +be for all services to understand the PROXY protocol such as Data Lake does, and to alter their hello responses to +inform the driver they are behind a load balancer, potentially with the IP address of the load balancer itself. + +The benefit of this solution would be that no changes would be required from the application side, and could also not +require a restart of any application. A single request to the service through the load balancer could automatically +trigger the change in the hello response and cause the driver to switch into load balancing mode pointing at the load +balancer's IP address. Also with this solution it would provide services the ability to record the original IP addresses +of the application that was connecting to it as they are provided the PROXY protocol's header bytes. + +The additional complexity of this alternative on the driver side is that instead of starting in a single mode and +remaining there for the life of the application, the driver would need to deal with additional state changes based on +the results of the server monitors. From a service perspective, every service would need to be updated to understand the +PROXY protocol header bytes prepended to the initial connection and modify their states and hello responses accordingly. +Additionally load balancers would need to have additional configuration as noted in the reference section below, and +only load balancers that support the PROXY protocol would be supported. + +## Changelog + +- 2024-04-25: Clarify that `TopologyDescriptionChangedEvent` must be emitted on topology close +- 2024-03-06: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter and reformat changelog. +- 2022-01-18: Clarify that `OP_MSG` must be used in load balanced mode. +- 2021-12-22: Clarify that pinned connections in transactions are exclusive. +- 2021-10-14: Note that `loadBalanced=true` conflicts with `srvMaxHosts`. diff --git a/source/load-balancers/load-balancers.rst b/source/load-balancers/load-balancers.rst new file mode 100644 index 0000000000..9bb5750a0f --- /dev/null +++ b/source/load-balancers/load-balancers.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `load-balancers.md `_. diff --git a/source/load-balancers/tests/README.md b/source/load-balancers/tests/README.md new file mode 100644 index 0000000000..45f185caa6 --- /dev/null +++ b/source/load-balancers/tests/README.md @@ -0,0 +1,49 @@ +# Load Balancer Support Tests + +______________________________________________________________________ + +## Introduction + +This document describes how drivers should create load balanced clusters for testing and how tests should be executed +for such clusters. + +## Testing Requirements + +For each server version that supports load balanced clusters, drivers MUST add two Evergreen tasks: one with a sharded +cluster with both authentication and TLS enabled and one with a sharded cluster with authentication and TLS disabled. In +each task, the sharded cluster MUST be configured with two mongos nodes running on localhost ports 27017 and 27018. The +shard and config servers may run on any free ports. Each task MUST also start up two TCP load balancers operating in +round-robin mode: one fronting both mongos servers and one fronting a single mongos. + +### Load Balancer Configuration + +Drivers MUST use the `run-load-balancer.sh` script in `drivers-evergreen-tools` to start the TCP load balancers for +Evergreen tasks. This script MUST be run after the backing sharded cluster has already been started. The script writes +the URIs of the load balancers to a YAML expansions file, which can be read by drivers via the `expansions.update` +Evergreen command. This will store the URIs into the `SINGLE_MONGOS_LB_URI` and `MULTI_MONGOS_LB_URI` environment +variables. + +### Test Runner Configuration + +If the backing sharded cluster is configured with TLS enabled, drivers MUST add the relevant TLS options to both +`SINGLE_MONGOS_LB_URI` and `MULTI_MONGOS_LB_URI` to ensure that test clients can connect to the cluster. Drivers MUST +use the final URI stored in `SINGLE_MONGOS_LB_URI` (with additional TLS options if required) to configure internal +clients for test runners (e.g. the internal MongoClient described by the +[Unified Test Format spec](../../unified-test-format/unified-test-format.md)). + +In addition to modifying load balancer URIs, drivers MUST also mock server support for returning a `serviceId` field in +`hello` or legacy `hello` command responses when running tests against a load-balanced cluster. This can be done by +using the value of `topologyVersion.processId` to set `serviceId`. This MUST be done for all connections established by +the test runner, including those made by any internal clients. + +## Tests + +The YAML and JSON files in this directory contain platform-independent tests written in the +[Unified Test Format](../../unified-test-format/unified-test-format.md). Drivers MUST run the following test suites +against a load balanced cluster: + +1. All test suites written in the Unified Test Format +2. Retryable Reads +3. Retryable Writes +4. Change Streams +5. Initial DNS Seedlist Discovery diff --git a/source/load-balancers/tests/cursors.json b/source/load-balancers/tests/cursors.json new file mode 100644 index 0000000000..b11bf2c6fa --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/cursors.yml b/source/load-balancers/tests/cursors.yml new file mode 100644 index 0000000000..1b558affe1 --- /dev/null +++ b/source/load-balancers/tests/cursors.yml @@ -0,0 +1,507 @@ +description: cursors are correctly pinned to connections for load-balanced clusters + +schemaVersion: '1.4' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - connectionReadyEvent + - connectionClosedEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + - collection: + id: &collection2 collection2 + database: *database0 + collectionName: &collection2Name coll2 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - collectionName: *collection1Name + databaseName: *database0Name + documents: [] + - collectionName: *collection2Name + 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 cursor0 + - &assertConnectionNotPinned + name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + 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: + - &createAndSaveCursor + name: createFindCursor + object: *collection0 + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor0 cursor0 + - &assertConnectionPinned + 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 } + - *assertConnectionNotPinned + - &closeCursor + name: close + object: *cursor0 + expectEvents: + - client: *client0 + events: + - &findWithBatchSizeStarted + commandStartedEvent: + command: + find: *collection0Name + filter: {} + batchSize: 2 + commandName: find + - &findWithBatchSizeSucceeded + commandSucceededEvent: + reply: + cursor: + id: { $$type: [ int, long ] } + firstBatch: { $$type: array } + ns: { $$type: string } + commandName: find + - &getMoreStarted + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + commandName: getMore + - &getMoreSucceeded + 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: + - *createAndSaveCursor + - *assertConnectionPinned + - *closeCursor + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *findWithBatchSizeStarted + - *findWithBatchSizeSucceeded + - &killCursorsStarted + commandStartedEvent: + commandName: killCursors + - &killCursorsSucceeded + commandSucceededEvent: + commandName: killCursors + - client: *client0 + eventType: cmap + events: + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + # If a network error occurs during a getMore request, the connection must remain pinned. and drivers must not + # attempt to send a killCursors command when the cursor is closed because the connection is no longer valid. + - 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 + - *createAndSaveCursor + - *assertConnectionPinned + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: + _id: 1 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: + _id: 2 + # Third next() call should perform a getMore. + - name: iterateUntilDocumentOrError + object: *cursor0 + expectError: + # Network errors are considered client-side errors per the unified test format spec. + isClientError: true + - *assertConnectionPinned + - *closeCursor # Execute a close operation to actually release the connection. + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *findWithBatchSizeStarted + - *findWithBatchSizeSucceeded + - *getMoreStarted + - &getMoreFailed + commandFailedEvent: + commandName: getMore + - client: *client0 + eventType: cmap + events: + # Events to set the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the find command + getMore. + - connectionCheckedOutEvent: {} + # Events for the close() operation. + - 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 + - *createAndSaveCursor + - *assertConnectionPinned + - *closeCursor + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *findWithBatchSizeStarted + - *findWithBatchSizeSucceeded + - *killCursorsStarted + - commandFailedEvent: + commandName: killCursors + - client: *client0 + eventType: cmap + events: + # Events to set the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the find command + killCursors. + - 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: &hostNotFoundCode 7 # This is not a state change error code, so it should not cause SDAM changes. + - *createAndSaveCursor + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: + _id: 1 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: + _id: 2 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectError: + errorCode: *hostNotFoundCode + - *assertConnectionPinned + - *closeCursor + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *findWithBatchSizeStarted + - *findWithBatchSizeSucceeded + - *getMoreStarted + - *getMoreFailed + - *killCursorsStarted + - *killCursorsSucceeded + - client: *client0 + eventType: cmap + events: + # Events to set the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the find command + getMore + killCursors. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + # Basic tests for cursor-creating commands besides "find". We don't need to replicate the full set of tests defined + # above for each such command. Instead, only one test is needed per command to ensure that the pinned connection is + # correctly passed down to the server. + # + # Each test creates a cursor with a small batch size and fully iterates it. Because drivers do not publish CMAP + # events when using pinned connections, each test asserts that only one set of ready/checkout/checkin events are + # published. + + - 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: *collection0Name + cursor: + batchSize: 2 + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + - *getMoreStarted + - *getMoreSucceeded + - client: *client0 + eventType: cmap + events: + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: listCollections pins the cursor to a connection + runOnRequirements: + - serverless: forbid # CLOUDP-98562 listCollections batchSize is ignored on serverless. + 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 + # Write out the event for getMore rather than using the getMoreStarted anchor because the "collection" field + # is not equal to *collection0Name as the command is not executed against a collection. + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: { $$type: string } + commandName: getMore + - *getMoreSucceeded + - client: *client0 + eventType: cmap + events: + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: listIndexes pins the cursor to a connection + operations: + # There is an automatic index on _id so we create two more indexes to force multiple batches with batchSize=2. + - name: createIndex + object: *collection0 + arguments: + keys: &x1IndexSpec { x: 1 } + name: &x1IndexName x_1 + - name: createIndex + object: *collection0 + arguments: + keys: &y1IndexSpec { y: 1 } + name: &y1IndexName 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: *collection0Name + indexes: + - name: *x1IndexName + key: *x1IndexSpec + commandName: createIndexes + - commandSucceededEvent: + commandName: createIndexes + - commandStartedEvent: + command: + createIndexes: *collection0Name + indexes: + - name: *y1IndexName + key: *y1IndexSpec + commandName: createIndexes + - commandSucceededEvent: + commandName: createIndexes + - commandStartedEvent: + command: + listIndexes: *collection0Name + cursor: + batchSize: 2 + commandName: listIndexes + databaseName: *database0Name + - commandSucceededEvent: + commandName: listIndexes + - *getMoreStarted + - *getMoreSucceeded + - client: *client0 + eventType: cmap + events: + # Events for first createIndexes. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for second createIndexes. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for listIndexes and getMore. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: change streams pin to a connection + runOnRequirements: + - serverless: forbid # Serverless does not support change streams. + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + saveResultAsEntity: &changeStream0 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: + # Events for creating the change stream. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for closing the change stream. + - connectionCheckedInEvent: {} diff --git a/source/load-balancers/tests/event-monitoring.json b/source/load-balancers/tests/event-monitoring.json new file mode 100644 index 0000000000..938c70bf38 --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/event-monitoring.yml b/source/load-balancers/tests/event-monitoring.yml new file mode 100644 index 0000000000..52be457f68 --- /dev/null +++ b/source/load-balancers/tests/event-monitoring.yml @@ -0,0 +1,99 @@ +description: monitoring events include correct fields + +schemaVersion: '1.3' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - poolClearedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + 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/source/load-balancers/tests/lb-connection-establishment.json b/source/load-balancers/tests/lb-connection-establishment.json new file mode 100644 index 0000000000..0eaadf30c2 --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/lb-connection-establishment.yml b/source/load-balancers/tests/lb-connection-establishment.yml new file mode 100644 index 0000000000..46e5d781f3 --- /dev/null +++ b/source/load-balancers/tests/lb-connection-establishment.yml @@ -0,0 +1,36 @@ +description: connection establishment for load-balanced clusters + +schemaVersion: '1.3' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + uriOptions: + # Explicitly set loadBalanced to false to override the option from the global URI. + loadBalanced: false + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name 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: + # No events should be published because the server fails the connection handshake, so the "ping" command is never + # sent. + - client: *client0 + events: [] diff --git a/source/load-balancers/tests/non-lb-connection-establishment.json b/source/load-balancers/tests/non-lb-connection-establishment.json new file mode 100644 index 0000000000..6aaa7bdf98 --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/non-lb-connection-establishment.yml b/source/load-balancers/tests/non-lb-connection-establishment.yml new file mode 100644 index 0000000000..e805549ac0 --- /dev/null +++ b/source/load-balancers/tests/non-lb-connection-establishment.yml @@ -0,0 +1,56 @@ +description: connection establishment if loadBalanced is specified for non-load balanced clusters + +schemaVersion: '1.3' + +runOnRequirements: + # Don't run on replica sets because the URI used to configure the clients will contain multiple hosts and the + # replicaSet option, which will cause an error when constructing the lbTrueClient entity. + - topologies: [ single, sharded ] + +createEntities: + - client: + id: &lbTrueClient lbTrueClient + # Restrict to a single mongos to ensure there are not multiple hosts in the URI, which would conflict with + # loadBalanced=true. + useMultipleMongoses: false + uriOptions: + loadBalanced: true + - database: + id: &lbTrueDatabase lbTrueDatabase + client: *lbTrueClient + databaseName: &lbTrueDatabaseName lbTrueDb + - client: + id: &lbFalseClient lbFalseClient + uriOptions: + loadBalanced: false + - database: + id: &lbFalseDatabase lbFalseDatabase + client: *lbFalseClient + databaseName: &lbFalseDatabaseName lbFalseDb + +_yamlAnchors: + runCommandArguments: + - &pingArguments + arguments: + commandName: ping + command: { ping: 1 } + +tests: + # These tests assert that drivers behave correctly if loadBalanced=true/false for non-load balanced clusters. Existing + # spec tests should cover the case where loadBalanced is unset. + + # If the server is not configured to be behind a load balancer and the URI contains loadBalanced=true, the driver + # should error during the connection handshake because the server's hello response does not contain a serviceId field. + - description: operations against non-load balanced clusters fail if URI contains loadBalanced=true + operations: + - name: runCommand + object: *lbTrueDatabase + <<: *pingArguments + 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 + <<: *pingArguments diff --git a/source/load-balancers/tests/sdam-error-handling.json b/source/load-balancers/tests/sdam-error-handling.json new file mode 100644 index 0000000000..2107afe5b3 --- /dev/null +++ b/source/load-balancers/tests/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.4.7" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "errorCode": 11600, + "appName": "lbSDAMErrorTestClient" + } + } + } + }, + { + "name": "insertOne", + "object": "singleColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isError": 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/source/load-balancers/tests/sdam-error-handling.yml b/source/load-balancers/tests/sdam-error-handling.yml new file mode 100644 index 0000000000..c5a69339e3 --- /dev/null +++ b/source/load-balancers/tests/sdam-error-handling.yml @@ -0,0 +1,273 @@ +description: state change errors are correctly handled + +schemaVersion: '1.4' + +runOnRequirements: + - topologies: [ load-balanced ] + +_yamlAnchors: + observedEvents: &observedEvents + - connectionCreatedEvent + - connectionReadyEvent + - connectionCheckedOutEvent + - connectionCheckOutFailedEvent + - connectionCheckedInEvent + - connectionClosedEvent + - poolClearedEvent + +createEntities: + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - client: + id: &singleClient singleClient + useMultipleMongoses: false + uriOptions: + appname: &singleClientAppName lbSDAMErrorTestClient + retryWrites: false + observeEvents: *observedEvents + - database: + id: &singleDB singleDB + client: *singleClient + databaseName: &singleDBName singleDB + - collection: + id: &singleColl singleColl + database: *singleDB + collectionName: &singleCollName singleColl + - client: + id: &multiClient multiClient + useMultipleMongoses: true + uriOptions: + retryWrites: false + observeEvents: *observedEvents + - database: + id: &multiDB multiDB + client: *multiClient + databaseName: &multiDBName multiDB + - collection: + id: &multiColl multiColl + database: *multiDB + collectionName: &multiCollName multiColl + +initialData: + - collectionName: *singleCollName + databaseName: *singleDBName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - collectionName: *multiCollName + databaseName: *multiDBName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + +tests: + - description: only connections for a specific serviceId are closed when pools are cleared + runOnRequirements: + # This test assumes that two sequential connections receive different serviceIDs. + # Sequential connections to a serverless instance may receive the same serviceID. + - serverless: forbid + operations: + # Create two cursors to force two connections. + - name: createFindCursor + object: *multiColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor0 cursor0 + - name: createFindCursor + object: *multiColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor1 cursor1 + # Close both cursors to return the connections to the pool. + - name: close + object: *cursor0 + - name: close + object: *cursor1 + # Fail an operation with a state change error. + - name: failPoint + object: testRunner + arguments: + client: *multiClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [insert] + errorCode: &errorCode 11600 # InterruptedAtShutdown + - name: insertOne + object: *multiColl + arguments: + document: { x: 1 } + expectError: + errorCode: *errorCode + # Do another operation to ensure the relevant connection has been closed. + - name: insertOne + object: *multiColl + arguments: + document: { x: 1 } + expectEvents: + - client: *multiClient + eventType: cmap + events: + # Create cursors. + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Close cursors. + - connectionCheckedInEvent: {} + - connectionCheckedInEvent: {} + # Set failpoint. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # First insertOne. + - connectionCheckedOutEvent: {} + - poolClearedEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: + reason: stale + # Second insertOne. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + # This test uses singleClient to ensure that connection attempts are routed + # to the same mongos on which the failpoint is set. + - description: errors during the initial connection hello are ignored + runOnRequirements: + # Require SERVER-49336 for failCommand + appName on the initial handshake. + - minServerVersion: '4.4.7' + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [isMaster, hello] + errorCode: 11600 + appName: *singleClientAppName + - name: insertOne + object: *singleColl + arguments: + document: { x: 1 } + expectError: + isError: 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: *singleClientAppName + - 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 + # Force two connections to be checked out from the pool. + - name: createFindCursor + object: *singleColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor0 cursor0 + - name: createFindCursor + object: *singleColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor1 cursor1 + # Iterate cursor0 three times to force a network error. + - name: iterateUntilDocumentOrError + object: *cursor0 + - name: iterateUntilDocumentOrError + object: *cursor0 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectError: + isClientError: true + - name: close + object: *cursor0 + # Iterate cursor1 three times to force a network error. + - 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: + # Events for creating both cursors. + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for iterating and closing the first cursor. The failed + # getMore should cause a poolClearedEvent to be published. + - poolClearedEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} + # Events for iterating and closing the second cursor. The failed + # getMore should not clear the pool because the connection's + # generation number is stale. + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} diff --git a/source/load-balancers/tests/server-selection.json b/source/load-balancers/tests/server-selection.json new file mode 100644 index 0000000000..00c7e4c95b --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/server-selection.yml b/source/load-balancers/tests/server-selection.yml new file mode 100644 index 0000000000..5c5c9a9a12 --- /dev/null +++ b/source/load-balancers/tests/server-selection.yml @@ -0,0 +1,50 @@ +description: server selection for load-balanced clusters + +schemaVersion: '1.3' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + readPreference: + # Use secondaryPreferred to ensure that operations can succeed even if the shards are only comprised of one + # server. + mode: &readPrefMode secondaryPreferred + +initialData: + - collectionName: *collection0Name + 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: *collection0Name + filter: {} + $readPreference: + mode: *readPrefMode + commandName: find + databaseName: *database0Name diff --git a/source/load-balancers/tests/transactions.json b/source/load-balancers/tests/transactions.json new file mode 100644 index 0000000000..ca9c145217 --- /dev/null +++ b/source/load-balancers/tests/transactions.json @@ -0,0 +1,1665 @@ +{ + "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": {} + } + ] + } + ] + }, + { + "description": "pinned connection is released when session ended", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "x": 1 + }, + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "endSession", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/load-balancers/tests/transactions.yml b/source/load-balancers/tests/transactions.yml new file mode 100644 index 0000000000..4807ff21ef --- /dev/null +++ b/source/load-balancers/tests/transactions.yml @@ -0,0 +1,616 @@ +description: transactions are correctly pinned to connections for load-balanced clusters + +schemaVersion: '1.4' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + # Do not observe commandSucceededEvent or commandFailedEvent because we cannot guarantee success or failure of + # commands like commitTransaction and abortTransaction in a multi-mongos load-balanced setup. + - commandStartedEvent + - connectionReadyEvent + - connectionClosedEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + - session: + id: &session0 session0 + client: *client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + +_yamlAnchors: + documents: + - &insertDocument + _id: 4 + +tests: + - description: sessions are reused in LB mode + operations: + - &nonTransactionalInsert + name: insertOne + object: *collection0 + arguments: + document: { x: 1 } + - *nonTransactionalInsert + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + + - description: all operations go to the same mongos + operations: + - &startTransaction + name: startTransaction + object: *session0 + - &transactionalInsert + name: insertOne + object: *collection0 + arguments: + document: { x: 1 } + session: *session0 + - &assertConnectionPinned + name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 1 + - *transactionalInsert + - *transactionalInsert + - *transactionalInsert + - *transactionalInsert + - *transactionalInsert + - *assertConnectionPinned + - &commitTransaction + name: commitTransaction + object: *session0 + expectEvents: + - client: *client0 + events: + - &insertStarted + commandStartedEvent: + commandName: insert + - *insertStarted + - *insertStarted + - *insertStarted + - *insertStarted + - *insertStarted + - &commitStarted + commandStartedEvent: + commandName: commitTransaction + - client: *client0 + eventType: cmap + events: + # The connection is never checked back in. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + + - description: transaction can be committed multiple times + operations: + - *startTransaction + - *transactionalInsert + - *assertConnectionPinned + - *commitTransaction + - *assertConnectionPinned + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *assertConnectionPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + - *commitStarted + - *commitStarted + - *commitStarted + - 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: &nonTransientErrorCode 51 # ManualInterventionRequired + - *startTransaction + - name: insertOne + object: *collection0 + arguments: + document: { x: 1 } + session: *session0 + expectError: &nonTransientExpectedError + errorCode: *nonTransientErrorCode + errorLabelsOmit: [ TransientTransactionError ] + - *assertConnectionPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the fail point. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the transactional insert. + - 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: *nonTransientErrorCode + - *startTransaction + - *transactionalInsert + - name: commitTransaction + object: *session0 + expectError: *nonTransientExpectedError + - *assertConnectionPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the fail point. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the transactional insert and commit. + - connectionCheckedOutEvent: {} + + # Errors during abort are different than errors during commit and CRUD operations because the pinned connection is + # always released after abort. + - 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: &nonTransientErrorCode 51 # ManualInterventionRequired + - *startTransaction + - *transactionalInsert + - name: abortTransaction + object: *session0 + - &assertConnectionNotPinned + name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 0 + expectEvents: + - client: *client0 + events: + - *insertStarted + - &abortStarted + commandStartedEvent: + commandName: abortTransaction + - client: *client0 + eventType: cmap + events: + # Events for setting the fail point. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the transactional insert and abort. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: pinned connection is released after a transient non-network CRUD error + runOnRequirements: + - serverless: forbid # (CLOUDP-88216) Serverless does not append error labels to errors triggered by failpoints. + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: &transientErrorCode 24 # LockTimeout + - *startTransaction + - <<: *transactionalInsert + expectError: &transientExpectedServerError + errorCode: *transientErrorCode + errorLabelsContain: [ TransientTransactionError ] + - *assertConnectionNotPinned + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *abortStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for abortTransction. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: pinned connection is released after a transient network CRUD error + runOnRequirements: + - serverless: forbid # (CLOUDP-88216) Serverless does not append error labels to errors triggered by failpoints. + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - *startTransaction + - <<: *transactionalInsert + expectError: &transientExpectedNetworkError + isClientError: true + errorLabelsContain: [ TransientTransactionError ] + - *assertConnectionNotPinned + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *abortStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: + reason: error + # Events for abortTransaction + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: pinned connection is released after a transient non-network commit error + runOnRequirements: + - serverless: forbid # (CLOUDP-88216) Serverless does not append error labels to errors triggered by failpoints. + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ commitTransaction ] + errorCode: *transientErrorCode + - *startTransaction + - *transactionalInsert + - <<: *commitTransaction + expectError: *transientExpectedServerError + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + # Events for commitTransaction. + - 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 + - *startTransaction + - *transactionalInsert + - <<: *commitTransaction + # Ignore the result and error because the operation might fail if it targets a new mongos that isn't aware of + # the transaction or the server-side reaper thread closes the transaction first. We only want to assert that + # the operation is retried, which is done via monitoring expectations, so the exact result/error is not + # necessary. + ignoreResultAndError: true + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + # The commit will be automatically retried. + - *commitStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + # Events for the first commitTransaction. + - connectionCheckedInEvent: {} + - connectionClosedEvent: + reason: error + # Events for the commitTransaction retry. + - 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: *transientErrorCode + - *startTransaction + - *transactionalInsert + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *abortStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + # Events for abortTransaction. + - 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 + - *startTransaction + - *transactionalInsert + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *abortStarted + # The abort will be automatically retried. + - *abortStarted + - client: *client0 + eventType: cmap + events: + # Events for setting the failpoint. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + # Events for the insert. + - connectionCheckedOutEvent: {} + # Events for the first abortTransaction. + - connectionCheckedInEvent: {} + - connectionClosedEvent: + reason: error + # Events for the abortTransaction retry. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: pinned connection is released on successful abort + operations: + - *startTransaction + - *transactionalInsert + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *abortStarted + - client: *client0 + eventType: cmap + events: + # The insert will create and pin a connection. The abort will use it and then unpin. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: pinned connection is returned when a new transaction is started + operations: + - *startTransaction + - *transactionalInsert + - *commitTransaction + - *assertConnectionPinned + - *startTransaction + - *assertConnectionNotPinned # startTransaction will unpin the connection. + - *transactionalInsert + - *assertConnectionPinned # The first operation in the new transaction will pin the connection again. + - *commitTransaction + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + - *insertStarted + - *commitStarted + - client: *client0 + eventType: cmap + events: + # Events for the first insert and commit. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for startTransaction. + - connectionCheckedInEvent: {} + # Events for the second insert and commit. + - connectionCheckedOutEvent: {} + + - description: pinned connection is returned when a non-transaction operation uses the session + operations: + - *startTransaction + - *transactionalInsert + - *commitTransaction + - *assertConnectionPinned + - *transactionalInsert + # The insert is a non-transactional operation that uses the session, so it unpins the connection. + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - *commitStarted + - *insertStarted + - client: *client0 + eventType: cmap + events: + # Events for the first insert and commit. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for the second insert. + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: a connection can be shared by a transaction and a cursor + operations: + - *startTransaction + - *transactionalInsert + - *assertConnectionPinned + - name: createFindCursor + object: *collection0 + arguments: + filter: {} + batchSize: 2 + session: *session0 + saveResultAsEntity: &cursor0 cursor0 + - *assertConnectionPinned + - name: close + object: *cursor0 + - *assertConnectionPinned + # Abort the transaction to ensure that the connection is unpinned. + - name: abortTransaction + object: *session0 + - *assertConnectionNotPinned + expectEvents: + - client: *client0 + events: + - *insertStarted + - commandStartedEvent: + commandName: find + - commandStartedEvent: + commandName: killCursors + - *abortStarted + - client: *client0 + eventType: cmap + events: + # Events for the insert, find, and killCursors. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for abortTransaction. + - connectionCheckedInEvent: {} + + - description: pinned connection is released when session ended + operations: + - *startTransaction + - *transactionalInsert + - *commitTransaction + - &endSession + name: endSession + object: *session0 + expectEvents: + - client: *client0 + eventType: cmap + events: + # Events for the insert and commitTransaction. + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + # Events for endSession. + - connectionCheckedInEvent: {} \ No newline at end of file diff --git a/source/load-balancers/tests/wait-queue-timeouts.json b/source/load-balancers/tests/wait-queue-timeouts.json new file mode 100644 index 0000000000..3dc6e46cff --- /dev/null +++ b/source/load-balancers/tests/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/source/load-balancers/tests/wait-queue-timeouts.yml b/source/load-balancers/tests/wait-queue-timeouts.yml new file mode 100644 index 0000000000..9d8c935fea --- /dev/null +++ b/source/load-balancers/tests/wait-queue-timeouts.yml @@ -0,0 +1,82 @@ +description: wait queue timeout errors include details about checked out connections + +schemaVersion: '1.3' + +runOnRequirements: + - topologies: [ load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + uriOptions: + maxPoolSize: 1 + waitQueueTimeoutMS: 50 + observeEvents: + - connectionCheckedOutEvent + - connectionCheckOutFailedEvent + - session: + id: &session0 session0 + client: *client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + 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 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/source/logging/logging.md b/source/logging/logging.md new file mode 100644 index 0000000000..6f7c36c200 --- /dev/null +++ b/source/logging/logging.md @@ -0,0 +1,421 @@ +# Logging + +- Title: Logging +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This specification defines requirements for drivers' logging configuration and behavior. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**Structured logging** + +Structured logging refers to producing log messages in a structured format, i.e. a series of key-value pairs, which can +be converted to external formats such as JSON. + +**Unstructured logging** + +Unstructured logging refers to producing string log messages which embed all attached information within that string. + +### Implementation requirements + +Drivers SHOULD implement support for logging in a manner that is idiomatic for their language and ecosystem. + +#### Minimizing Required Code Changes + +If possible, drivers SHOULD support the following configuration requirements in a manner that does not require any +changes to an application's source code, and in the case of compiled languages, does not require recompilation. However, +if that is impossible to do without conflicting with idiomatic logging patterns for the language ecosystem, it is +acceptable for a driver to require minimal changes to enable logging, such as recompiling with a feature flag specified, +or passing a logging configuration to a `MongoClient` constructor. In that case, drivers SHOULD strive to minimize the +amount of code change needed. + +Drivers SHOULD take advantage of built-in support for logging configuration within logging frameworks or their language +ecosystem if available. If unavailable, drivers MUST support configuration via the listed fallback implementation +methods. + +In addition to supporting configuration without code changes/recompilation, drivers MAY additionally provide support for +enabling and configuring logging via their API in a manner that does require code changes, and MAY support additional +configuration options beyond those defined in this specification. + +For languages/ecosystems where libraries depend on only a logging interface (for example, the Java driver and +[SLF4J](https://www.slf4j.org/)) and application developers must choose a log handler, the requirements MAY be +considered satisfied if there is a log handler available in the ecosystem which users can select that satisfies the +requirements. Similarly, the requirements MAY be considered satisfied if the driver provides an integration with a +logging framework which satisfies the requirements. + +#### Per-Component Configuration of Log Levels + +Drivers MUST support enabling logging and specifying the minimum severity level for emitted messages on a per-component +level, for each component defined below in the [Components](#components) section. + +> **Fallback implementation method**: Support configuration via environment variables corresponding to each component, +> as defined in [Components](#components), as well as via the environment variable `MONGODB_LOG_ALL`. +> +> Each of these variables may be set to any of the values defined below in [Log Severity Levels](#log-severity-levels) +> to indicate the minimum severity level at which messages should be emitted for the corresponding component, or in the +> case of `MONGODB_LOG_ALL`, all components. Setting a value for `MONGODB_LOG_ALL` is equivalent to setting that value +> for all of the per-component variables. +> +> If `MONGODB_LOG_ALL` is specified in addition to one or more component variables, the component variable(s) MUST take +> precedence. +> +> The default is to not log anything. +> +> If a variable is set to an invalid value, it MUST be treated as if it were not specified at all, and the driver MAY +> attempt to warn the user about the misconfiguration via a log message or otherwise but MUST NOT throw an exception. + +#### Configurable Log Destination + +Drivers MUST support configuring where log messages should be output, including the options: + +- stdout + +- stderr + +- Output file (path MUST be configurable). For languages that are not relying on a logging interface or framework to + handle file support, the driver can choose to either support this directly (i.e. the driver allows the user to + specify a path and itself handles writing to that path), or to instead provide a straightforward, idiomatic way to + programmatically consume the messages and in turn write them to a file, e.g. via a Node.js + [stream](https://nodejs.org/api/stream.html), along with a documentation example of how to do this. + + > **Fallback implementation method**: If the environment variable `MONGODB_LOG_PATH` is provided: + > + > - If the value is "stdout" (case-insensitive), log to stdout. + > - If the value is "stderr" (case-insensitive), log to stderr. + > - Else, if direct logging to files is supported, log to a file at the specified path. If the file already exists, it + > MUST be appended to. + > + > If the variable is not provided or is set to an invalid value (which could be invalid for any reason, e.g. the path + > does not exist or is not writeable), the driver MUST log to stderr and the driver MAY attempt to warn the user about + > the misconfiguration via a log message or otherwise but MUST NOT throw an exception. + +#### Configurable Max Document Length + +Drivers MUST support configuring the maximum logged length for extended JSON documents in log messages. The unit here is +flexible and can be bytes, Unicode code points, code units, or graphemes, depending on what a driver is able to +accomplish with its language's string APIs. The default max length is 1000 of whichever unit is selected. If the chosen +unit is anything other than a Unicode code point, the driver MUST ensure that it gracefully handles cases where the +truncation length falls mid code point, by either rounding the length up or down to the closest code point boundary or +using the Unicode replacement character, to avoid producing invalid Unicode data. Drivers MUST implement truncation +naively by simply truncating the output at the required length; i.e. do not attempt to implement truncation such that +the output is still valid JSON. Truncated extended JSON MUST have a trailing ellipsis `...` appended to indicate to the +user that truncation occurred. The ellipsis MUST NOT count toward the max length. + +> **Fallback Implementation method**: Environment variable `MONGODB_LOG_MAX_DOCUMENT_LENGTH`. When unspecified, any +> extended JSON representation of a document which is longer than the default max length MUST be truncated to that +> length. When set to an integer value, any extended JSON document longer than that value MUST be truncated to that +> length. If a variable is set to an invalid value, it MUST be treated as if it were not specified at all, and the +> driver MAY attempt to warn the user about the misconfiguration via a log message or otherwise but MUST NOT throw an +> exception. + +#### Components + +Drivers MUST support configuring minimum log severity levels on a per-component level. The below components currently +exist and correspond to the listed specifications. This list is expected to grow over time as logging is added to more +specifications. + +Drivers SHOULD specify the component names in whatever the idiomatic way is for their language. For example, the Java +command component could be named `org.mongodb.driver.protocol.command`. + +Drivers MAY define additional language-specific components in addition to these for any driver-specific messages they +produce. + +| Component Name | Specification(s) | Environment Variable | +| --------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------ | +| command | [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) | `MONGODB_LOG_COMMAND` | +| topology | [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) | `MONGODB_LOG_TOPOLOGY` | +| serverSelection | [Server Selection](../server-selection/server-selection.md) | `MONGODB_LOG_SERVER_SELECTION` | +| connection | [Connection Monitoring and Pooling](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) | `MONGODB_LOG_CONNECTION` | + +#### Log Severity Levels + +Driver specifications defining log messages MUST use log levels from the following list, inspired by the Syslog Protocol +as described in [RFC 5424](https://www.rfc-editor.org/rfc/rfc5424): + +| Code | Level Name | Meaning | Environment Variable value (case-insensitive) | +| ---- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| - | Off | Nothing is logged. | `off` | +| 0 | Emergency | - | `emergency` | +| 1 | Alert | - | `alert` | +| 2 | Critical | - | `critical` | +| 3 | Error | Any error that we are unable to report to the user via driver API. | `error` | +| 4 | Warning | Indicates a situation where undesirable application behavior may occur. Example: The driver ignores an unrecognized option in a connection string. | `warn` | +| 5 | Notice | Indicates an event that is unusual but not problematic. Example: a change stream is automatically resumed. | `notice` | +| 6 | Informational | High-level information about normal driver behavior. Example: `MongoClient` creation or close. | `info` | +| 7 | Debug | Detailed information that may be helpful when debugging an application. Example: A command starting. | `debug` | +| 8 | Trace | Very fine-grained details related to logic flow. Example: entering and exiting function bodies. | `trace` | + +Note that the Emergency, Alert, and Critical levels have been intentionally left undefined. At the time of writing this +specification, we do not expect any driver specifications to need to log at these levels, but we have included them in +the list of permitted levels for consistency with Syslog. + +The levels above are defined in order from most to least severe. Not all logging frameworks will necessarily support all +of these levels. If an equivalent level is not available, drivers MUST emit messages for that level at the closest, less +severe level if one is available, or the closest more severe level otherwise. + +For example: + +- If an Informational level is not available and Debug is, messages defined as Informational in a specification MUST be + emitted at Debug level. +- If a Trace level is not available, Trace messages MUST be emitted at Debug level. + +#### Structured versus Unstructured Logging + +If structured logging is available in and idiomatic for the driver's language/ecosystem, the driver SHOULD produce +structured log messages. Note that some ecosystems with structured logging support may also have support available to +convert structured output to traditional unstructured messages for users who want it (for example, the +[log feature](https://docs.rs/tracing/latest/tracing/#emitting-log-records) in Rust's +[tracing](https://docs.rs/tracing/latest/tracing/) crate). If such support is available, drivers SHOULD utilize it to +support both types of logging. + +Note that drivers implementing unstructured logging MUST still support some internal way to intercept the data contained +in messages in a structured form, as this is required to implement the unified tests for logging conformance. See the +[unified test format specification](../unified-test-format/unified-test-format.md#expectedlogmessage) for details. + +#### Representing Documents in Log Messages + +BSON documents MUST be represented in relaxed extended JSON when they appear in log messages to improve readability. + +#### Representing Errors in Log Messages + +Drivers MAY represent errors in log messages in whatever format is idiomatic for their language and existing error +types. For example, if a driver's error classes have existing `toString()` implementations, those MAY be used. +Alternatively, if a driver emits structured log messages, a structured format containing error data could be used. Any +information which a driver reports via its error classes MUST be included in the log representations. Note that if the +driver includes full server responses in its errors these MUST be truncated in accordance with the max document length +option. + +#### Logging Failures + +Specifications MAY define log messages that correspond to failures which also are reported via exceptions in the API, +for example a "command failed" log message. Such messages MUST NOT use log levels more severe than `debug`. + +While it might seem natural that such messages would be logged at `error` level, not all failures that the driver +considers worthy of an error will be considered a true error by the application. For example, consider an application +that unconditionally creates a collection on startup, and ignores any `NamespaceExists` errors received in response: it +would be undesirable for those failures to show up in application logs at `error` level. + +Additionally, some applications will already have infrastructure in place to log any unhandled exceptions at `error` +level. If the driver were to log exception-related messages at `error` level, such applications would end up with +duplicate, `error`-level messages for these exceptions. On the other hand, some applications may not log exceptions at +all, or might not include all of the relevant information about an encountered exception in their custom log messages; +for these applications, there is value in a driver emitting such log messages. + +Given this, logging such messages at `debug` level strikes the best balance between making the diagnostic information +available, but not overloading users with overly severe and/or duplicative messages. Users who do not log exceptions +will have a way to see driver exceptions, by turning on `debug` logging, while users who do log exceptions can filter +for the true exceptions by only looking at more severe log levels. + +#### Omitting Null Values from Log Messages + +Some log messages will include fields that are only present under particular circumstances, for example on certain +server versions. When such a field is not present: + +- If the driver does structured logging, the field MUST be omitted from the message altogether, i.e. the field MUST not + be present with an explicit null value. +- If the driver does unstructured logging, the corresponding segment of the message string MUST be omitted altogether. + +#### Performance Considerations + +The computation required to generate certain log messages can be significant, e.g. if extended JSON serialization is +required. If possible, drivers SHOULD check whether a log message would actually be emitted and consumed based on the +users' configuration before doing such computation. For example, this can be checked in Rust via +[log::log_enabled](https://docs.rs/log/latest/log/macro.log_enabled.html). + +Drivers SHOULD optimize extended JSON generation to avoid generating JSON strings longer than will be emitted, such that +the complexity is O(N) where N = ``, rather than N = ``. + +#### Standard Naming in Structured Log Messages + +Driver specifications typically allow for language-appropriate naming variations, e.g. using snakecase or camelcase to +name a property. However, for log messages, drivers doing structured logging MUST use the exact names and casing +specified for the names of fields included in messages. This will be easier for our support team since the names will be +consistent across languages, and will simplify writing language-agnostic tooling to search through and parse structured +logs. + +#### Including Timestamps in Log Messages + +Drivers MAY add timestamps to their log messages if one will not be added automatically by the logging framework(s) they +use. + +#### Supporting Both Programmatic and Environment Variable Configuration + +If a driver supports configuration via both environment variables and programmatically via API, programmatic +configuration MUST take precedence over environment variables. Drivers supporting both forms of configuration MUST +document this behavior and MUST provide an example of how users can implement custom logic to allow an environment +variable to override a programmatic default, so that users who prefer the opposite behavior have a way to achieve it. + +## Test Plan + +Tests for logging behavior are defined in each corresponding specification. The +[unified test runner specification](../unified-test-format/unified-test-format.md) has support for specifying logging +expectations in tests. + +## Motivation for Change + +A common complaint from our support team is that they don't know how to easily get debugging information from drivers. +Some drivers provide debug logging, but others do not. For drivers that do provide it, the log messages produced and the +mechanisms for enabling debug logging are inconsistent. + +Although users can implement their own debug logging support via existing driver events (SDAM, APM, etc), this requires +code changes. It is often difficult to quickly implement and deploy such changes in production at the time they are +needed, and to remove the changes afterward. Additionally, there are useful scenarios to log that do not correspond to +existing events. Standardizing on debug log messages that drivers produce and how to enable/configure logging will +provide TSEs, CEs, and MongoDB users an easier way to get debugging information out of our drivers, facilitate support +of drivers for our internal teams, and improve our documentation around troubleshooting. + +## Design Rationale + +### Truncation of large documents + +1. Why have an option? + + We considered a number of approaches for dealing with documents of potentially very large size in log messages, e.g. + command documents, including 1) always logging the full document, 2) only logging documents with the potential to + be large when the user opts in, and 3) truncating large documents by default, but allowing the user to adjust the + maximum length logged. We chose the third option as we felt it struck the best balance between concerns around + readability and usability of log messages. In the case where data is sufficiently small, the default behavior will + show the user the full data. In the case where data is large, the user will receive a readable message with + truncated data, but have the option to see more or all of the data. + +2. Why are the units for max document length flexible? + + String APIs vary across languages, and not all drivers will be able to easily and efficiently truncate strings in the + same exact manner. The important thing is that the option exists and that its default value is reasonable, and for + all possible unit choices (byte, code point, code unit, or grapheme) we felt 1000 was a reasonable default. See + [here](https://exploringjs.com/impatient-js/ch_unicode.html) for a helpful primer on related Unicode concepts. + +3. Why do we implement naive truncation rather than truncating the JSON so it is still valid? + + Designing and implementing a truncation algorithm for JSON that outputs valid JSON, but fits in as much of the + original JSON as possible, would be non-trivial. The server team wrote an entire separate truncation design + document when they implemented this for their log messages. This is more of a necessity for the server where the + entire log message is JSON, but we don't know if parsing the documents included in log messages is something that + users will actually need to do. Furthermore, any users who want parseable documents have an escape hatch to do so: + they can set the max document length to a very large value. If we hear of use cases in the future for parsing the + documents in log messages, we could make an additive change to this specification to permit a smarter truncation + algorithm. + +### Structured versus Unstructured Logging + +The MongoDB server produces structured logs as of 4.4, so it seems natural that MongoDB drivers might too. However, +structured logging is not idiomatic or common in some language ecosystems, so we have chosen not to require it. + +### Omitting Null Values from Log Messages + +We considered alternatives such as allowing, or requiring, drivers to explicitly include null values in log messages. +While this might make it easier to identify cases where a value is unexpectedly null, we decided against it because +there are a number of values that will often be null, or even always be null for certain applications (e.g. `serviceId` +when not connected to a load-balanced topology) and their inclusion may confuse users and lead them to think the null +value is meaningful. Additionally, always including null values would increase the size of log messages. The omission of +null values is left to the drivers' discretion for any driver-specific logs not covered by common specification +components. + +### Invalid Values of Environment Variables + +For drivers supporting configuration via environment variables, the spec requires that if an environment variable is set +to an invalid value the driver behaves as if the value were not specified at all, and optionally warns the user but does +not throw an error. We considered the following alternatives: + +1. Drivers could be required to throw an exception if a value is invalid: This was rejected because of concerns around + the implications for environments/applications where multiple versions of the driver or multiple drivers may be + present and where the validation logic may not match, meaning a value considered valid for one driver/version might + not be by another. Additionally, there is no obvious place to throw an exception from about invalid environment + variables; `MongoClient` constructors would be one possibility, but not all languages will support per-client + configuration so throwing there regarding an environment variable might be surprising to users. + + Note that these same concerns do not apply to logging options that are specified via driver API: there is no risk of + such options propagating to other drivers/driver versions present, and drivers can report exceptions at the point + the options are specified, either globally or per-client. Therefore, drivers MUST validate programmatic logging + options in a manner consistent with how they validate all other programmatic options, and if possible SHOULD prefer + to throw exceptions for invalid configuration. + +2. Drivers could be required to log a warning if a value is invalid: While drivers MAY do this, requiring it was + rejected because depending on the language/framework log messages may not be a viable way to communicate a warning: + if a language's default behavior is to log nothing, or only log messages at a more severe level than `warn`, the + user will not actually receive the message unless it is logged at a level and component they have successfully + enabled. + +### Programmatic Configuration Taking Precedence + +We chose to have programmatic configuration win out over environment variables because: + +1. This allows applications built atop drivers (e.g. mongosh) to fully control the driver's logging behavior by setting + options for it programmatically. +2. This is consistent with how many drivers treat options specified both in a connection string and programmatically: + programmatic options win out. +3. It is straightforward for users to override this behavior (by writing logic to read in environment variables and + override programmatic defaults), but if we went with the opposite default, it would be more complicated for users + to override: not all languages will necessarily have an easy way to override/unset an environment variable from + within application code. + +## Backwards Compatibility + +This specification takes the stance that the contents of log messages (both structured and unstructured) are *not* +covered by semantic versioning, but that logging components *are*, since changing the name of a component or removing a +component altogether has the potential to break user logging configuration and cause users to silently miss log +messages. + +As a result, any drivers that already support logging should be free to update the messages they log to match those +defined in various specifications. However, drivers should take care to avoid removing or renaming existing logging +components except in major version releases. + +Since this specification defines no particular API, drivers are free to keep any existing programmatic APIs they have +for configuring logging. If such APIs are incompatible with the logging specification requirements (for example, the +driver defines its own set of log levels in a public type, which do not match the spec-defined levels), changes to match +the specification should be staged in via semantic versioning. + +## Reference Implementation + +Links to be added once Rust and C# implementations have been merged. + +## Security Implication + +Drivers must take care to avoid exposing sensitive information (e.g. authentication credentials) in log messages. It +will be up to each individual specification that defines log messages to define which information should be redacted and +add tests confirming its redaction. + +## Future work + +### Additional Log Components + +Additional log components may be added as logging is added to more specifications. + +### Preventing Recursion When Using MongoDB as a Log Sink + +If a user chooses to store log messages produced by a driver in MongoDB, it may be possible for them to end up recursing +infinitely if each write to store a log message generates additional log messages. This has historically not been an +issue in drivers that already produce log messages, or with the command monitoring API, but if users start to run into +this issue, we could try to address it at the specification level by e.g. requiring drivers to support disabling logging +on individual clients or for particular namespaces. + +## Changelog + +- 2024-03-06: Migrated from reStructuredText to Markdown. + +- 2022-11-16: Add policy on severity level for log messages corresponding to driver exceptions. + +- 2022-11-11: Clarify that guidance around null values only strictly applies to spec-defined log messages. + +- 2022-10-26: Allow drivers to add timestamps to log messages. + +- 2022-11-10: Clarify driver-specific null omission. + +- 2022-12-29: Fix typo in trace log level example + +- 2023-01-04: Elaborate on treatment of invalid values of environment variables. Permit drivers to omit direct support + for logging to file so long as they provide a straightforward way for users to consume the log messages + programmatically and write to a file themselves. Require that programmatic configuration take precedence over + environment variables. diff --git a/source/logging/logging.rst b/source/logging/logging.rst new file mode 100644 index 0000000000..d269595988 --- /dev/null +++ b/source/logging/logging.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `logging.md `_. diff --git a/source/max-staleness/max-staleness-tests.md b/source/max-staleness/max-staleness-tests.md new file mode 100644 index 0000000000..1e2627634b --- /dev/null +++ b/source/max-staleness/max-staleness-tests.md @@ -0,0 +1,144 @@ +# Max Staleness Tests + +These prose tests supplement the machine-readable YAML tests. + +## Connection string + +Tests of drivers' connection string parsing and validation. + +### Basic + +maxStalenessSeconds is set to 120 from this connection string: + +```text +mongodb://host/?readPreference=secondary&maxStalenessSeconds=120 +``` + +### No maxStalenessSeconds with mode "primary" + +These connection strings are invalid because the mode is "primary", either by default or explicitly: + +```text +mongodb://host/?maxStalenessSeconds=120 +mongodb://host/?readPreference=primary&maxStalenessSeconds=120 +``` + +### Specifying "no max staleness" with "-1" + +A MongoClient with this connection string has no max staleness, the same as if "maxStalenessSeconds" were omitted from +the connection string: + +```text +mongodb://host/?readPreference=secondary&maxStalenessSeconds=-1 +``` + +This connection string is valid and does not raise an error: + +```text +mongodb://host/?readPreference=primary&maxStalenessSeconds=-1> +``` + +### No validation + +A MongoClient can be initialized with this connection string; the small maxStalenessSeconds value causes no error +initially: + +```text +mongodb://host/?readPreference=secondary&maxStalenessSeconds=1 +``` + +## heartbeatFrequencyMS is configurable + +Server Discovery and Monitoring was updated to accommodate the Max Staleness Spec: heartbeatFrequencyMS MUST now be +configurable in all drivers, not just single-threaded drivers. + +Test that a user can set heartbeatFrequencyMS in the connection string: + +```text +mongodb://host/?heartbeatFrequencyMS=500 +``` + +Test that a client initialized with this string checks a standalone server roughly twice per second. + +## Driver Tests + +### Parse lastWriteDate + +Configure a client with heartbeatFrequencyMS of 500 milliseconds. + +Connect to a MongoDB replica set member with maxWireVersion at least 5. (Only replica set members with maxWireVersion 5 +have a lastWriteDate in the legacy hello reply.) + +Insert a document and wait one second. + +Get the client's ServerDescription for the server. It must have a non-zero lastWriteDate. Wait another second.[^1] + +Insert a document and wait one second. + +Get the client's ServerDescription for the server. The ServerDescription's lastWriteDate must be greater than before, +but less than 10 seconds greater. + +(This test is as lenient as possible, while still testing that the driver parses lastWriteDate and converts it to an +appropriate unit.) + +### Absent lastWriteDate + +Connect to a MongoDB server with maxWireVersion less than 5. Check that the driver's ServerDescription for this server +has no lastWriteDate, represented with null, zero, or however the driver represents a non-value. + +### Direct connection to mongos + +Initialize a MongoClient with this connection string: + +```text +mongodb://mongos/?readPreference=secondary&maxStalenessSeconds=120 +``` + +Where "mongos" is the hostname and port of a mongos server running wire protocol version 5+. Execute a "findOne" +operation. The MongoClient must include following read preference element with its "find" command: + +```javascript +$readPreference: { + mode: "secondary", + maxStalenessSeconds: 120 +} +``` + +The "maxStalenessSeconds" element must be a BSON int32 or int64 type. + +Do the same test with this connection string: + +```text +mongodb://mongos/?readPreference=secondary +``` + +The read preference element sent to mongos must omit "maxStalenessSeconds". + +## Mongos + +These tests MUST be added to the server code repository, validating mongos's maxStalenessSeconds implementation. + +### maxStalenessSeconds and mode "primary" + +mongos MUST reject a read with: + +```javascript +$readPreference: {mode: "primary", maxStalenessSeconds: 120} +``` + +### Validation + +mongos MUST reject a read with `maxStalenessSeconds` that is not an int32 or int64. + +### maxStalenessSeconds too small + +The minimum value of maxStalenessSeconds is 90. mongos MUST reject a read with: + +```javascript +$readPreference: {mode: "secondary", maxStalenessSeconds: 89} +``` + +The error code MUST be 160. + +[^1]: This ensures the next insert occurs at least one second after server selection (another write may have occurred from + the noop writer just before server selection if mongod was configured with periodicNoopIntervalSecs=1). diff --git a/source/max-staleness/max-staleness-tests.rst b/source/max-staleness/max-staleness-tests.rst index 06f9dc2c79..a264ee1b0a 100644 --- a/source/max-staleness/max-staleness-tests.rst +++ b/source/max-staleness/max-staleness-tests.rst @@ -1,148 +1,4 @@ -=================== -Max Staleness Tests -=================== -These prose tests supplement the machine-readable YAML tests. - -.. contents:: - -Connection string -================= - -Tests of drivers' connection string parsing and validation. - -Basic ------ - -maxStalenessSeconds is set to 120 from this connection string:: - - mongodb://host/?readPreference=secondary&maxStalenessSeconds=120 - -No maxStalenessSeconds with mode "primary" ------------------------------------------- - -These connection strings are invalid because the mode is "primary", either -by default or explicitly:: - - mongodb://host/?maxStalenessSeconds=120 - mongodb://host/?readPreference=primary&maxStalenessSeconds=120 - -Specifying "no max staleness" with "-1" ---------------------------------------- - -A MongoClient with this connection string has no max staleness, the same as -if "maxStalenessSeconds" were omitted from the connection string:: - - mongodb://host/?readPreference=secondary&maxStalenessSeconds=-1 - -This connection string is valid and does not raise an error: - - mongodb://host/?readPreference=primary&maxStalenessSeconds=-1 - -No validation -------------- - -A MongoClient can be initialized with this connection string; -the small maxStalenessSeconds value causes no error initially:: - - mongodb://host/?readPreference=secondary&maxStalenessSeconds=1 - -heartbeatFrequencyMS is configurable -==================================== - -Server Discovery and Monitoring was updated to accommodate the Max Staleness Spec: -heartbeatFrequencyMS MUST now be configurable in all drivers, not just single-threaded drivers. - -Test that a user can set heartbeatFrequencyMS in the connection string:: - - mongodb://host/?heartbeatFrequencyMS=500 - -Test that a client initialized with this string -checks a standalone server roughly twice per second. - -Driver Tests -============ - -Parse lastWriteDate -------------------- - -Configure a client with heartbeatFrequencyMS of 500 milliseconds. - -Connect to a MongoDB replica set member with maxWireVersion at least 5. -(Only replica set members with maxWireVersion 5 have a lastWriteDate in -the ismaster reply.) - -Insert a document and wait one second. - -Get the client's ServerDescription for the server. -It must have a non-zero lastWriteDate. - -Insert a document and wait one second. - -Get the client's ServerDescription for the server. -The ServerDescription's lastWriteDate must be greater than before, -but less than 10 seconds greater. - -(This test is as lenient as possible, -while still testing that the driver parses lastWriteDate -and converts it to an appropriate unit.) - -Absent lastWriteDate --------------------- - -Connect to a MongoDB server with maxWireVersion less than 5. -Check that the driver's ServerDescription for this server has no lastWriteDate, -represented with null, zero, or however the driver represents a non-value. - -Direct connection to mongos ---------------------------- - -Initialize a MongoClient with this connection string:: - - mongodb://mongos/?readPreference=secondary&maxStalenessSeconds=120 - -Where "mongos" is the hostname and port -of a mongos server running wire protocol version 5+. -Execute a "findOne" operation. -The MongoClient must include following read preference element with its -"find" command:: - - $readPreference: { - mode: "secondary", - maxStalenessSeconds: 120 - } - -The "maxStalenessSeconds" element must be a BSON int32 or int64 type. - -Do the same test with this connection string:: - - mongodb://mongos/?readPreference=secondary - -The read preference element sent to mongos must omit "maxStalenessSeconds". - -Mongos -====== - -These tests MUST be added to the server code repository, -validating mongos's maxStalenessSeconds implementation. - -maxStalenessSeconds and mode "primary" --------------------------------------- - -mongos MUST reject a read with:: - - $readPreference: {mode: "primary", maxStalenessSeconds: 120} - -Validation ----------- - -mongos MUST reject a read with ``maxStalenessSeconds`` that is not an int32 or int64. - -maxStalenessSeconds too small ------------------------------ - -The minimum value of maxStalenessSeconds is 90. mongos MUST reject a read with:: - - $readPreference: {mode: "secondary", maxStalenessSeconds: 89} - -The error code MUST be 160. +.. note:: + This specification has been converted to Markdown and renamed to + `max-staleness-tests.md `_. diff --git a/source/max-staleness/max-staleness.md b/source/max-staleness/max-staleness.md new file mode 100644 index 0000000000..0f693db54e --- /dev/null +++ b/source/max-staleness/max-staleness.md @@ -0,0 +1,492 @@ +# Max Staleness + +- Status: Accepted +- Minimum Server Version: 3.4 + +## Abstract + +Read preference gains a new option, "maxStalenessSeconds". + +A client (driver or mongos) MUST estimate the staleness of each secondary, based on lastWriteDate values provided in +server hello responses, and select only those secondaries whose staleness is less than or equal to maxStalenessSeconds. + +Most of the implementation of the maxStalenessSeconds option is specified in the Server Discovery And Monitoring Spec +and the Server Selection Spec. This document supplements those specs by collecting information specifically about +maxStalenessSeconds. + +## Meta + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Motivation for Change + +Users have often asked for ways to avoid reading from stale secondaries. An application with a geographically +distributed replica set may want to prefer nearby members to minimize latency, while at the same time avoiding extremely +laggy secondaries to mitigate the risk of very stale reads. + +### Goals + +- Provide an approximate means of limiting the staleness of secondary reads. +- Provide a client-side knob to adjust the tradeoff between network-local reads and data recency. +- Be robust in the face of clock skew between the client and servers, and skew between the primary and secondaries. +- Avoid "inadvertent primary read preference": prevent a maxStalenessSeconds setting so small it forces all reads to the + primary regardless of actual replication lag. +- Specify how mongos routers and shards track the opTimes of Config Servers as Replica Sets ("CSRS"). + +### Non-Goals + +- Provide a global server-side configuration of max acceptable staleness (see [rejected ideas](#rejected-ideas)). +- Support small values for max staleness. +- Make a consistency guarantee resembling readConcern "afterOpTime". +- Specify how maxStalenessSeconds interacts with readConcern "afterOpTime" in drivers (distinct from the goal for + routers and shards). +- Compensate for the duration of server checks in staleness estimations. + +## Specification + +### API + +"maxStalenessSeconds" is a new read preference option, with a positive integer value. It MUST be configurable similar to +other read preference options like "readPreference" and "tag_sets". Clients MUST also recognize it in the connection +string: + +```text +mongodb://host/?readPreference=secondary&maxStalenessSeconds=120 +``` + +Clients MUST consider "maxStalenessSeconds=-1" in the connection string to mean "no maximum staleness". + +A connection string combining a positive maxStalenessSeconds with read preference mode "primary" MUST be considered +invalid; this includes connection strings with no explicit read preference mode. + +By default there is no maximum staleness. + +A driver connected to a replica set requires that maxStalenessSeconds be absent, or be at least +smallestMaxStalenessSeconds (90 seconds) and at least heartbeatFrequencyMS + idleWritePeriodMS. The exact mechanism for +enforcement is defined in the Server Selection Spec. + +Besides configuring maxStalenessSeconds in the connection string, the API for configuring it in code is not specified; +drivers are free to use None, null, -1, or other representations of "no value" to represent "no max staleness". + +### Replica Sets + +Replica set primaries and secondaries implement the following features to support maxStalenessSeconds. + +#### idleWritePeriodMS + +An idle primary writes a no-op to the oplog every 10 seconds to refresh secondaries' lastWriteDate values (see +SERVER-23892 and [primary must write periodic no-ops](#primary-must-write-periodic-no-ops)). This spec refers to this +period as `idleWritePeriodMS` with constant value 10,000. + +#### lastWrite + +A primary's or secondary's hello response contains a "lastWrite" subdocument with these fields (SERVER-8858): + +- lastWriteDate: a BSON UTC datetime, the wall-clock time of the **primary** when it most recently recorded a write to + the oplog. +- opTime: an opaque value representing the position in the oplog of the most recently seen write. Needed for sharding, + not used for the maxStalenessSeconds read preference option. + +### Wire Version + +The maxWireVersion MUST be incremented to 5 to indicate that the server includes maxStalenessSeconds features +(SERVER-23893). + +### Client + +A client (driver or mongos) MUST estimate the staleness of each secondary, based on lastWriteDate values provided in +server hello responses, and select for reads only those secondaries whose estimated staleness is less than or equal to +maxStalenessSeconds. + +If any server's maxWireVersion is less than 5 and maxStalenessSeconds is a positive number, every attempt at server +selection throws an error. + +When there is a known primary, a secondary S's staleness is estimated with this formula: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS +``` + +Where "P" and "S" are the primary's and secondary's ServerDescriptions. All datetimes are in milliseconds. The staleness +estimate could be temporarily negative. + +When there is no known primary, a secondary S's staleness is estimated with this formula: + +```javascript +SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS +``` + +Where "SMax" is the secondary with the greatest lastWriteDate. + +#### Explanation of Staleness Estimate With Primary + +1. When the client checks the primary, it gets the delta between the primary's lastWriteDate and the client clock. Call + this "Client_to_Primary". +2. When the client checks a secondary, it gets the delta between the secondary's lastWriteDate and the client clock. + Call this "Client_to_Secondary". +3. The difference of these two is an estimate of the delta between the primary's and secondary's lastWriteDate. + +Thus: + +```javascript +staleness = Client_to_Secondary - Client_to_Primary += (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) +``` + +Finally, add heartbeatFrequencyMS: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS +``` + +This adjusts for the pessimistic assumption that S stops replicating right after S.lastUpdateTime, so it will be +heartbeatFrequencyMS *more* stale by the time it is checked again. This means S must be fresh enough at S.lastUpdateTime +to be eligible for reads from now until the next check, even if it stops replicating. + +See the Server Discovery and Monitoring Spec and Server Selection Spec for details of client implementation. + +### Routers and shards + +Background: Shard servers and mongos servers in a sharded cluster with CSRS use readConcern "afterOptime" for +consistency guarantees when querying the shard config. + +Besides tracking lastWriteDate, routers and shards additionally track the opTime of CSRS members if they have +maxWireVersion 5 or greater. (See Server Discovery and Monitoring Spec for details.) + +When a router or shard selects a CSRS member to read from with readConcern like: + +```yaml +readConcern: { afterOpTime: OPTIME } +``` + +... then it follows this selection logic: + +1. Make a list of known CSRS data members. +2. Filter out those whose last known opTime is older than OPTIME. +3. If no servers remain, select the primary. +4. Otherwise, select randomly one of the CSRS members whose roundTripTime is within localThresholdMS of the member with + the fastest roundTripTime. + +Step 4 is the standard localThresholdMS logic from the Server Selection Spec. + +This algorithm helps routers and shards select a secondary that is likely to satisfy readConcern "afterOpTime" without +blocking. + +This feature is only for routers and shards, not drivers. See [Future Work](#future-work). + +## Reference Implementation + +The C Driver (CDRIVER-1363) and Perl Driver (PERL-626). + +## Estimating Staleness: Example With a Primary and Continuous Writes + +Consider a primary P and a secondary S, and a client with heartbeatFrequencyMS set to 10 seconds. Say that the primary's +clock is 50 seconds skewed ahead of the client's. + +The client checks P and S at time 60 (meaning 60 seconds past midnight) by the client's clock. The primary reports its +lastWriteDate is 10. + +Then, S reports its lastWriteDate is 0. The client estimates S's staleness as: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS += (60 - 0) - (60 - 10) + 10 += 20 seconds +``` + +(Values converted from milliseconds to seconds for the sake of discussion.) + +Note that the secondary appears only 10 seconds stale at this moment, but the client adds heartbeatFrequencyMS, +pessimistically assuming that the secondary will not replicate at all between now and the next check. If the current +staleness plus heartbeatFrequencyMS is still less than maxStalenessSeconds, then we can safely read from the secondary +from now until the next check. + +The client re-checks P and S 10 seconds later, at time 70 by the client's clock. S responds first with a lastWriteDate +of 5: it has fallen 5 seconds further behind. The client updates S's lastWriteDate and lastUpdateTime. The client now +estimates S's staleness as: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS += (70 - 5) - (60 - 10) + 10 += 25 seconds +``` + +Say that P's response arrives 10 seconds later, at client time 80, and reports its lastWriteDate is 30. S's staleness is +still 25 seconds: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS += (70 - 5) - (80 - 30) + 10 += 25 seconds +``` + +The same story as a table: + +| Client clock | Primary clock | Event | S.lastUpdateTime | S.lastWriteDate | P.lastUpdateTime | P.lastWriteDate | S staleness | +| ------------ | ------------- | --------------- | ---------------- | --------------- | ---------------- | --------------- | ----------- | +| 60 | 10 | P and S respond | 60 | 0 | 60 | 10 | 20 seconds | +| 70 | 20 | S responds | 70 | 5 | 60 | 10 | 25 seconds | +| 80 | 30 | P responds | 70 | 5 | 80 | 30 | 25 seconds | + +## Estimating Staleness: Example With No Primary + +Consider a replica set with secondaries S1 and S2, and no primary. S2 lags 15 seconds *farther* behind S1 and has not +yet caught up. The client has heartbeatFrequencyMS set to 10 seconds. + +When the client checks the two secondaries, S1's lastWriteDate is 20 and S2's lastWriteDate is 5. + +Because S1 is the secondary with the maximum lastWriteDate, "SMax", its staleness estimate equals heartbeatFrequencyMS: + +> SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS = 20 - 20 + 10 = 10 + +(Since max staleness must be at least heartbeatFrequencyMS + idleWritePeriodMS, S1 is eligible for reads no matter +what.) + +S2's staleness estimate is: + +```javascript +SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS += 20 - 5 + 10 += 25 +``` + +## Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set + +Consider a primary P and a secondary S, and a client with heartbeatFrequencyMS set to 500 ms. There is no clock skew. +(Previous examples show that skew has no effect.) + +The primary has been idle for 10 seconds and writes a no-op to the oplog at time 50 (meaning 50 seconds past midnight), +and again at time 60. + +Before the secondary can replicate the no-op at time 60, the client checks both servers. The primary reports its +lastWriteDate is 60, the secondary reports 50. + +The client estimates S's staleness as: + +```javascript +(S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS += (60 - 50) - (60 - 60) + 0.5 += 10.5 +``` + +The same story as a table: + +| Clock | Event | S.lastUpdateTime | S.lastWriteDate | P.lastUpdateTime | P.lastWriteDate | S staleness | +| ----- | --------------------- | ---------------- | --------------- | ---------------- | --------------- | ----------- | +| 50 | Idle write | 50 | | 50 | | | +| 60 | Idle write begins | 60 | | 50 | | | +| 60 | Client checks P and S | 60 | 60 | 50 | 60 | 10.5 | +| 60 | Idle write completes | 60 | | 60 | | | + +In this scenario the actual secondary lag is between 0 and 10 seconds. But the staleness estimate can be as large as: + +```javascript +staleness = idleWritePeriodMS + heartbeatFrequencyMS +``` + +To ensure the secondary is always eligible for reads in an idle replica set, we require: + +```javascript +maxStalenessSeconds * 1000 >= heartbeatFrequencyMS + idleWritePeriodMS +``` + +## Supplemental + +Python scripts in this document's source directory: + +- `test_max_staleness_spo.py`: Uses `scipy.optimize` to determine worst-case accuracy of the staleness estimate in an + idle replica set. +- `test_staleness_estimate.py`: Tests whether a client would correctly select a secondary from an idle replica set, + given a random distribution of values for maxStalenessSeconds, heartbeatFrequencyMS, lastWriteDate, and + lastUpdateTime. + +## Test Plan + +See `max-staleness-tests.md`, and the YAML and JSON tests in the tests directory. + +## Design Rationale + +### Specify max staleness in seconds + +Other driver options that are timespans are in milliseconds, for example serverSelectionTimeoutMS. The max staleness +option is specified in seconds, however, to make it obvious to users that clients can only enforce large, imprecise max +staleness values. + +### maxStalenessSeconds is part of Read Preferences + +maxStalenessSeconds MAY be configurable at the client, database, and collection level, and per operation, the same as +other read preference fields are, because users expressed that their tolerance for stale reads varies per operation. + +### Primary must write periodic no-ops + +Consider a scenario in which the primary does *not*: + +1. There are no writes for an hour. +2. A client performs a heavy read-only workload with read preference mode "nearest" and maxStalenessSeconds of 90 + seconds. +3. The primary receives a write. +4. In the brief time before any secondary replicates the write, the client re-checks all servers. +5. Since the primary's lastWriteDate is an hour ahead of all secondaries', the client only queries the primary. +6. After heartbeatFrequencyMS, the client re-checks all servers and finds that the secondaries aren't lagging after all, + and resumes querying them. + +This apparent "replication lag spike" is just a measurement error, but it causes exactly the behavior the user wanted to +avoid: a small replication lag makes the client route all queries from the secondaries to the primary. + +Therefore an idle primary must execute a no-op every 10 seconds (idleWritePeriodMS) to keep secondaries' lastWriteDate +values close to the primary's clock. The no-op also keeps opTimes close to the primary's, which helps mongos choose an +up-to-date secondary to read from in a CSRS. + +Monitoring software like MongoDB Cloud Manager that charts replication lag will also benefit when spurious lag spikes +are solved. + +See +[Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set](#estimating-staleness-example-of-worst-case-accuracy-with-idle-replica-set). +and [SERVER-23892](https://jira.mongodb.org/browse/SERVER-23892). + +### Smallest allowed value for maxStalenessSeconds + +If maxStalenessSeconds is a positive number, it must be at least smallestMaxStalenessSeconds (90 seconds) and at least +heartbeatFrequencyMS + idleWritePeriodMS. The exact mechanism for enforcement is defined in the Server Selection Spec. + +The justification for heartbeatFrequencyMS + idleWritePeriodMS is technical: If maxStalenessSeconds is set to exactly +heartbeatFrequencyMS (converted to seconds), then so long as a secondary lags even a millisecond it is ineligible. +Despite the user's read preference mode, the client will always read from the primary. + +This is an example of "inadvertent primary read preference": a maxStalenessSeconds setting so small it forces all reads +to the primary regardless of actual replication lag. We want to prohibit this effect (see [goals](#goals)). + +We also want to ensure that a secondary in an idle replica set is always considered eligible for reads with +maxStalenessSeconds. See +[Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set](#estimating-staleness-example-of-worst-case-accuracy-with-idle-replica-set). + +Requiring maxStalenessSeconds to be at least 90 seconds is a design choice. If the only requirement were that +maxStalenessSeconds be at least heartbeatFrequencyMS + idleWritePeriodMS, then the smallest value would be 20 seconds +for multi-threaded drivers (10 second idleWritePeriodMS plus multi-threaded drivers' default 10 second +heartbeatFrequencyMS), 70 seconds for single-threaded drivers (whose default heartbeatFrequencyMS is 60 seconds), and 40 +seconds for mongos (whose replica set monitor checks servers every 30 seconds). + +The smallest configurable value for heartbeatFrequencyMS is 0.5 seconds, so maxStalenessSeconds could be as small as +10.5 when using a driver connected to a replica set, but mongos provides no such flexibility. + +Therefore, this spec *also* requires that maxStalenessSeconds is at least 90: + +- To provide a minimum for all languages and topologies that is easy to document and explain +- To avoid application breakage when moving from replica set to sharded cluster, or when using the same URI with + different drivers +- To emphasize that maxStalenessSeconds is a low-precision heuristic +- To avoid the arbitrary-seeming minimum of 70 seconds imposed by single-threaded drivers + +### All servers must have wire version 5 to support maxStalenessSeconds + +Clients with minWireVersion < 5 MUST throw an error if maxStalenessSeconds is set, and any available server in the +topology has maxWireVersion less than 5. + +An available server is defined in the [Server Selection](../server-selection/server-selection.md#terms) specification. + +Servers began reporting lastWriteDate in wire protocol version 5, and clients require some or all servers' lastWriteDate +in order to estimate any servers' staleness. The exact requirements of the formula vary according to TopologyType, so +this spec makes a simple ruling: if any server is running an outdated version, maxStalenessSeconds cannot be supported. + +### Rejected ideas + +#### Add all secondaries' opTimes to primary's hello response + +Not needed; each secondary's self-report of its opTime is just as good as the primary's. + +#### Use opTimes from command responses besides hello + +An idea was to add opTime to command responses that don't already include it (e.g., "find"), and use these opTimes to +update ServerDescriptions more frequently than the periodic hello calls. + +But while a server is not being used (e.g., while it is too stale, or while it does not match some other part of the +Read Preference), only its periodic hello responses can update its opTime. Therefore, heartbeatFrequencyMS sets a lower +bound on maxStalenessSeconds, so there is no benefit in recording each server's opTime more frequently. On the other +hand there would be costs: effort adding opTime to all command responses, lock contention getting the opTime on the +server and recording it on the client, complexity in the spec and the client code. + +#### Use current time in staleness estimate + +A proposed staleness formula estimated the secondary's worst possible staleness: + +```javascript +P.lastWriteDate + (now - P.lastUpdateTime) - S.lastWriteDate +``` + +In this proposed formula, the place occupied by "S.lastUpdateTime" in the actual formula is replaced with "now", at the +moment in the server selection process when staleness is being estimated. + +This formula attempted a worst-case estimate right now: it assumed the primary kept writing after the client checked it, +and that the secondary replicated nothing since the client last checked the secondary. The formula was rejected because +it would slosh load to and from the secondary during the interval between checks. + +For example: Say heartbeatFrequencyMS is 10 seconds and maxStalenessSeconds is set to 25 seconds, and immediately after +a secondary is checked its staleness is estimated at 20 seconds. It is eligible for reads until 5 seconds after the +check, then it becomes ineligible, causing all queries to be directed to the primary until the next check, 5 seconds +later. + +#### Server-side Configuration + +We considered a deployment-wide "max staleness" setting that servers communicate to clients in hello, e.g., "120 seconds +is the max staleness." The read preference config is simplified: "maxStalenessSeconds" is gone, instead we have +"staleOk: true" (the default?) and "staleOk: false". + +Based on Customer Advisory Board feedback, configuring staleness per-operation on the client side is more useful. We +should merely avoid closing the door on a future server-side configuration feature. + +## References + +Complaints about stale reads, and proposed solutions: + +- [SERVER-3346](https://jira.mongodb.org/browse/SERVER-3346) +- [SERVER-4935](https://jira.mongodb.org/browse/SERVER-4935) +- [SERVER-4936](https://jira.mongodb.org/browse/SERVER-4936) +- [SERVER-8476](https://jira.mongodb.org/browse/SERVER-8476) +- [SERVER-12861](https://jira.mongodb.org/browse/SERVER-12861) + +## Future Work + +### Future feature to support readConcern "afterOpTime" + +If a future spec allows applications to use readConcern "afterOptime", clients should prefer secondaries that have +already replicated to that opTime, so reads do not block. This is an extension of the mongos logic for CSRS to +applications. + +### Future feature to support server-side configuration + +For this spec, we chose to control maxStalenessSeconds in client code. A future spec could allow database administrators +to configure from the server side how much replication lag makes a secondary too stale to read from. (See +[Server-side Configuration](#server-side-configuration) above.) This could be implemented atop the current feature: if a +server communicates is staleness configuration in its hello response like: + +```javascript +{ hello: true, maxStalenessSeconds: 30 } +``` + +... then a future client can use the value from the server as its default maxStalenessSeconds when there is no +client-side setting. + +## Changelog + +- 2024-08-09: Updated wire versions in tests to 4.0+. + +- 2024-04-30: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and revise changelog. + +- 2021-09-08: Updated tests to support driver removal of support for server versions older than 3.6. + +- 2021-09-03: Clarify that wire version check only applies to available servers. + +- 2021-04-06: Updated to use hello command. + +- 2016-09-29: Specify "no max staleness" in the URI with "maxStalenessMS=-1" instead of "maxStalenessMS=0". + +- 2016-10-24: Rename option from "maxStalenessMS" to "maxStalenessSeconds". + +- 2016-10-25: Change minimum maxStalenessSeconds value from 2 * heartbeatFrequencyMS to heartbeatFrequencyMS + + idleWritePeriodMS (with proper conversions of course). + +- 2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the future, require maxStalenessSeconds to + be at least 90. diff --git a/source/max-staleness/max-staleness.rst b/source/max-staleness/max-staleness.rst index 185a728ba0..fcf41a3ccc 100644 --- a/source/max-staleness/max-staleness.rst +++ b/source/max-staleness/max-staleness.rst @@ -1,601 +1,4 @@ -============= -Max Staleness -============= -:Spec: 142 -:Title: Max Staleness -:Author: \A. Jesse Jiryu Davis -:Lead: Bernie Hackett, Andy Schwerin -:Advisors: Christian Kvalheim, Jeff Yemin, Eric Milkie -:Status: Accepted -:Type: Standards -:Last Modified: November 21, 2016 -:Version: 1.3 - -.. contents:: - -Abstract -======== - -Read preference gains a new option, "maxStalenessSeconds". - -A client (driver or mongos) MUST estimate the staleness of each secondary, -based on lastWriteDate values provided in server isMaster responses, and select only -those secondaries whose staleness is less than or equal to maxStalenessSeconds. - -Most of the implementation of the maxStalenessSeconds option is specified in the -Server Discovery And Monitoring Spec and the Server Selection Spec. This -document supplements those specs by collecting information specifically about -maxStalenessSeconds. - -Meta -==== - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119`_. - -.. _RFC 2119: https://www.ietf.org/rfc/rfc2119.txt - -Motivation for Change -===================== - -Users have often asked for ways to avoid reading from stale secondaries. An -application with a geographically distributed replica set may want to prefer -nearby members to minimize latency, while at the same time avoiding extremely laggy -secondaries to mitigate the risk of very stale reads. - -Goals ------ - -* Provide an approximate means of limiting the staleness of secondary reads. -* Provide a client-side knob to adjust the tradeoff between network-local reads - and data recency. -* Be robust in the face of clock skew between the client and servers, - and skew between the primary and secondaries. -* Avoid "inadvertent primary read preference": prevent a maxStalenessSeconds setting - so small it forces all reads to the primary regardless of actual replication lag. -* Specify how mongos routers and shards track the opTimes of Config Servers as - Replica Sets ("CSRS"). - -Non-Goals ---------- - -* Provide a global server-side configuration of max acceptable staleness (see - `rejected ideas`_). -* Support small values for max staleness. -* Make a consistency guarantee resembling readConcern "afterOpTime". -* Specify how maxStalenessSeconds interacts with readConcern "afterOpTime" in drivers - (distinct from the goal for routers and shards). -* Compensate for the duration of server checks in staleness estimations. - -Specification -============= - -API ---- - -"maxStalenessSeconds" is a new read preference option, with a positive integer value. -It MUST be configurable similar to other read preference options like "readPreference" -and "tag_sets". Clients MUST also recognize it in the connection string:: - - mongodb://host/?readPreference=secondary&maxStalenessSeconds=120 - -Clients MUST consider "maxStalenessSeconds=-1" in the connection string to mean -"no maximum staleness". - -A connection string combining a positive maxStalenessSeconds with read preference -mode "primary" MUST be considered invalid; this includes connection strings with -no explicit read preference mode. - -By default there is no maximum staleness. - -A driver connected to a replica set requires that maxStalenessSeconds be absent, -or be at least smallestMaxStalenessSeconds (90 seconds) and at least heartbeatFrequencyMS + idleWritePeriodMS. -The exact mechanism for enforcement is defined in the Server Selection Spec. - -Besides configuring maxStalenessSeconds in the connection string, -the API for configuring it in code is not specified; -drivers are free to use None, null, -1, or other representations of "no value" -to represent "no max staleness". - -Replica Sets ------------- - -Replica set primaries and secondaries implement the following features to -support maxStalenessSeconds. - -idleWritePeriodMS -~~~~~~~~~~~~~~~~~ - -An idle primary writes a no-op to the oplog every 10 seconds to refresh secondaries' -lastWriteDate values (see SERVER-23892 and `primary must write periodic no-ops`_). -This spec refers to this period as ``idleWritePeriodMS`` with constant -value 10,000. - -lastWrite -~~~~~~~~~ - -A primary's or secondary's isMaster response contains a "lastWrite" subdocument -with these fields (SERVER-8858): - -* lastWriteDate: a BSON UTC datetime, - the wall-clock time of the **primary** when it most recently recorded a write to the oplog. -* opTime: an opaque value representing the most recent replicated write. - Needed for sharding, not used for the maxStalenessSeconds read preference option. - - -Wire Version ------------- - -The maxWireVersion MUST be incremented to 5 -to indicate that the server includes maxStalenessSeconds features -(SERVER-23893). - -Client ------- - -A client (driver or mongos) MUST estimate the staleness of each secondary, -based on lastWriteDate values provided in server isMaster responses, and select for -reads only those secondaries whose estimated staleness is less than or equal to -maxStalenessSeconds. - -If any server's maxWireVersion is less than 5 and maxStalenessSeconds is a positive number, -every attempt at server selection throws an error. - -When there is a known primary, -a secondary S's staleness is estimated with this formula:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - -Where "P" and "S" are the primary's and secondary's ServerDescriptions. -All datetimes are in milliseconds. -The staleness estimate could be temporarily negative. - -When there is no known primary, -a secondary S's staleness is estimated with this formula:: - - SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS - -Where "SMax" is the secondary with the greatest lastWriteDate. - -Explanation of Staleness Estimate With Primary -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1. When the client checks the primary, it gets the delta between the primary's - lastWriteDate and the client clock. Call this "Client_to_Primary". -2. When the client checks a secondary, it gets the delta between the secondary's - lastWriteDate and the client clock. Call this "Client_to_Secondary". -3. The difference of these two is an estimate of the delta between - the primary's and secondary's lastWriteDate. - -Thus:: - - staleness = Client_to_Secondary - Client_to_Primary - = (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) - -Finally, add heartbeatFrequencyMS:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - -This adjusts for the pessimistic assumption that S stops replicating right after S.lastUpdateTime, -so it will be heartbeatFrequencyMS *more* stale by the time it is checked again. -This means S must be fresh enough at S.lastUpdateTime to be eligible for reads from -now until the next check, even if it stops replicating. - -See the Server Discovery and Monitoring Spec and Server Selection Spec for -details of client implementation. - -Routers and shards ------------------- - -Background: Shard servers and mongos servers in a sharded cluster with CSRS -use readConcern "afterOptime" for consistency guarantees when querying the -shard config. - -Besides tracking lastWriteDate, routers and shards additionally track the opTime of -CSRS members if they have maxWireVersion 5 or greater. (See Server Discovery and Monitoring Spec -for details.) - -When a router or shard selects a CSRS member to read from with readConcern -like:: - - readConcern: { afterOpTime: OPTIME } - -... then it follows this selection logic: - -1. Make a list of known CSRS data members. -2. Filter out those whose last known opTime is older than OPTIME. -3. If no servers remain, select the primary. -4. Otherwise, select randomly one of the CSRS members whose roundTripTime is - within localThresholdMS of the member with the fastest roundTripTime. - -Step 4 is the standard localThresholdMS logic from the Server Selection Spec. - -This algorithm helps routers and shards select a secondary that is likely to -satisfy readConcern "afterOpTime" without blocking. - -This feature is only for routers and shards, not drivers. See `Future Work`_. - -Reference Implementation -======================== - -The C Driver (CDRIVER-1363) and Perl Driver (PERL-626). - -Estimating Staleness: Example With a Primary and Continuous Writes -================================================================== - -Consider a primary P and a secondary S, -and a client with heartbeatFrequencyMS set to 10 seconds. -Say that the primary's clock is 50 seconds skewed ahead of the client's. - -The client checks P and S at time 60 (meaning 60 seconds past midnight) by the client's clock. -The primary reports its lastWriteDate is 10. - -Then, S reports its lastWriteDate is 0. The client estimates S's staleness as:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - = (60 - 0) - (60 - 10) + 10 - = 20 seconds - -(Values converted from milliseconds to seconds for the sake of discussion.) - -Note that the secondary appears only 10 seconds stale at this moment, -but the client adds heartbeatFrequencyMS, pessimistically assuming that -the secondary will not replicate at all between now and the next check. -If the current staleness plus heartbeatFrequencyMS is still less than maxStalenessSeconds, -then we can safely read from the secondary from now until the next check. - -The client re-checks P and S 10 seconds later, at time 70 by the client's clock. -S responds first with a lastWriteDate of 5: it has fallen 5 seconds further behind. -The client updates S's lastWriteDate and lastUpdateTime. -The client now estimates S's staleness as:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - = (70 - 5) - (60 - 10) + 10 - = 25 seconds - -Say that P's response arrives 10 seconds later, at client time 80, -and reports its lastWriteDate is 30. -S's staleness is still 25 seconds:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - = (70 - 5) - (80 - 30) + 10 - = 25 seconds - -The same story as a table: - -+--------------+---------------+-----------------+------------------+-----------------+------------------+-----------------+-------------+ -| Client clock | Primary clock | Event | S.lastUpdateTime | S.lastWriteDate | P.lastUpdateTime | P.lastWriteDate | S staleness | -+==============+===============+=================+==================+=================+==================+=================+=============+ -| 60 | 10 | P and S respond | 60 | 0 | 60 | 10 | 20 seconds | -+--------------+---------------+-----------------+------------------+-----------------+------------------+-----------------+-------------+ -| 70 | 20 | S responds | 70 | 5 | 60 | 10 | 25 seconds | -+--------------+---------------+-----------------+------------------+-----------------+------------------+-----------------+-------------+ -| 80 | 30 | P responds | 70 | 5 | 80 | 30 | 25 seconds | -+--------------+---------------+-----------------+------------------+-----------------+------------------+-----------------+-------------+ - -.. Generated with table.py from https://zeth.net/code/table.txt like: - - from table import Table - - data = [ - ['Client clock', 'Primary clock', 'Event', 'S.lastUpdateTime', 'S.lastWriteDate', - 'P.lastUpdateTime', 'P.lastWriteDate', 'S staleness'], - ['60', '10', 'P and S respond', '60', '0', '60', '10', '20 seconds'], - ['70', '20', 'S responds', '70', '5', '60', '10', '25 seconds'], - ['80', '30', 'P responds', '70', '5', '80', '30', '25 seconds'] - ] - - print Table(data).create_table() - -Estimating Staleness: Example With No Primary -============================================= - -Consider a replica set with secondaries S1 and S2, and no primary. -S2 lags 15 seconds *farther* behind S1 and has not yet caught up. -The client has heartbeatFrequencyMS set to 10 seconds. - -When the client checks the two secondaries, -S1's lastWriteDate is 20 and S2's lastWriteDate is 5. - -Because S1 is the secondary with the maximum lastWriteDate, "SMax", -its staleness estimate equals heartbeatFrequencyMS: - - SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS - = 20 - 20 + 10 - = 10 - -(Since max staleness must be at least heartbeatFrequencyMS + idleWritePeriodMS, -S1 is eligible for reads no matter what.) - -S2's staleness estimate is:: - - SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS - = 20 - 5 + 10 - = 25 - -Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set -========================================================================== - -Consider a primary P and a secondary S, -and a client with heartbeatFrequencyMS set to 500 ms. -There is no clock skew. (Previous examples show that skew has no effect.) - -The primary has been idle for 10 seconds and writes a no-op to the oplog at time 50 -(meaning 50 seconds past midnight), and again at time 60. - -Before the secondary can replicate the no-op at time 60, the client checks both servers. -The primary reports its lastWriteDate is 60, the secondary reports 50. - -The client estimates S's staleness as:: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - = (60 - 50) - (60 - 60) + 0.5 - = 10.5 - -The same story as a table: - -+-------+-----------------------+------------------+-----------------+------------------+-----------------+-------------+ -| Clock | Event | S.lastUpdateTime | S.lastWriteDate | P.lastUpdateTime | P.lastWriteDate | S staleness | -+=======+=======================+==================+=================+==================+=================+=============+ -| 50 | Idle write | 50 | | 50 | | | -+-------+-----------------------+------------------+-----------------+------------------+-----------------+-------------+ -| 60 | Idle write begins | 60 | | 50 | | | -+-------+-----------------------+------------------+-----------------+------------------+-----------------+-------------+ -| 60 | Client checks P and S | 60 | 60 | 50 | 60 | 10.5 | -+-------+-----------------------+------------------+-----------------+------------------+-----------------+-------------+ -| 60 | Idle write completes | 60 | | 60 | | | -+-------+-----------------------+------------------+-----------------+------------------+-----------------+-------------+ - -.. Generated with table.py from https://zeth.net/code/table.txt like: - - from table import Table - - data = [ - ['Clock', 'Event', 'S.lastUpdateTime', 'S.lastWriteDate', - 'P.lastUpdateTime', 'P.lastWriteDate', 'S staleness'], - ['50', 'Idle write', '50', '', '50', '', ''], - ['60', 'Idle write begins', '60', '', '50', '', ''], - ['60', 'Client checks P and S', '60', '60', '50', '60', '10.5'], - ['60', 'Idle write completes', '60', '', '60', '', ''], - ] - - print Table(data).create_table() - -In this scenario the actual secondary lag is between 0 and 10 seconds. -But the staleness estimate can be as large as:: - - staleness = idleWritePeriodMS + heartbeatFrequencyMS - -To ensure the secondary is always eligible for reads in an idle replica set, -we require:: - - maxStalenessSeconds * 1000 >= heartbeatFrequencyMS + idleWritePeriodMS - -Supplemental -============ - -Python scripts in this document's source directory: - -* `test_max_staleness_spo.py`: Uses `scipy.optimize` to determine worst-case - accuracy of the staleness estimate in an idle replica set. -* `test_staleness_estimate.py`: Tests whether a client would correctly select - a secondary from an idle replica set, given a random distribution of values - for maxStalenessSeconds, heartbeatFrequencyMS, lastWriteDate, and - lastUpdateTime. - -Test Plan -========= - -See `max-staleness-tests.rst`, -and the YAML and JSON tests in the tests directory. - -Design Rationale -================ - -Specify max staleness in seconds --------------------------------- - -Other driver options that are timespans are in milliseconds, for example -serverSelectionTimeoutMS. The max staleness option is specified in seconds, -however, to make it obvious to users that clients can only enforce large, -imprecise max staleness values. - -maxStalenessSeconds is part of Read Preferences ------------------------------------------------ - -maxStalenessSeconds MAY be configurable at the client, database, and collection -level, and per operation, the same as other read preference fields are, -because users expressed that their tolerance for stale reads varies per -operation. - -Primary must write periodic no-ops ----------------------------------- - -Consider a scenario in which the primary does *not*: - -1. There are no writes for an hour. -2. A client performs a heavy read-only workload with read preference mode - "nearest" and maxStalenessSeconds of 90 seconds. -3. The primary receives a write. -4. In the brief time before any secondary replicates the write, the client - re-checks all servers. -5. Since the primary's lastWriteDate is an hour ahead of all secondaries', the - client only queries the primary. -6. After heartbeatFrequencyMS, the client re-checks all servers and finds - that the secondaries aren't lagging after all, and resumes querying them. - -This apparent "replication lag spike" is just a measurement error, but it causes -exactly the behavior the user wanted to avoid: a small replication lag makes the -client route all queries from the secondaries to the primary. - -Therefore an idle primary must execute a no-op every 10 seconds (idleWritePeriodMS) to keep secondaries' -lastWriteDate values close to the primary's clock. The no-op also keeps opTimes close to -the primary's, which helps mongos choose an up-to-date secondary to read from -in a CSRS. - -Monitoring software like MongoDB Cloud Manager that charts replication lag -will also benefit when spurious lag spikes are solved. - -See `Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set`_. -and `SERVER-23892 `_. - -Smallest allowed value for maxStalenessSeconds ----------------------------------------------- - -If maxStalenessSeconds is a positive number, -it must be at least smallestMaxStalenessSeconds (90 seconds) and at least heartbeatFrequencyMS + idleWritePeriodMS. -The exact mechanism for enforcement is defined in the Server Selection Spec. - -The justification for heartbeatFrequencyMS + idleWritePeriodMS is technical: -If maxStalenessSeconds is set to exactly heartbeatFrequencyMS (converted to seconds), -then so long as a secondary lags even a millisecond -it is ineligible. -Despite the user's read preference mode, the client will always read from the primary. - -This is an example of "inadvertent primary read preference": -a maxStalenessSeconds setting so small -it forces all reads to the primary regardless of actual replication lag. -We want to prohibit this effect (see `goals`_). - -We also want to ensure that a secondary in an idle replica set is always considered -eligible for reads with maxStalenessSeconds. See -`Estimating Staleness: Example of Worst-Case Accuracy With Idle Replica Set`_. - -Requiring maxStalenessSeconds to be at least 90 seconds is a design choice. -If the only requirement were that maxStalenessSeconds be at least heartbeatFrequencyMS + idleWritePeriodMS, -then the smallest value would be 20 seconds for multi-threaded drivers (10 second idleWritePeriodMS plus -multi-threaded drivers' default 10 second heartbeatFrequencyMS), 70 seconds for single-threaded drivers -(whose default heartbeatFrequencyMS is 60 seconds), and 40 seconds for mongos (whose replica set monitor -checks servers every 30 seconds). - -The smallest configurable value for heartbeatFrequencyMS is 0.5 seconds, -so maxStalenessSeconds could be as small as 10.5 when using a driver connected to a replica set, -but mongos provides no such flexibility. - -Therefore, this spec *also* requires that maxStalenessSeconds is at least 90: - -- To provide a minimum for all languages and topologies that is easy to document and explain -- To avoid application breakage when moving from replica set to sharded cluster, or when using the same URI with different drivers -- To emphasize that maxStalenessSeconds is a low-precision heuristic -- To avoid the arbitrary-seeming minimum of 70 seconds imposed by single-threaded drivers - -All servers must have wire version 5 to support maxStalenessSeconds -------------------------------------------------------------------- - -Clients are required to throw an error if maxStalenessSeconds is set, -and any server in the topology has maxWireVersion less than 5. - -Servers began reporting lastWriteDate in wire protocol version 5, -and clients require some or all servers' lastWriteDate in order to -estimate any servers' staleness. -The exact requirements of the formula vary according to TopologyType, -so this spec makes a simple ruling: if any server is running an outdated version, -maxStalenessSeconds cannot be supported. - -Rejected ideas --------------- - -Add all secondaries' opTimes to primary's isMaster response -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Not needed; each secondary's self-report of its opTime is just as good as the -primary's. - -Use opTimes from command responses besides isMaster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An idea was to add opTime to command responses that don't already include it -(e.g., "find"), and use these opTimes to update ServerDescriptions more -frequently than the periodic isMaster calls. - -But while a server is not being used (e.g., while it is too stale, or while it -does not match some other part of the Read Preference), only its periodic -isMaster responses can update its opTime. Therefore, heartbeatFrequencyMS -sets a lower bound on maxStalenessSeconds, so there is no benefit in recording -each server's opTime more frequently. On the other hand there would be -costs: effort adding opTime to all command responses, lock contention -getting the opTime on the server and recording it on the client, complexity -in the spec and the client code. - -Use current time in staleness estimate -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A proposed staleness formula estimated the secondary's worst possible staleness:: - - P.lastWriteDate + (now - P.lastUpdateTime) - S.lastWriteDate - -In this proposed formula, the place occupied by "S.lastUpdateTime" in the actual formula is replaced with "now", -at the moment in the server selection process when staleness is being estimated. - -This formula attempted a worst-case estimate right now: -it assumed the primary kept writing after the client checked it, -and that the secondary replicated nothing since the client last checked the secondary. -The formula was rejected because it would slosh load to and from the secondary -during the interval between checks. - -For example: -Say heartbeatFrequencyMS is 10 seconds and maxStalenessSeconds is set to 25 seconds, -and immediately after a secondary is checked its staleness is estimated at 20 seconds. -It is eligible for reads until 5 seconds after the check, then it becomes ineligible, -causing all queries to be directed to the primary until the next check, 5 seconds later. - -Server-side Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~ - -We considered a deployment-wide "max staleness" setting that servers -communicate to clients in isMaster, e.g., "120 seconds is the max staleness." -The read preference config is simplified: "maxStalenessSeconds" is gone, instead we -have "staleOk: true" (the default?) and "staleOk: false". - -Based on Customer Advisory Board feedback, configuring staleness -per-operation on the client side is more useful. We should merely avoid -closing the door on a future server-side configuration feature. - -References -========== - -Complaints about stale reads, and proposed solutions: - -* `SERVER-3346 `_ -* `SERVER-4935 `_ -* `SERVER-4936 `_ -* `SERVER-8476 `_ -* `SERVER-12861 `_ - -Future Work -=========== - -Future feature to support readConcern "afterOpTime" ---------------------------------------------------- - -If a future spec allows applications to use readConcern "afterOptime", clients -should prefer secondaries that have already replicated to that opTime, so reads -do not block. This is an extension of the mongos logic for CSRS to applications. - -Future feature to support server-side configuration ---------------------------------------------------- - -For this spec, we chose to control maxStalenessSeconds in client code. -A future spec could allow database administrators to configure from the server -side how much replication lag makes a secondary too stale to read from. -(See `Server-side Configuration`_ above.) This could be implemented atop the -current feature: if a server communicates is staleness configuration in its -ismaster response like:: - - { ismaster: true, maxStalenessSeconds: 30 } - -... then a future client can use the value from the server as its default -maxStalenessSeconds when there is no client-side setting. - -Changes -======= - -2016-09-29: Specify "no max staleness" in the URI with "maxStalenessMS=-1" -instead of "maxStalenessMS=0". -2016-10-24: Rename option from "maxStalenessMS" to "maxStalenessSeconds". -2016-10-25: Change minimum maxStalenessSeconds value from 2 * heartbeatFrequencyMS -to heartbeatFrequencyMS + idleWritePeriodMS (with proper conversions of course). -2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the -future, require maxStalenessSeconds to be at least 90. +.. note:: + This specification has been converted to Markdown and renamed to + `max-staleness.md `_. diff --git a/source/max-staleness/tests/README.md b/source/max-staleness/tests/README.md new file mode 100644 index 0000000000..5e190e7317 --- /dev/null +++ b/source/max-staleness/tests/README.md @@ -0,0 +1,51 @@ +# Max Staleness Tests + +This directory contains platform-independent tests that drivers can use to prove their conformance to the Max Staleness +Spec. The tests are provided in both YAML and JSON formats, and drivers may test against whichever format is more +convenient for them. + +## Test Format and Use + +YAML files contain the following setup for each test: + +- `heartbeatFrequencyMS`: optional int +- `topology_description`: the state of a mocked cluster + - `type`: the TopologyType + - `servers`: a list of ServerDescriptions, each with: + - `address`: a "host:port" + - `type`: a ServerType + - `avg_rtt_ms`: average round trip time in milliseconds[^1] + - `lastWrite`: subdocument + - `lastWriteDate`: nonzero int64, milliseconds since some past time + - `maxWireVersion`: an int + - `lastUpdateTime`: milliseconds since the Unix epoch +- `read_preference`: a read preference document + +For each test, create a MongoClient. Configure it with the heartbeatFrequencyMS specified by the test, or accept the +driver's default heartbeatFrequencyMS if the test omits this field. + +(Single-threaded and multi-threaded clients now make heartbeatFrequencyMS configurable. This is a change in Server +Discovery and Monitoring to support maxStalenessSeconds. Before, multi-threaded clients were allowed to make it +configurable or not.) + +For each test, create a new TopologyDescription object initialized with the values from `topology_description`. +Initialize ServerDescriptions from the provided data. Create a ReadPreference object initialized with the values from +`read_preference`. Select servers that match the ReadPreference. + +Each test specifies that it expects an error, or specifies two sets of servers: + +- `error: true` +- `suitable_servers`: the set of servers in the TopologyDescription that are suitable for the ReadPreference, without + taking `avg_rtt_ms` into account. +- `in_latency_window`: the set of suitable servers whose round trip time qualifies them according to the default latency + threshold of 15ms. In each test there is one server in the latency window, to ensure tests pass or fail + deterministically. + +If the file contains `error: true`, drivers MUST test that they throw an error during server selection due to an invalid +read preference. For other files, drivers MUST test that they correctly select the set of servers in +`in_latency_window`. + +Drivers MAY also test that before filtration by latency, they select the specified set of "suitable" servers. + +[^1]: `avg_rtt_ms` is included merely for consistency with Server Selection tests. It is not significant in Max Staleness + tests. diff --git a/source/max-staleness/tests/README.rst b/source/max-staleness/tests/README.rst deleted file mode 100644 index b6d6f2c978..0000000000 --- a/source/max-staleness/tests/README.rst +++ /dev/null @@ -1,72 +0,0 @@ -=================== -Max Staleness Tests -=================== - -This directory contains platform-independent tests that drivers can use -to prove their conformance to the Max Staleness Spec. The tests -are provided in both YAML and JSON formats, and drivers may test against -whichever format is more convenient for them. - -Test Format and Use -------------------- - -YAML files contain the following setup for each test: - -- ``heartbeatFrequencyMS``: optional int - -- ``topology_description``: the state of a mocked cluster - - - ``type``: the TopologyType - - - ``servers``: a list of ServerDescriptions, each with: - - - ``address``: a "host:port" - - - ``type``: a ServerType - - - ``avg_rtt_ms``: average round trip time in milliseconds [1]_ - - - ``lastWrite``: subdocument - - - ``lastWriteDate``: nonzero int64, milliseconds since some past time - - - ``maxWireVersion``: an int - - - ``lastUpdateTime``: milliseconds since the Unix epoch - -- ``read_preference``: a read preference document - -For each test, create a MongoClient. -Configure it with the heartbeatFrequencyMS specified by the test, -or accept the driver's default heartbeatFrequencyMS if the test omits this field. - -(Single-threaded and multi-threaded clients now make heartbeatFrequencyMS configurable. -This is a change in Server Discovery and Monitoring to support maxStalenessSeconds. -Before, multi-threaded clients were allowed to make it configurable or not.) - -For each test, create a new TopologyDescription object initialized with the -values from ``topology_description``. Initialize ServerDescriptions from the -provided data. Create a ReadPreference object initialized with the values -from ``read_preference``. Select servers that match the ReadPreference. - -Each test specifies that it expects an error, or specifies two sets of servers: - -- ``error: true`` -- ``suitable_servers``: the set of servers in the TopologyDescription - that are suitable for the ReadPreference, without taking ``avg_rtt_ms`` - into account. -- ``in_latency_window``: the set of suitable servers whose round trip time - qualifies them according to the default latency threshold of 15ms. - In each test there is one server in the latency window, to ensure - tests pass or fail deterministically. - -If the file contains ``error: true``, drivers MUST test that they throw an -error during server selection, due to an invalid read preference or -incompatible wire versions. For other files, drivers MUST test that they -correctly select the set of servers in ``in_latency_window``. - -Drivers MAY also test that before filtration by latency, they select the -specified set of "suitable" servers. - -.. [1] ``avg_rtt_ms`` is included merely for consistency with - Server Selection tests. It is not significant in Max Staleness tests. diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json b/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json index 1e3dd0bfd9..db8b061b30 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json @@ -7,7 +7,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -37,7 +37,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -49,7 +49,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -63,7 +63,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml index ca4c83d633..793aa10eaa 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml @@ -8,14 +8,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.json b/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.json deleted file mode 100644 index 7f9fa764c7..0000000000 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "topology_description": { - "type": "ReplicaSetNoPrimary", - "servers": [ - { - "address": "a:27017", - "type": "RSSecondary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 5, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "2" - } - } - }, - { - "address": "b:27017", - "type": "RSSecondary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 4, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - } - ] - }, - "read_preference": { - "mode": "Nearest", - "maxStalenessSeconds": 120 - }, - "error": true -} diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.yml deleted file mode 100644 index 74032747ec..0000000000 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Incompatible.yml +++ /dev/null @@ -1,25 +0,0 @@ -# During server selection, clients (drivers or mongos) MUST raise an error if -# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` -# is less than 5 (`SERVER-23893`_). ---- -topology_description: - type: ReplicaSetNoPrimary - servers: - - &1 - address: a:27017 - type: RSSecondary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "2"}} - - &2 - address: b:27017 - type: RSSecondary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. - lastWrite: {lastWriteDate: {$numberLong: "1"}} -read_preference: - mode: Nearest - maxStalenessSeconds: 120 -error: true diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json b/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json index e1abef2844..10b6f28786 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml index 3acc594c71..2016941713 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/LastUpdateTime.yml @@ -8,21 +8,21 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 1 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 25002 # Not used when there's no primary. lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 25001 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json b/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json new file mode 100644 index 0000000000..28e5e2aa4a --- /dev/null +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.json @@ -0,0 +1,20 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "a:27017", + "type": "Unknown" + }, + { + "address": "b:27017", + "type": "Unknown" + } + ] + }, + "read_preference": { + "mode": "Nearest", + "maxStalenessSeconds": 1 + }, + "error": true +} diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml new file mode 100644 index 0000000000..575c367b83 --- /dev/null +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml @@ -0,0 +1,15 @@ +# maxStalenessSeconds must be at least 90 seconds, even with no known servers. +--- +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: a:27017 + type: Unknown + - &2 + address: b:27017 + type: Unknown +read_preference: + mode: Nearest + maxStalenessSeconds: 1 # Too small. +error: true diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json index 53549e6431..38b9986500 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml index 6f71c92b7d..783831f477 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest.yml @@ -8,21 +8,21 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 avg_rtt_ms: 5 lastUpdateTime: 0 type: RSSecondary lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json index e2768c7fb8..586b47ccd2 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml index ecf74e7e43..867b7706a4 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Nearest2.yml @@ -8,21 +8,21 @@ topology_description: avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 avg_rtt_ms: 5 lastUpdateTime: 0 type: RSSecondary lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json b/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json index 28e5e2aa4a..5905fcbc60 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.json @@ -14,7 +14,8 @@ }, "read_preference": { "mode": "Nearest", - "maxStalenessSeconds": 1 + "maxStalenessSeconds": 90 }, - "error": true + "suitable_servers": [], + "in_latency_window": [] } diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml index 575c367b83..463bf7d6dc 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/NoKnownServers.yml @@ -1,4 +1,4 @@ -# maxStalenessSeconds must be at least 90 seconds, even with no known servers. +# valid maxStalenessSeconds and no known servers results in an empty set of suitable servers --- topology_description: type: ReplicaSetNoPrimary @@ -11,5 +11,6 @@ topology_description: type: Unknown read_preference: mode: Nearest - maxStalenessSeconds: 1 # Too small. -error: true + maxStalenessSeconds: 90 +suitable_servers: [] +in_latency_window: [] diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json b/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json new file mode 100644 index 0000000000..15a62090e3 --- /dev/null +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.json @@ -0,0 +1,60 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "a:27017", + "type": "PossiblePrimary", + "avg_rtt_ms": 5, + "maxWireVersion": 0 + }, + { + "address": "b:27017", + "type": "Unknown", + "avg_rtt_ms": 5, + "maxWireVersion": 0 + }, + { + "address": "c:27017", + "type": "RSSecondary", + "maxWireVersion": 21, + "avg_rtt_ms": 5, + "lastWrite": { + "lastWriteDate": { + "$numberLong": "1" + } + } + } + ] + }, + "read_preference": { + "mode": "Nearest", + "maxStalenessSeconds": 120 + }, + "suitable_servers": [ + { + "address": "c:27017", + "type": "RSSecondary", + "maxWireVersion": 21, + "avg_rtt_ms": 5, + "lastWrite": { + "lastWriteDate": { + "$numberLong": "1" + } + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "type": "RSSecondary", + "maxWireVersion": 21, + "avg_rtt_ms": 5, + "lastWrite": { + "lastWriteDate": { + "$numberLong": "1" + } + } + } + ] +} diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml new file mode 100644 index 0000000000..ccbec183ca --- /dev/null +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/OneKnownTwoUnavailable.yml @@ -0,0 +1,28 @@ +# Clients ignore Unknown/PossiblePrimary servers even when read preference has maxStalenessSeconds +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: a:27017 + type: PossiblePrimary + avg_rtt_ms: 5 + maxWireVersion: 0 + - &2 + address: b:27017 + type: Unknown + avg_rtt_ms: 5 + maxWireVersion: 0 + - &3 + address: c:27017 + type: RSSecondary + maxWireVersion: 21 + avg_rtt_ms: 5 + lastWrite: {lastWriteDate: {$numberLong: "1"}} +read_preference: + mode: Nearest + maxStalenessSeconds: 120 +suitable_servers: +- *3 +in_latency_window: +- *3 + diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json index 8c6be6886a..7c036f725c 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.json @@ -8,7 +8,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -39,7 +39,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -53,7 +53,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml index 6260eb85c0..c01aff6f79 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred.yml @@ -9,14 +9,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: PrimaryPreferred diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json index 26007c026e..56fcb156bb 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -28,7 +28,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "nyc" } @@ -58,7 +58,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -75,7 +75,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml index 19c297abe8..a5df80ff36 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml @@ -12,7 +12,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: tokyo # Matches second tag set. - &2 @@ -21,7 +21,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: nyc read_preference: diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json b/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json index 7d5eb58f4d..5a4b0c8226 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.json @@ -8,7 +8,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "125002" @@ -23,7 +23,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -38,7 +38,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -53,7 +53,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -80,7 +80,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -97,7 +97,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml index 3d6832ff42..93e81841b4 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/Secondary.yml @@ -9,7 +9,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "125002"}} tags: data_center: tokyo # No match, but its lastWriteDate is used in estimate. @@ -18,7 +18,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc @@ -27,7 +27,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. tags: data_center: nyc @@ -36,7 +36,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} tags: data_center: tokyo # No match. diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json index df0bb5d77f..19a948e928 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.json @@ -7,7 +7,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -38,7 +38,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -52,7 +52,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml index 4c6a49d0cb..f997882bc0 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred.yml @@ -8,14 +8,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: SecondaryPreferred diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json index 1ac3ea0aed..b4633d88f3 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.json @@ -8,7 +8,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "125002" @@ -23,7 +23,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -38,7 +38,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -53,7 +53,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -80,7 +80,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -97,7 +97,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml index f0e26742f2..f4a6c8f6b6 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml @@ -9,7 +9,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "125002"}} tags: data_center: tokyo # No match, but its lastWriteDate is used in estimate. @@ -18,7 +18,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc @@ -27,7 +27,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. tags: data_center: nyc @@ -36,7 +36,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} tags: data_center: tokyo # No match. diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json b/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json index cb5dc5175a..ccb916f107 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.json @@ -7,7 +7,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 4, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml b/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml index 59e9536daf..e6be41ca67 100644 --- a/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml +++ b/source/max-staleness/tests/ReplicaSetNoPrimary/ZeroMaxStaleness.yml @@ -8,14 +8,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json b/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json index ed18d5837e..00137cf69e 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json @@ -7,7 +7,7 @@ "type": "RSPrimary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -37,7 +37,7 @@ "type": "RSPrimary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -49,7 +49,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -63,7 +63,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml index b5d9192315..1ea9b209c8 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml @@ -8,14 +8,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.json deleted file mode 100644 index d27ea11202..0000000000 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "topology_description": { - "type": "ReplicaSetWithPrimary", - "servers": [ - { - "address": "a:27017", - "type": "RSPrimary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 5, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - }, - { - "address": "b:27017", - "type": "RSSecondary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 4, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - } - ] - }, - "read_preference": { - "mode": "Nearest", - "maxStalenessSeconds": 120 - }, - "error": true -} diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.yml deleted file mode 100644 index 9265526895..0000000000 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Incompatible.yml +++ /dev/null @@ -1,25 +0,0 @@ -# During server selection, clients (drivers or mongos) MUST raise an error if -# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` -# is less than 5 (`SERVER-23893`_). ---- -topology_description: - type: ReplicaSetWithPrimary - servers: - - &1 - address: a:27017 - type: RSPrimary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1"}} - - &2 - address: b:27017 - type: RSSecondary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. - lastWrite: {lastWriteDate: {$numberLong: "1"}} -read_preference: - mode: Nearest - maxStalenessSeconds: 120 -error: true diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json b/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json index bbd8238e8a..9d1db2de65 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.json @@ -13,7 +13,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml index 129310744f..e4fbe1ff7d 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LastUpdateTime.yml @@ -8,7 +8,7 @@ topology_description: avg_rtt_ms: 50 # Too far. lastUpdateTime: 1 lastWrite: {lastWriteDate: {$numberLong: "2"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary @@ -17,14 +17,14 @@ topology_description: # Updated 125 sec after primary, so 125 sec stale. # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. lastWrite: {lastWriteDate: {$numberLong: "2"}} - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 125001 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json index cb05f52aa2..b0636236cc 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -39,7 +39,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -51,7 +51,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -65,7 +65,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml index 455696492d..1e479c32e0 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat.yml @@ -10,14 +10,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json index be169a3dcb..76edfcb836 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml index 3b6b5adc50..836467bf1f 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/LongHeartbeat2.yml @@ -10,14 +10,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json index 173f5742a2..aa936e3c67 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml index 2b5126d4f6..9edd4199bf 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml @@ -11,14 +11,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json index eee3462783..c24752a7f1 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json @@ -7,7 +7,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml index 1872d3b365..92358a702d 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml @@ -9,14 +9,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: maxStalenessSeconds: 120 diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json index 753fb82ca3..d3a9535b09 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml index 76915f22b5..5ef6c720a5 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest.yml @@ -8,21 +8,21 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 avg_rtt_ms: 5 lastUpdateTime: 0 type: RSSecondary lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json index 6233c0815a..f91706e804 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "c:27017", @@ -37,7 +37,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, @@ -56,7 +56,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -68,7 +68,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ], "in_latency_window": [ @@ -82,7 +82,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 } ] } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml index 3b249d8f3b..7d4f706fc7 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest2.yml @@ -8,21 +8,21 @@ topology_description: avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. - maxWireVersion: 5 + maxWireVersion: 21 - &3 address: c:27017 avg_rtt_ms: 5 lastUpdateTime: 0 type: RSSecondary lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 150 diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json index 9a1cd3bb12..4ed0b9ed2e 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -28,7 +28,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "nyc" } @@ -58,7 +58,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -75,7 +75,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml index 0c2b6e3ea2..6db5d4362b 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Nearest_tags.yml @@ -12,7 +12,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: tokyo - &2 @@ -21,7 +21,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: nyc read_preference: diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json index 107ae2755e..7945530e6a 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -39,7 +39,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -53,7 +53,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml index 8caa31ed8f..e38f33d7c1 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred.yml @@ -9,14 +9,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: PrimaryPreferred diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.json b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.json deleted file mode 100644 index a6681f6a13..0000000000 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "topology_description": { - "type": "ReplicaSetWithPrimary", - "servers": [ - { - "address": "a:27017", - "type": "RSPrimary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 5, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - }, - { - "address": "b:27017", - "type": "RSSecondary", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 4, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - } - ] - }, - "read_preference": { - "mode": "PrimaryPreferred", - "maxStalenessSeconds": 150 - }, - "error": true -} diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml deleted file mode 100644 index 677a52ce9a..0000000000 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Primary has wire version 5, secondary has 4, read preference primaryPreferred -# with maxStalenessSeconds. The client must error, even though it uses primary and -# never applies maxStalenessSeconds. Proves that the compatibility check precedes -# filtration. ---- -topology_description: - type: ReplicaSetWithPrimary - servers: - - &1 - address: a:27017 - type: RSPrimary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1"}} - - &2 - address: b:27017 - type: RSSecondary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 4 # Too old. - lastWrite: {lastWriteDate: {$numberLong: "1"}} -read_preference: - mode: PrimaryPreferred - maxStalenessSeconds: 150 -error: true - diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json index 5f8a21f15c..b433d6a430 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.json @@ -7,7 +7,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -38,7 +38,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -52,7 +52,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml index ac61c3a994..ff35b9bdab 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred.yml @@ -8,14 +8,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: SecondaryPreferred diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json index 09ce6d6bd0..e594af7832 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "125002" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -35,7 +35,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 1, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -50,7 +50,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -65,7 +65,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -92,7 +92,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -107,7 +107,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 1, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -124,7 +124,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml index c32e8c41af..4d17a4d7fd 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml @@ -9,14 +9,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc @@ -25,7 +25,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 1 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} # Not used in estimate since we have a primary. tags: data_center: nyc @@ -34,7 +34,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. tags: data_center: nyc @@ -43,7 +43,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} tags: data_center: tokyo # No match. diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json index 3700c30453..bc0953c657 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -40,7 +40,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "nyc" } @@ -70,7 +70,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -87,7 +87,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml index eea732a8ce..82c5a799f7 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml @@ -12,14 +12,14 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: tokyo - &3 @@ -28,7 +28,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: nyc read_preference: diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json index f117159f64..2817cf9225 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.json @@ -8,7 +8,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "125002" @@ -20,7 +20,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -35,7 +35,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 1, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -50,7 +50,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -65,7 +65,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -92,7 +92,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -107,7 +107,7 @@ "type": "RSSecondary", "avg_rtt_ms": 50, "lastUpdateTime": 1, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1000001" @@ -124,7 +124,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml index 3f529477a8..721cbc8a5d 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags.yml @@ -9,14 +9,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc @@ -25,7 +25,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 1 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1000001"}} # Not used in estimate since we have a primary. tags: data_center: nyc @@ -34,7 +34,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. tags: data_center: nyc @@ -43,7 +43,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} tags: data_center: tokyo # No match. diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json index b739c6141b..7aa487a078 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.json @@ -13,7 +13,7 @@ "$numberLong": "125002" } }, - "maxWireVersion": 5 + "maxWireVersion": 21 }, { "address": "b:27017", @@ -25,7 +25,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -40,7 +40,7 @@ "$numberLong": "1" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "nyc" } @@ -70,7 +70,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } @@ -87,7 +87,7 @@ "$numberLong": "2" } }, - "maxWireVersion": 5, + "maxWireVersion": 21, "tags": { "data_center": "tokyo" } diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml index e15450b58e..669ec5bb56 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/Secondary_tags2.yml @@ -12,14 +12,14 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "125002"}} - maxWireVersion: 5 + maxWireVersion: 21 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "2"}} - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: tokyo - &3 @@ -28,7 +28,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. - maxWireVersion: 5 + maxWireVersion: 21 tags: data_center: nyc read_preference: diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json b/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json index f17aa93a3f..fff5609fcc 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.json @@ -7,7 +7,7 @@ "type": "RSPrimary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "2" @@ -19,7 +19,7 @@ "type": "RSSecondary", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 4, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml b/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml index 99d3a66f35..18790394e7 100644 --- a/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +++ b/source/max-staleness/tests/ReplicaSetWithPrimary/ZeroMaxStaleness.yml @@ -8,14 +8,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "2"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/Sharded/Incompatible.json b/source/max-staleness/tests/Sharded/Incompatible.json deleted file mode 100644 index c261383f4a..0000000000 --- a/source/max-staleness/tests/Sharded/Incompatible.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "topology_description": { - "type": "Sharded", - "servers": [ - { - "address": "a:27017", - "type": "Mongos", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 5, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - }, - { - "address": "b:27017", - "type": "Mongos", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 4, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - } - ] - }, - "read_preference": { - "mode": "Nearest", - "maxStalenessSeconds": 120 - }, - "error": true -} diff --git a/source/max-staleness/tests/Sharded/Incompatible.yml b/source/max-staleness/tests/Sharded/Incompatible.yml deleted file mode 100644 index 7658136271..0000000000 --- a/source/max-staleness/tests/Sharded/Incompatible.yml +++ /dev/null @@ -1,25 +0,0 @@ -# During server selection, clients (drivers or mongos) MUST raise an error if -# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` -# is less than 5 (`SERVER-23893`_). ---- -topology_description: - type: Sharded - servers: - - &1 - address: a:27017 - type: Mongos - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1"}} - - &2 - address: b:27017 - type: Mongos - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. - lastWrite: {lastWriteDate: {$numberLong: "1"}} -read_preference: - mode: Nearest - maxStalenessSeconds: 120 -error: true diff --git a/source/max-staleness/tests/Sharded/SmallMaxStaleness.json b/source/max-staleness/tests/Sharded/SmallMaxStaleness.json index 27b9f1c12f..98e05be363 100644 --- a/source/max-staleness/tests/Sharded/SmallMaxStaleness.json +++ b/source/max-staleness/tests/Sharded/SmallMaxStaleness.json @@ -8,7 +8,7 @@ "type": "Mongos", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -20,7 +20,7 @@ "type": "Mongos", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -39,7 +39,7 @@ "type": "Mongos", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -51,7 +51,7 @@ "type": "Mongos", "avg_rtt_ms": 50, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -65,7 +65,7 @@ "type": "Mongos", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/Sharded/SmallMaxStaleness.yml b/source/max-staleness/tests/Sharded/SmallMaxStaleness.yml index 2a3d8e490d..9d5034df1e 100644 --- a/source/max-staleness/tests/Sharded/SmallMaxStaleness.yml +++ b/source/max-staleness/tests/Sharded/SmallMaxStaleness.yml @@ -9,14 +9,14 @@ topology_description: type: Mongos avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} - &2 address: b:27017 type: Mongos avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/Single/Incompatible.json b/source/max-staleness/tests/Single/Incompatible.json deleted file mode 100644 index b37fec7c1a..0000000000 --- a/source/max-staleness/tests/Single/Incompatible.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "topology_description": { - "type": "Single", - "servers": [ - { - "address": "a:27017", - "type": "Standalone", - "avg_rtt_ms": 5, - "lastUpdateTime": 0, - "maxWireVersion": 4, - "lastWrite": { - "lastWriteDate": { - "$numberLong": "1" - } - } - } - ] - }, - "read_preference": { - "mode": "Nearest", - "maxStalenessSeconds": 120 - }, - "error": true -} diff --git a/source/max-staleness/tests/Single/Incompatible.yml b/source/max-staleness/tests/Single/Incompatible.yml deleted file mode 100644 index d85d9714d9..0000000000 --- a/source/max-staleness/tests/Single/Incompatible.yml +++ /dev/null @@ -1,18 +0,0 @@ -# During server selection, clients (drivers or mongos) MUST raise an error if -# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` -# is less than 5 (`SERVER-23893`_). ---- -topology_description: - type: Single - servers: - - &1 - address: a:27017 - type: Standalone - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 4 # Incompatible. - lastWrite: {lastWriteDate: {$numberLong: "1"}} -read_preference: - mode: Nearest - maxStalenessSeconds: 120 -error: true diff --git a/source/max-staleness/tests/Single/SmallMaxStaleness.json b/source/max-staleness/tests/Single/SmallMaxStaleness.json index c6b10231b8..d948739855 100644 --- a/source/max-staleness/tests/Single/SmallMaxStaleness.json +++ b/source/max-staleness/tests/Single/SmallMaxStaleness.json @@ -8,7 +8,7 @@ "type": "Standalone", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -27,7 +27,7 @@ "type": "Standalone", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" @@ -41,7 +41,7 @@ "type": "Standalone", "avg_rtt_ms": 5, "lastUpdateTime": 0, - "maxWireVersion": 5, + "maxWireVersion": 21, "lastWrite": { "lastWriteDate": { "$numberLong": "1" diff --git a/source/max-staleness/tests/Single/SmallMaxStaleness.yml b/source/max-staleness/tests/Single/SmallMaxStaleness.yml index c4285092b2..3cba2ae55c 100644 --- a/source/max-staleness/tests/Single/SmallMaxStaleness.yml +++ b/source/max-staleness/tests/Single/SmallMaxStaleness.yml @@ -9,7 +9,7 @@ topology_description: type: Standalone avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 + maxWireVersion: 21 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest diff --git a/source/max-staleness/tests/Unknown/SmallMaxStaleness.json b/source/max-staleness/tests/Unknown/SmallMaxStaleness.json index bf6174b8e4..0e609bcf94 100644 --- a/source/max-staleness/tests/Unknown/SmallMaxStaleness.json +++ b/source/max-staleness/tests/Unknown/SmallMaxStaleness.json @@ -6,7 +6,7 @@ { "address": "a:27017", "type": "Unknown", - "maxWireVersion": 5 + "maxWireVersion": 21 } ] }, diff --git a/source/max-staleness/tests/Unknown/SmallMaxStaleness.yml b/source/max-staleness/tests/Unknown/SmallMaxStaleness.yml index 3a39615ac0..5d42063765 100644 --- a/source/max-staleness/tests/Unknown/SmallMaxStaleness.yml +++ b/source/max-staleness/tests/Unknown/SmallMaxStaleness.yml @@ -7,7 +7,7 @@ topology_description: - &1 address: a:27017 type: Unknown - maxWireVersion: 5 + maxWireVersion: 21 read_preference: mode: Nearest maxStalenessSeconds: 1 diff --git a/source/message/OP_MSG.md b/source/message/OP_MSG.md new file mode 100644 index 0000000000..9238b469ac --- /dev/null +++ b/source/message/OP_MSG.md @@ -0,0 +1,417 @@ +# OP_MSG + +- Status: Accepted +- Minimum Server Version: 3.6 + +### Abstract + +`OP_MSG` is a bi-directional wire protocol opcode introduced in MongoDB 3.6 with the goal of replacing most existing +opcodes, merging their use into one extendable opcode. + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Specification + +#### Usage + +All messages, including authentication messages, MUST use `OP_MSG`. Refer to the +[handshake specification](https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md) +for the appropriate use of `OP_MSG`. See also +[OP_COMPRESSED specification](https://github.com/mongodb/specifications/blob/master/source/compression/OP_COMPRESSED.md) +for more details. + +#### OP_MSG + +Types used in this document + +| Type | Meaning | +| -------- | ------------------------------------------------- | +| document | A BSON document | +| cstring | NULL terminated string | +| int32 | 4 bytes (32-bit signed integer, two's complement) | +| uint8 | 1 byte (8-bit unsigned integer) | +| uint32 | 4 bytes (32-bit unsigned integer) | +| union | One of the listed members | + +Elements inside brackets (`[` `]`) are optional parts of the message. + +- Zero or more instances: `*` +- One or more instances: `+` + +The new opcode, called `OP_MSG`, has the following structure: + +```c +struct Section { + uint8 payloadType; + union payload { + document document; // payloadType == 0 + struct sequence { // payloadType == 1 + int32 size; + cstring identifier; + document* documents; + }; + }; +}; + +struct OP_MSG { + struct MsgHeader { + int32 messageLength; + int32 requestID; + int32 responseTo; + int32 opCode = 2013; + }; + uint32 flagBits; + Section+ sections; + [uint32 checksum;] +}; +``` + +Each `OP_MSG` MUST NOT exceed the `maxMessageSizeBytes` as configured by the MongoDB Handshake. + +Each `OP_MSG` MUST have one section with `Payload Type 0`, and zero or more `Payload Type 1`. Bulk writes SHOULD use +`Payload Type 1`, and MUST do so when the batch contains more than one entry. + +Sections may exist in any order. Each `OP_MSG` MAY contain a checksum, and MUST set the relevant `flagBits` when that +field is included. + +| Field | Description | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| flagBits | Network level flags, such as signaling recipient that another message is incoming without any other actions in the meantime, and availability of message checksums | +| sections | An array of one or more sections | +| checksum | crc32c message checksum. When present, the appropriate flag MUST be set in the flagBits. | + +##### flagBits + +flagBits contains a bit vector of specialized network flags. The low 16 bits declare what the current message contains, +and what the expectations of the recipient are. The high 16 bits are designed to declare optional attributes of the +current message and expectations of the recipient. + +All unused bits MUST be set to 0. + +Clients MUST error if any unsupported or undefined required bits are set to 1 and MUST ignore all undefined optional +bits. + +The currently defined flags are: + +| Bit | Name | Request | Response | Description | +| --- | --------------- | ------- | -------- | ---------------------------------------------------------------------------------- | +| 0 | checksumPresent | x | x | Checksum present | +| 1 | moreToCome | x | x | Sender will send another message and is not prepared for overlapping messages | +| -16 | exhaustAllowed | x | | Client is prepared for multiple replies (using the moreToCome bit) to this request | + +##### checksumPresent + +This is a reserved field for future support of `crc32c` checksums. + +##### moreToCome + +The `OP_MSG` message is essentially a request-response protocol, one message per turn. However, setting the `moreToCome` +flag indicates to the recipient that the sender is not ready to give up its turn and will send another message. + +##### moreToCome On Requests + +When the `moreToCome` flag is set on a request it signals to the recipient that the sender does not want to know the +outcome of the message. There is no response to a request where `moreToCome` has been set. Clients doing unacknowledged +writes MUST set the `moreToCome` flag, and MUST set the writeConcern to `w=0`. + +If, during the processing of a `moreToCome` flagged write request, a server discovers that it is no longer primary, then +the server will close the connection. All other errors during processing will be silently dropped, and will not result +in the connection being closed. + +##### moreToCome On Responses + +When the `moreToCome` flag is set on a response it signals to the recipient that the sender will send additional +responses on the connection. The recipient MUST continue to read responses until it reads a response with the +`moreToCome` flag not set, and MUST NOT send any more requests on this connection until it reads a response with the +`moreToCome` flag not set. The client MUST either consume all messages with the `moreToCome` flag set or close the +connection. + +When the server sends responses with the `moreToCome` flag set, each of these responses will have a unique `messageId`, +and the `responseTo` field of every follow-up response will be the `messageId` of the previous response. + +The client MUST be prepared to receive a response without `moreToCome` set prior to completing iteration of a cursor, +even if an earlier response for the same cursor had the `moreToCome` flag set. To continue iterating such a cursor, the +client MUST issue an explicit `getMore` request. + + + +##### exhaustAllowed + +Setting this flag on a request indicates to the recipient that the sender is prepared to handle multiple replies (using +the `moreToCome` bit) to this request. The server will never produce replies with the `moreToCome` bit set unless the +request has the `exhaustAllowed` bit set. + +Setting the `exhaustAllowed` bit on a request does not guarantee that the responses will have the `moreToCome` bit set. + +MongoDB server only handles the `exhaustAllowed` bit on the following operations. A driver MUST NOT set the +`exhaustAllowed` bit on other operations. + +| Operation | Minimum MongoDB Version | +| ------------------------------ | -------------------------------------- | +| getMore | 4.2 | +| hello (including legacy hello) | 4.4 (discoverable via topologyVersion) | + +##### sections + +Each message contains one or more sections. A section is composed of an uint8 which determines the payload's type, and a +separate payload field. The payload size for payload type 0 and 1 is determined by the first 4 bytes of the payload +field (includes the 4 bytes holding the size but not the payload type). + +| Field | Description | +| ------- | --------------------------------------------------------------------------------- | +| type | A byte indicating the layout and semantics of payload | +| payload | The payload of a section can either be a single document, or a document sequence. | + +When the Payload Type is 0, the content of the payload is: + +| Field | Description | +| -------- | ---------------------------------------------------------------------------------- | +| document | The BSON document. The payload size is inferred from the document's leading int32. | + +When the Payload Type is 1, the content of the payload is: + +| Field | Description | +| ---------- | -------------------------------------------------------------------------------------------------------------- | +| size | Payload size (includes this 4-byte field) | +| identifier | A unique identifier (for this message). Generally the name of the "command argument" it contains the value for | +| documents | 0 or more BSON documents. Each BSON document cannot be larger than `maxBSONObjectSize`. | + +Any unknown Payload Types MUST result in an error and the socket MUST be closed. There is no ordering implied by payload +types. A section with payload type 1 can be serialized before payload type 0. + +A fully constructed `OP_MSG` MUST contain exactly one `Payload Type 0`, and optionally any number of `Payload Type 1` +where each identifier MUST be unique per message. + +#### Command Arguments As Payload + +Certain commands support "pulling out" certain arguments to the command, and providing them as `Payload Type 1`, where +the `identifier` is the command argument's name. Specifying a command argument as a separate payload removes the need to +use a BSON Array. For example, `Payload Type 1` allows an array of documents to be specified as a sequence of BSON +documents on the wire without the overhead of array keys. + +MongoDB 3.6 only allows certain command arguments to be provided this way. These are: + +| Command Name | Command Argument | +| ------------ | ---------------- | +| insert | documents | +| update | updates | +| delete | deletes | + +#### Global Command Arguments + +The new opcode contains no field for providing the database name. Instead, the protocol now has the concept of global +command arguments. These global command arguments can be passed to all MongoDB commands alongside the rest of the +command arguments. + +Currently defined global arguments: + +| Argument Name | Default Value | Description | +| ----------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| $db | | The database name to execute the command on. MUST be provided and be a valid database name. | +| `$readPreference` | `{ "mode": "primary" }` | Determines server selection, and also whether a secondary server permits reads or responds "not writable primary". See Server Selection Spec for rules about when read preference must or must not be included, and for rules about when read preference "primaryPreferred" must be added automatically. | + +Additional global arguments are likely to be introduced in the future and defined in their own specs. + +### User originating commands + +Drivers MUST NOT mutate user provided command documents in any way, whether it is adding required arguments, pulling out +arguments, compressing it, adding supplemental APM data or any other modification. + +### Examples + +#### Command Arguments As Payload Examples + +For example, an insert can be represented like: + +```javascript +{ + "insert": "collectionName", + "documents": [ + {"_id": "Document#1", "example": 1}, + {"_id": "Document#2", "example": 2}, + {"_id": "Document#3", "example": 3} + ], + "writeConcern": { w: "majority" } +} +``` + +Or, pulling out the `"documents"` argument out of the command document and Into `Payload Type 1`. The `Payload Type 0` +would then be: + +```javascript +{ + "insert": "collectionName", + "$db": "databaseName", + "writeConcern": { w: "majority" } +} +``` + +And `Payload Type 1`: + +```yaml +identifier: "documents" +documents: {"_id": "Document#1", "example": 1}{"_id": "Document#2", "example": 2}{"_id": "Document#3", "example": 3} +``` + +Note that the BSON documents are placed immediately after each other, not with any separator. The writeConcern is also +left intact as a command argument in the `Payload Type 0` section. The command name MUST continue to be the first key of +the command arguments in the `Payload Type 0` section. + +______________________________________________________________________ + +An update can for example be represented like: + +```javascript +{ + "update": "collectionName", + "updates": [ + { + "q": {"example": 1}, + "u": { "$set": { "example": 4} } + }, + { + "q": {"example": 2}, + "u": { "$set": { "example": 5} } + } + ] +} +``` + +Or, pulling out the `"update"` argument out of the command document and Into `Payload Type 1`. The `Payload Type 0` +would then be: + +```javascript +{ + "update": "collectionName", + "$db": "databaseName" +} +``` + +And `Payload Type 1`: + +```yaml +identifier: updates +documents: {"q": {"example": 1}, "u": { "$set": { "example": 4}}}{"q": {"example": 2}, "u": { "$set": { "example": 5}}} +``` + +Note that the BSON documents are placed immediately after each other, not with any separator. + +______________________________________________________________________ + +A delete can for example be represented like: + +```javascript +{ + "delete": "collectionName", + "deletes": [ + { + "q": {"example": 3}, + "limit": 1 + }, + { + "q": {"example": 4}, + "limit": 1 + } + ] +} +``` + +Or, pulling out the `"deletes"` argument out of the command document and into `Payload Type 1`. The `Payload Type 0` +would then be: + +```javascript +{ + "delete": "collectionName", + "$db": "databaseName" +} +``` + +And `Payload Type 1`: + +```yaml +identifier: delete +documents: {"q": {"example": 3}, "limit": 1}{"q": {"example": 4}, "limit": 1} +``` + +Note that the BSON documents are placed immediately after each other, not with any separator. + +### Test Plan + +- Create a single document and insert it over `OP_MSG`, ensure it works +- Create two documents and insert them over `OP_MSG`, ensure each document is pulled out and presented as document + sequence. +- hello.maxWriteBatchSize might change and be bumped to 100,000 +- Repeat the previous 5 tests as updates, and then deletes. +- Create one small document, and one large 16mb document. Ensure they are inserted, updated and deleted in one + roundtrip. + +### Motivation For Change + +MongoDB clients are currently required to work around various issues that each current opcode has, such as having to +determine what sort of node is on the other end as it affects the actual structure of certain messages. MongoDB 3.6 +introduces a new wire protocol opcode, `OP_MSG`, which aims to resolve most historical issues along with providing a +future compatible and extendable opcode. + +### Backwards Compatibility + +The hello.maxWriteBatchSize is being bumped, which also affects `OP_QUERY`, not only `OP_MSG`. As a sideeffect, write +errors will now have the message truncated, instead of overflowing the maxMessageSize, if the server determines it would +overflow the allowed size. This applies to all commands that write. The error documents are structurally the same, with +the error messages simply replaced with empty strings. + +### Reference Implementations + +- mongoc +- .net + +### Future Work + +In the near future, this opcode is expected to be extended and include support for: + +- Message checksum (crc32c) +- Output document sequences +- `moreToCome` can also be used for other commands, such as `killCursors` to restore `OP_KILL_CURSORS` behaviour as + currently any errors/replies are ignored. + +### Q & A + +- Has the maximum number of documents per batch changed ? + + - The maximum number of documents per batch is dictated by the `maxWriteBatchSize` value returned during the MongoDB + Handshake. It is likely this value will be bumped from 1,000 to 100,000. + +- Has the maximum size of the message changed? + + - No. The maximum message size is still the `maxMessageSizeBytes` value returned during the MongoDB Handshake. + +- Is everything still little-endian? + + - Yes. As with BSON, all MongoDB opcodes must be serialized in little-endian format. + +- How does fire-and-forget (w=0 / unacknowledged write) work over `OP_MSG`? + + - The client sets the `moreToCome` flag on the request. The server will not send a response to such requests. + - Malformed operation or errors such as duplicate key errors are not discoverable and will be swallowed by the server. + - Write errors due to not-primary will close the connection, which clients will pickup on next time it uses the + connection. This means at least one unacknowledged write operation will be lost as the client does not discover + the failover until next time the socket is used. + +- Should we provide `runMoreToComeCommand()` helpers? Since the protocol allows any command to be tagged with + `moreToCome`, effectively allowing any operation to become `fire & forget`, it might be a good idea to add such + helper, rather then adding wire protocol headers as options to the existing `runCommand` helpers. + +### Changelog + +- 2026-06-05: Use OP_MSG for all messages. +- 2024-04-30: Convert from RestructuredText to Markdown. +- 2022-10-05: Remove spec front matter. +- 2022-01-13: Clarify that `OP_MSG` must be used when using stable API +- 2021-12-16: Clarify that old drivers should default to OP_QUERY handshakes +- 2021-04-20: Suggest using OP_MSG for initial handshake when using stable API +- 2021-04-06: Updated to use hello and not writable primary +- 2017-11-12: Specify read preferences for OP_MSG with direct connection +- 2017-08-17: Added the `User originating command` section +- 2017-07-18: Published initial version diff --git a/source/message/OP_MSG.rst b/source/message/OP_MSG.rst index 59bb90826d..d9dc0ca03b 100644 --- a/source/message/OP_MSG.rst +++ b/source/message/OP_MSG.rst @@ -1,580 +1,4 @@ -====== -OP_MSG -====== - -:Title: One opcode to rule them all -:Author: Hannes Magnússon -:Advisory Group: \A. Jesse Jiryu Davis, Jeremy Mikola, Mathias Stearn, Robert Stam -:Approvers: Bernie Hackett, David Golden, Jeff Yemin, Matt broadstone -:Informed: Bryan Reinero, Chris Hendel, drivers@ -:Status: Approved -:Type: Standards -:Last Modified: 2017-11-12 -:Minimum Server Version: 3.6 -:Version: 1.1 - - - -.. contents:: - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -Abstract -======== - -``OP_MSG`` is a bi-directional wire protocol opcode introduced in MongoDB 3.6 -with the goal of replacing most existing opcodes, merging their use into one -extendable opcode. - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - - - - - -Specification -============= - - -Usage ------ - -``OP_MSG`` is only available in MongoDB 3.6 (``maxWireVersion >= 6``) and later. -MongoDB drivers MUST continue to perform the MongoDB Handshake using ``OP_QUERY`` -to determine if the node supports ``OP_MSG``. - -If the node supports ``OP_MSG``, any and all messages MUST use ``OP_MSG``, -optionally compressed with ``OP_COMPRESSED``. - - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -OP_MSG ------- - -Types used in this document - -============= ============================================================= -Type Meaning -============= ============================================================= -document A BSON document -------------- ------------------------------------------------------------- -cstring NULL terminated string -------------- ------------------------------------------------------------- -int32 4 bytes (32-bit signed integer, two's complement) -------------- ------------------------------------------------------------- -uint8 1 byte (8-bit unsigned integer) -------------- ------------------------------------------------------------- -uint32 4 bytes (32-bit unsigned integer) -------------- ------------------------------------------------------------- -union One of the listed members -============= ============================================================= - -Elements inside brackets (``[`` ``]``) are optional parts of the message. - -* Zero or more instances: ``*`` -* One or more instances: ``+`` - -The new opcode, called ``OP_MSG``, has the following structure: - -.. code:: c - - struct Section { - uint8 payloadType; - union payload { - document document; // payloadType == 0 - struct sequence { // payloadType == 1 - int32 size; - cstring identifier; - document* documents; - }; - }; - }; - - struct OP_MSG { - struct MsgHeader { - int32 messageLength; - int32 requestID; - int32 responseTo; - int32 opCode = 2013; - }; - uint32 flagBits; - Section+ sections; - [uint32 checksum;] - }; - - -Each ``OP_MSG`` MUST NOT exceed the ``maxMessageSizeBytes`` as configured by -the MongoDB Handshake. - -Each ``OP_MSG`` MUST have one section with ``Payload Type 0``, and zero or more -``Payload Type 1``. Bulk writes SHOULD use ``Payload Type 1``, and MUST do so -when the batch contains more than one entry. - -Sections may exist in any order. Each ``OP_MSG`` MAY contain a checksum, and -MUST set the relevant `flagBits` when that field is included. - - -==================== ======================================================== -Field Description -==================== ======================================================== -flagBits Network level flags, such as signaling recipient that - another message is incoming without any other actions - in the meantime, and availability of message checksums --------------------- -------------------------------------------------------- -sections An array of one or more sections --------------------- -------------------------------------------------------- -checksum crc32c message checksum. When present, the appropriate - flag MUST be set in the flagBits. -==================== ======================================================== - - - - - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -flagBits -~~~~~~~~ - -flagBits contains a bit vector of specialized network flags. The low 16 bits -declare what the current message contains, and what the expectations of the -recipient are. The high 16 bits are designed to declare optional attributes of -the current message and expectations of the recipient. - -All unused bits MUST be set to 0. - -Clients MUST error if any unsupported or undefined required bits are set to 1 -and MUST ignore all undefined optional bits. - -The currently defined flags are: - -===== ==================== ========= ========== =========================== -Bit Name Request Response Description ------ -------------------- --------- ---------- --------------------------- -0 checksumPresent x x Checksum present ------ -------------------- --------- ---------- --------------------------- -1 moreToCome x Sender will send another - message and is not prepared - for overlapping messages -===== ==================== ========= ========== =========================== - - -checksumPresent -~~~~~~~~~~~~~~~ - -This is a reserved field for future support of crc32c checksums. - - -moreToCome -~~~~~~~~~~ - -The ``OP_MSG`` message is essentially a request-response protocol, one message -per turn. However, setting the ``moreToCome`` flag indicates to the recipient that -the sender is not ready to give up his turn and will send another message. - -When the ``moreToCome`` flag is set on a request it signals to the recipient that -the sender does not want to know the outcome of the message. There is no -response to a request where ``moreToCome`` has been set. Clients doing -unacknowledged writes MUST set the ``moreToCome`` flag, and MUST set the -writeConcern to ``w=0``. - -If, during the processing of a ``moreToCome`` flagged write request, a server -discovers that it is no longer primary, then the server will close the -connection. All other errors during processing will be silently dropped, and -will not result in the connection being closed. - - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -sections -~~~~~~~~ - -Each message contains one or more sections. A section is composed of an -uint8 which determines the payload's type, and a separate payload field. The -payload size for payload type 0 and 1 is determined by the first 4 bytes of -the payload field (includes the 4 bytes holding the size but not the payload type). - - -========= ================================================================= -Field Description ---------- ----------------------------------------------------------------- -type A byte indicating the layout and semantics of payload ---------- ----------------------------------------------------------------- -payload The payload of a section can either be a single document, or a - document sequence. -========= ================================================================= - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -============ ============================================================== -Field Description -============ ============================================================== -When the Payload Type is 0, the content of the payload is ---------------------------------------------------------------------------- -document The BSON document. The payload size is inferred from the - document's leading int32. ------------- -------------------------------------------------------------- -When the Payload Type is 1, the content of the payload is ---------------------------------------------------------------------------- -size Payload size (includes this 4-byte field) ------------- -------------------------------------------------------------- -identifier A unique identifier (for this message). Generally the name of - the "command argument" it contains the value for ------------- -------------------------------------------------------------- -documents 0 or more BSON documents. Each BSON document cannot be larger - than ``maxBSONObjectSize``. -============ ============================================================== - - -Any unknown Payload Types MUST result in an error and the socket MUST be -closed. There is no ordering implied by payload types. A section with payload -type 1 can be serialized before payload type 0. - -A fully constructed ``OP_MSG`` MUST contain exactly one ``Payload Type 0``, and -optionally any number of ``Payload Type 1`` where each identifier MUST be -unique per message. - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -Command Arguments As Payload ----------------------------- - -Certain commands support "pulling out" certain arguments to the command, and -providing them as ``Payload Type 1``, where the `identifier` is the command -argument’s name. -Specifying a command argument as a separate payload removes the need to use a -BSON Array. For example, ``Payload Type 1`` allows an array of documents to be -specified as a sequence of BSON documents on the wire without the overhead of -array keys. - -MongoDB 3.6 only allows certain command arguments to be provided this way. -These are: - -============== ============================================================ -Command Name Command Argument -============== ============================================================ -insert documents --------------- ------------------------------------------------------------ -update updates --------------- ------------------------------------------------------------ -delete deletes -============== ============================================================ - - - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -Global Command Arguments ------------------------- - -The new opcode contains no field for providing the database name. Instead, the -protocol now has the concept of global command arguments. -These global command arguments can be passed to all MongoDB commands alongside -the rest of the command arguments. - -Currently defined global arguments: - -=============== ========================= ================================= -Argument Name Default Value Description -=============== ========================= ================================= -$db The database name to execute the - command on. MUST be provided and - be a valid database name. ---------------- ------------------------- --------------------------------- -$readPreference ``{ "mode": "primary" }`` Determines server selection, and - also whether a secondary server - permits reads or responds "not - master". See Server Selection Spec - for rules about when read preference - must or must not be included, and for - rules about when read preference - "primaryPreferred" must be added - automatically. -=============== ========================= ================================= - -Additional global arguments are likely to be introduced in the future and -defined in their own specs. - - - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -User originating commands -========================= - -Drivers MUST NOT mutate user provided command documents in any way, whether it -is adding required arguments, pulling out arguments, compressing it, adding -supplemental APM data or any other modification. - -.. This RST artwork improves the readability of the rendered document - -| -| -| - -Examples -======== - -Command Arguments As Payload Examples -------------------------------------- - -For example, an insert can be represented like:: - - { - "insert": "collectionName", - "documents": [ - {"_id": "Document#1", "example": 1}, - {"_id": "Document#2", "example": 2}, - {"_id": "Document#3", "example": 3} - ], - "writeConcern": { w: "majority" } - } - - -Or, pulling out the ``"documents"`` argument out of the command document and -Into ``Payload Type 1``. -The ``Payload Type 0`` would then be:: - - { - "insert": "collectionName", - "$db": "databaseName", - "writeConcern": { w: "majority" } - } - - -And ``Payload Type 1``:: - - identifier: "documents" - documents: {"_id": "Document#1", "example": 1}{"_id": "Document#2", "example": 2}{"_id": "Document#3", "example": 3} - - -Note that the BSON documents are placed immediately after each other, not with -any separator. The writeConcern is also left intact as a command argument in -the ``Payload Type 0`` section. -The command name MUST continue to be the first key of the command arguments in -the ``Payload Type 0`` section. - ----- - -An update can for example be represented like:: - - { - "update": "collectionName", - "updates": [ - { - "q": {"example": 1}, - "u": { "$set": { "example": 4} } - }, - { - "q": {"example": 2}, - "u": { "$set": { "example": 5} } - } - ] - } - - - -Or, pulling out the ``"update"`` argument out of the command document and -Into ``Payload Type 1``. -The ``Payload Type 0`` would then be:: - - - { - "update": "collectionName", - "$db": "databaseName" - } - -And ``Payload Type 1``:: - - identifier: updates - documents: {"q": {"example": 1}, "u": { "$set": { "example": 4}}}{"q": {"example": 2}, "u": { "$set": { "example": 5}}} - - -Note that the BSON documents are placed immediately after each other, not -with any separator. - ----- - -A delete can for example be represented like:: - - { - "delete": "collectionName", - "deletes": [ - { - "q": {"example": 3}, - "limit": 1 - }, - { - "q": {"example": 4}, - "limit": 1 - } - ] - } - -Or, pulling out the ``"deletes"`` argument out of the command document and into -``Payload Type 1``. -The ``Payload Type 0`` would then be:: - - { - "delete": "collectionName", - "$db": "databaseName" - } - -And ``Payload Type 1``:: - - identifier: delete - documents: {"q": {"example": 3}, "limit": 1}{"q": {"example": 4}, "limit": 1} - - -Note that the BSON documents are placed immediately after each other, not with any separator. - - - -Test Plan -========= -- Create a single document and insert it over ``OP_MSG``, ensure it works -- Create two documents and insert them over ``OP_MSG``, ensure each document is - pulled out and presented as document sequence. -- ismaster.maxWriteBatchSize might change and be bumped to 100,000 -- Repeat the previous 5 tests as updates, and then deletes. -- Create one small document, and one large 16mb document. Ensure they are - inserted, updated and deleted in one roundtrip. - - - -Motivation For Change -===================== - -MongoDB clients are currently required to work around various issues that -each current opcode has, such as having to determine what sort of node is on -the other end as it affects the actual structure of certain messages. -MongoDB 3.6 introduces a new wire protocol opcode, ``OP_MSG``, which aims to -resolve most historical issues along with providing a future compatible and -extendable opcode. - - -Backwards Compatibility -======================= - - -The isMaster.maxWriteBatchSize is being bumped, which also affects ``OP_QUERY``, -not only ``OP_MSG``. As a sideeffect, write errors will now have the message -truncated, instead of overflowing the maxMessageSize, if the server determines -it would overflow the allowed size. This applies to all commands that write. -The error documents are structurally the same, with the error messages simply -replaced with empty strings. - - -Reference Implementations -========================= - -- mongoc -- .net - -Future Work -=========== - - -In the near future, this opcode is expected to be extended and include support for: - -* exhausting docs (reading documents from the server without getmore) with - ``moreToCome`` -* Message checksum (crc32c) -* Output document sequences -* Similarly, certain commands will reply to messages using this technique when - possible to avoid the overhead of BSON Arrays. Drivers will be required to - allow all command replies to use this technique. Drivers will be required to - handle ``Payload Type 1``. -* ``moreToCome`` can also be used for other commands, such as ``killCursors`` to - restore ``OP_KILL_CURSORS`` behaviour as currently any errors/replies are ignored. - - - -Q & A -===== - -* Has the maximum number of documents per batch changed ? - * The maximum number of documents per batch is dictated by the - ``maxWriteBatchSize`` value returned during the MongoDB Handshake. It is - likely this value will be bumped from 1,000 to 100,000. -* Has the maximum size of the message changed? - * No. The maximum message size is still the ``maxMessageSizeBytes`` value - returned during the MongoDB Handshake. -* Is everything still little-endian? - * Yes. As with BSON, all MongoDB opcodes must be serialized in - little-endian format. -* How does fire-and-forget (w=0 / unacknowledged write) work over ``OP_MSG``? - * The client sets the ``moreToCome`` flag on the request. The server will - not send a response to such requests. - * Malformed operation or errors such as duplicate key errors are - not discoverable and will be swallowed by the server. - * Write errors due to not-primary will close the connection, which clients - will pickup on next time it uses the connection. This means at least one - unacknowledged write operation will be lost as the client does not - discover the failover until next time the socket is used. -* Will the ``moreToCome`` flag ever be used for responses? - The ``moreToCome`` flag will also be sent on response, where the sender signals to - the recipient that more messages are coming and the recipient must wait its - turn until all messages have arrived (e.g. ``moreToCome`` no longer set). This - in practice achieves "exhaust" functionality. Note that MongoDB 3.6 does not - currently implement this flag on responses, but may in the future. -* Should we provide ``runMoreToComeCommand()`` helpers? - Since the protocol allows any command to be tagged with ``moreToCome``, effectively - allowing any operation to become ``fire & forget``, it might be a good idea - to add such helper, rather then adding wire protocol headers as options to the - existing ``runCommand`` helpers. - - - - -Changelog -========= - -- 2017-11-12 Specify read preferences for OP_MSG with direct connection -- 2017-08-17 Added the ``User originating command`` section -- 2017-07-18 Published 1.0.0 +.. note:: + This specification has been converted to Markdown and renamed to + `OP_MSG.md `_. diff --git a/source/mongodb-handshake/handshake.md b/source/mongodb-handshake/handshake.md new file mode 100644 index 0000000000..e8d39bf6b9 --- /dev/null +++ b/source/mongodb-handshake/handshake.md @@ -0,0 +1,584 @@ +# MongoDB Handshake + +- Status: Accepted +- Minimum Server Version: 3.4 + +______________________________________________________________________ + +## Abstract + +MongoDB 3.4 has the ability to annotate connections with metadata provided by the connecting client. The intent of this +metadata is to be able to identify client level information about the connection, such as application name, driver name +and version. The provided information will be logged through the `mongo[d|s].log` and the profile logs; this should +enable sysadmins to easily backtrack log entries the offending application. The active connection data will also be +queryable through aggregation pipeline, to enable collecting and analyzing driver trends. + +After connecting to a MongoDB node a hello command (if Stable API is requested) or a legacy hello command is issued, +followed by authentication, if appropriate. This specification augments this handshake and defines certain arguments +that clients provide as part of the handshake. + +This spec furthermore adds a new connection string argument for applications to declare its application name to the +server. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Terms + +**hello command** + +The command named `hello`. It is the preferred and modern command for handshakes and topology monitoring. + +**legacy hello command** + +The command named `isMaster`. It is the deprecated equivalent of the `hello` command. It was deprecated in MongoDB 5.0. + +**isMaster / ismaster** + +The correct casing is `isMaster`, but servers will accept the alternate casing `ismaster`. Other case variations result +in `CommandNotFound`. Drivers MUST take this case variation into account when determining which commands to encrypt, +redact, or otherwise treat specially. + +## Specification + +### Connection handshake + +MongoDB uses the `hello` or `isMaster` commands for handshakes and topology monitoring. `hello` is the modern and +preferred command. `hello` must always be sent using the `OP_MSG` protocol. `isMaster` is referred to as "legacy hello" +and is maintained for backwards compatibility with servers that do not support the `hello` command. + +Drivers MUST use the `OP_MSG` protocol for all handshakes if their minWireVersion is 6 (MongoDB 3.6) or higher. If a +[server API version](../versioned-api/versioned-api.md) is requested or `loadBalanced: True`, drivers MUST also use the +`hello` command for the initial handshake. If server API version is not requested and `loadBalanced: False`, drivers +MUST use legacy hello for the first message of the initial handshake, and include `helloOk:true` in the handshake +request. + +If the legacy handshake response includes `helloOk: true`, then subsequent topology monitoring commands MUST use the +`hello` command. If the legacy handshake response does not include `helloOk: true`, then subsequent topology monitoring +commands MUST use the legacy hello command. Additionally, note that if the server does not understand `OP_MSG`, the +server will close the socket. See the Server Discovery and Monitoring spec +[here](https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.md#streaming-hello-or-legacy-hello) +and +[here](https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.md#error-handling) +for further information. + +The initial handshake MUST be performed on every socket to any and all servers upon establishing the connection to +MongoDB, including reconnects of dropped connections and newly discovered members of a cluster. It MUST be the first +command sent over the respective socket. If the command fails the client MUST disconnect. Timeouts MUST be applied to +this command per the +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) specification. + +`hello` and legacy hello commands issued after the initial connection handshake MUST NOT contain handshake arguments. +Any subsequent `hello` or legacy hello calls, such as the ones for topology monitoring purposes, MUST NOT include this +argument. + +#### Example Implementation + +Consider the following pseudo-code for establishing a new connection: + +```python +conn = Connection() +conn.connect() # Connect via TCP / TLS +if stable_api_configured or client_options.load_balanced: + cmd = {"hello": 1} +else: + cmd = {"legacy hello": 1, "helloOk": 1} +cmd["backpressure"] = True +cmd["client"] = client_metadata +if client_options.compressors: + cmd["compression"] = client_options.compressors +if client_options.load_balanced: + cmd["loadBalanced"] = True +creds = client_options.credentials +if creds: + # Negotiate auth mechanism and perform speculative auth. See Auth spec for details. + if not creds.has_mechanism_configured(): + cmd["saslSupportedMechs"] = ... + cmd["speculativeAuthenticate"] = ... + +reply = conn.send_command("admin", cmd) + +# Store the negotiated compressor, see OP_COMPRESSED spec. +if reply.get("compression"): + conn.compressor = reply["compression"][0] + +# Perform connection authentication. See Auth spec for details. +negotiated_mechs = reply.get("saslSupportedMechs") +speculative_auth = reply.get("speculativeAuthenticate") +conn.authenticate(creds, negotiated_mechs, speculative_auth) +``` + +#### Hello Command + +The initial handshake, as of MongoDB 3.4, supports a new argument, `client`, provided as a BSON object. This object has +the following structure: + +```javascript + { + hello: 1, + helloOk: true, + client: { + /* OPTIONAL. If present, the "name" is REQUIRED */ + application: { + name: "" + }, + /* REQUIRED, including all sub fields */ + driver: { + name: "", + version: "" + }, + /* REQUIRED */ + os: { + type: "", /* REQUIRED */ + name: "", /* OPTIONAL */ + architecture: "", /* OPTIONAL */ + version: "" /* OPTIONAL */ + }, + /* OPTIONAL */ + platform: "", + /* OPTIONAL */ + env: { + name: "", /* OPTIONAL */ + timeout_sec: 42, /* OPTIONAL */ + memory_mb: 1024, /* OPTIONAL */ + region: "", /* OPTIONAL */ + /* OPTIONAL */ + container: { + runtime: "", /* OPTIONAL */ + orchestrator: "" /* OPTIONAL */ + } + } + } + } +``` + + + +#### client.application.name + +This value is application configurable. + +The application name is printed to the mongod logs upon establishing the connection. It is also recorded in the slow +query logs and profile collections. + +The recommended way for applications to provide this value is through the connection URI. The connection string key is +`appname`. + +Example connection string: + +```text +mongodb://server:27017/db?appname=mongodump +``` + +This option MAY also be provided on the MongoClient itself, if normal for the driver. It is only valid to set this +attribute before any connection has been made to a server. Any attempt to set `client.application.name` MUST result in +an failure when doing so will either change the existing value, or have any connection to MongoDB reporting inconsistent +values. + +Drivers MUST NOT provide a default value for this key. + +#### client.driver.name + +This value is required and is not application configurable. + +The internal driver name. For drivers written on-top of other core drivers, the `appendMetadata()` method can be used to +add package information to an existing MongoClient. + +Example: + +```text +- "pymongo" +- "mongoc / phongo" +``` + +#### client.driver.version + +This value is required and is not application configurable. + +The internal driver version. The version formatting is not defined. For drivers written on-top of other core drivers, +the `appendMetadata()` method can be used to add package information to an existing MongoClient. + +Example: + +```text +- "1.1.2-beta0" +- "1.4.1 / 1.2.0" +``` + +#### client.os.type + +This value is required and is not application configurable. + +The Operating System primary identification type the client is running on. Equivalent to `uname -s` on POSIX systems. +This field is REQUIRED and clients must default to `unknown` when an appropriate value cannot be determined. + +Example: + +```text +- "Linux" +- "Darwin" +- "Windows" +- "BSD" +- "Unix" +``` + +#### client.os.name + +This value is optional, but RECOMMENDED, it is not application configurable. + +Detailed name of the Operating System's, such as fully qualified distribution name. On systemd systems, this is +typically `PRETTY_NAME` of `os-release(5)` (`/etc/os-release`) or the `DISTRIB_DESCRIPTION` (`/etc/lsb-release`, +`lsb_release(1) --description`) on LSB systems. The exact value and method to determine this value is undefined. + +Example: + +```text +- "Ubuntu 16.04 LTS" +- "macOS" +- "CygWin" +- "FreeBSD" +- "AIX" +``` + +#### client.os.architecture + +This value is optional, but RECOMMENDED, it is not application configurable. The machine hardware name. Equivalent to +`uname -m` on POSIX systems. + +Example: + +```text +- "x86_64" +- "ppc64le" +``` + +#### client.os.version + +This value is optional and is not application configurable. + +The Operating System version. + +Example: + +```text +- "10" +- "8.1" +- "16.04.1" +``` + +#### client.platform + +This value is optional and is not application configurable. + +Driver specific platform details. + +Example: + +```text +- clang 3.8.0 CFLAGS="-mcpu=power8 -mtune=power8 -mcmodel=medium" +- "Oracle JVM EE 9.1.1" +``` + + + +#### client.env + +This value is optional and is not application configurable. + +Information about the execution environment, including Function-as-a-Service (FaaS) identification and container +runtime. + +The contents of `client.env` MUST be adjusted to keep the handshake below the size limit; see +[Limitations](#limitations) for specifics. + +If no fields of `client.env` would be populated, `client.env` MUST be entirely omitted. + +##### FaaS + +FaaS details are captured in the `name`, `timeout_sec`, `memory_mb`, and `region` fields of `client.env`. The `name` +field is determined by which of the following environment variables are populated: + +| Name | Environment Variable | +| ------------ | --------------------------------------------------- | +| `aws.lambda` | `AWS_EXECUTION_ENV`[^1] or `AWS_LAMBDA_RUNTIME_API` | +| `azure.func` | `FUNCTIONS_WORKER_RUNTIME` | +| `gcp.func` | `K_SERVICE` or `FUNCTION_NAME` | +| `vercel` | `VERCEL` | + +If none of those variables are populated the other FaaS values MUST be entirely omitted. When variables for multiple +`client.env.name` values are present, `vercel` takes precedence over `aws.lambda`; any other combination MUST cause the +other FaaS values to be entirely omitted. + +Depending on which `client.env.name` has been selected, other FaaS fields in `client.env` SHOULD be populated: + +| Name | Field | Environment Variable | Expected Type | +| ------------ | ------------------------ | --------------------------------- | ------------- | +| `aws.lambda` | `client.env.region` | `AWS_REGION` | string | +| | `client.env.memory_mb` | `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` | int32 | +| `gcp.func` | `client.env.memory_mb` | `FUNCTION_MEMORY_MB` | int32 | +| | `client.env.timeout_sec` | `FUNCTION_TIMEOUT_SEC` | int32 | +| | `client.env.region` | `FUNCTION_REGION` | string | +| `vercel` | `client.env.region` | `VERCEL_REGION` | string | + +Missing variables or variables with values not matching the expected type MUST cause the corresponding `client.env` +field to be omitted and MUST NOT cause a user-visible error. + +##### Container + +Container runtime information is captured in `client.env.container`. + +`client.env.container.runtime` MUST be set to `"docker"` if the file `.dockerenv` exists in the root directory. + +`client.env.container.orchestrator` MUST be set to `"kubernetes"` if the environment variable `KUBERNETES_SERVICE_HOST` +is populated. + +If no fields of `client.env.container` would be populated, `client.env.container` MUST be entirely omitted. + +If the runtime environment has both FaaS and container information, both must have their metadata included in +`client.env`. + +### Speculative Authentication + +- Since: 4.4 + +The initial handshake supports a new argument, `speculativeAuthenticate`, provided as a BSON document. Clients +specifying this argument to `hello` or legacy hello will speculatively include the first command of an authentication +handshake. This command may be provided to the server in parallel with any standard request for supported authentication +mechanisms (i.e. `saslSupportedMechs`). This would permit clients to merge the contents of their first authentication +command with their initial handshake request, and receive the first authentication reply along with the initial +handshake reply. + +When the mechanism is `MONGODB-X509`, `speculativeAuthenticate` has the same structure as seen in the MONGODB-X509 +conversation section in the [Driver Authentication spec](../auth/auth.md#supported-authentication-methods). + +When the mechanism is `SCRAM-SHA-1` or `SCRAM-SHA-256`, `speculativeAuthenticate` has the same fields as seen in the +conversation subsection of the SCRAM-SHA-1 and SCRAM-SHA-256 sections in the +[Driver Authentication spec](../auth/auth.md#supported-authentication-methods) with an additional `db` field to specify +the name of the authentication database. + +When the mechanism is `MONGODB-OIDC`, `speculativeAuthenticate` has the same structure as seen in the MONGODB-OIDC +conversation section in the [Driver Authentication spec](../auth/auth.md#supported-authentication-methods). + +If the initial handshake command with a `speculativeAuthenticate` argument succeeds, the client should proceed with the +next step of the exchange. If the initial handshake response does not include a `speculativeAuthenticate` reply and the +`ok` field in the initial handshake response is set to 1, drivers MUST authenticate using the standard authentication +handshake. + +The `speculativeAuthenticate` reply has the same fields, except for the `ok` field, as seen in the conversation sections +for MONGODB-X509, SCRAM-SHA-1 and SCRAM-SHA-256 in the +[Driver Authentication spec](../auth/auth.md#supported-authentication-methods). + +Drivers MUST NOT validate the contents of the `saslSupportedMechs` attribute of the initial handshake reply. Drivers +MUST NOT raise an error if the `saslSupportedMechs` attribute of the reply includes an unknown mechanism. + +If an authentication mechanism is not provided either via connection string or code, but a credential is provided, +drivers MUST use the SCRAM-SHA-256 mechanism for speculative authentication and drivers MUST send `saslSupportedMechs`. + +Older servers will ignore the `speculativeAuthenticate` argument. New servers will participate in the standard +authentication conversation if this argument is missing. + +## Supporting Wrapping Libraries + +Drivers MUST allow libraries which wrap the driver to append to the client metadata generated by the driver. The +following class definition defines the options which MUST be supported: + +```typescript +class DriverInfoOptions { + /** + * The name of the library wrapping the driver. + */ + name: String; + + /** + * The version of the library wrapping the driver. + */ + version: Optional; + + /** + * Optional platform information for the wrapping driver. + */ + platform: Optional; +} +``` + +Two `DriverInfoOptions` objects are considered equal if they would result in the same metadata in the client handshake. +In practice, this means if a field is the empty string (`""`), treat it as unset. Assert that each field is strictly +equal with case sensitive string comparison. + +Note that how these options are provided to a driver during `MongoClient` initialization is left up to the implementer. + +### Metadata updates after MongoClient initialization + +Drivers MUST provide an API that allows appending `DriverInfoOptions` to a `MongoClient` instance after initialization, +adhering to the pattern described below while following idioms of the language of the driver: + +```java +interface MongoClient { + void appendMetadata(DriverInfoOptions driverInfoOptions); + // other existing members of MongoClient +} +``` + +After client metadata update, drivers MUST apply updated metadata to newly created connections. Drivers MUST NOT apply +updated metadata to already established connections, create new connections, or close existing connections solely for +the purpose of transferring updated metadata. + +### Appending metadata + +If `DriverInfoOptions` are provided during or after `MongoClient` initialization, these options MUST NOT replace any +existing metadata values, including driver-generated metadata and previously provided options. The provided options MUST +be appended to their respective fields, and be delimited by a `|` character. For example, when +[Motor](https://www.mongodb.com/docs/drivers/motor/) wraps PyMongo, the following fields are updated to include Motor's +"driver info": + +```typescript +{ + client: { + driver: { + name: "PyMongo|Motor", + version: "3.6.0|2.0.0" + } + } +} +``` + +Some client libraries provide APIs that accept a pre-initialized MongoClient as an argument. In these circumstances, it +is possible for multiple library objects to be associated with the same MongoClient, which could result in the same +metadata being appended multiple times. Drivers MUST ensure that any duplicate `DriverInfoOptions` objects provided to a +MongoClient or appended to a MongoClient do not result in additional metadata being appended. See +[Supporting Wrapping Libraries](#supporting-wrapping-libraries). + +**NOTE:** All strings provided as part of the driver info MUST NOT contain the delimiter used for metadata concatention. +Drivers MUST throw an error if any of these strings contains that character. + +### Deviations + +Some drivers have already implemented such functionality, and should not be required to make breaking changes to comply +with the requirements set forth here. A non-exhaustive list of acceptable deviations are as follows: + +- The name of `DriverInfoOptions` is non-normative, implementers may feel free to name this whatever they like. +- The choice of delimiter is not fixed, `|` is the recommended value, but some drivers currently use `/`. +- For cases where we own a particular stack of drivers (more than two), it may be preferable to accept a *list* of + strings for each field. + +## Limitations + +The entire `client` metadata BSON document MUST NOT exceed 512 bytes. This includes all BSON overhead. The +`client.application.name` cannot exceed 128 bytes. MongoDB will return an error if these limits are not adhered to, +which will result in handshake failure. Drivers MUST validate these values and truncate or omit driver provided values +if necessary. Implementers SHOULD cumulatively update fields in the following order until the document is under the size +limit: + +1. Omit fields from `env` except `env.name`. +2. Omit fields from `os` except `os.type`. +3. Omit the `env` document entirely. +4. Truncate `platform`. + +Additionally, implementers are encouraged to place high priority information about the platform earlier in the string, +in order to avoid possible truncating of those details. + +## Motivation For Change + +Being able to annotate individual connections with custom data will allow users and sysadmins to easily correlate events +happening on their MongoDB deployment to a specific application. For support engineers, it furthermore helps identify +potential problems in drivers or client platforms, and paves the way for providing proactive support via Cloud Manager +and/or Atlas to advise customers about out of date driver versions. + +## Design Rationale + +Drivers run on a multitude of platforms, languages, environments and systems. There is no defined list of data points +that may or may not be valuable to every system. Rather than specifying such a list it was decided we would report the +basics; something that everyone can discover and consider valuable. The obvious requirement here being the driver itself +and its version. Any additional information is generally very system specific. Scala may care to know the Java runtime, +while Python would like to know if it was built with C extensions - and C would like to know the compiler. + +Having to define dozens of arguments that may or may not be useful to one or two drivers isn't a good idea. Instead, we +define a `platform` argument that is driver dependent. This value will not have defined value across drivers and is +therefore not generically queryable -- however, it will gain defined schema for that particular driver, and will +therefore over time gain defined structure that can be formatted and value extracted from. + +## Backwards Compatibility + +The legacy hello command currently ignores arguments. (i.e. If arguments are provided the legacy hello command discards +them without erroring out). Adding client metadata functionality has therefore no backwards compatibility concerns. + +This also allows a driver to determine if the `hello` command is supported. On server versions that support the `hello` +command, the legacy hello command with `helloOk: true` will respond with `helloOk: true`. On server versions that do not +support the `hello` command, the `helloOk: true` argument is ignored and the legacy hello response will not contain +`helloOk: true`. + +## Reference Implementation + +[C Driver](https://github.com/mongodb/mongo-c-driver/blob/master/src/libmongoc/src/mongoc/mongoc-handshake.c). + +## Q&A + +- The 128 bytes application.name limit, does that include BSON overhead + + - No, just the string itself + +- The 512 bytes limit, does that include BSON overhead? + + - Yes + +- The 512 bytes limit, does it apply to the full initial handshake document or just the `client` subdocument + + - Just the subdocument + +- Should I really try to fill the 512 bytes with data? + + - Not really. The server does not attempt to normalize or compress this data in anyway, so it will hold it in memory + as-is per connection. 512 bytes for 20,000 connections is ~ 10mb of memory the server will need. + +- What happens if I pass new arguments in the legacy hello command to previous MongoDB versions? + + - Nothing. Arguments passed to the legacy hello command to prior versions of MongoDB are not treated in any special + way and have no effect one way or another. + +- Are there wire version bumps or anything accompanying this specification? + + - No + +- Is establishing the handshake required for connecting to MongoDB 3.4? + + - No, it only augments the connection. MongoDB will not reject connections without it + +- Does this affect SDAM implementations? + + - Possibly. There are a couple of gotchas. If the application.name is not in the URI... + - The SDAM monitoring cannot be launched until the user has had the ability to set the application name because the + application name has to be sent in the initial handshake. This means that the connection pool cannot be + established until the first user initiated command, or else some connections will have the application name + while other won't + - The initial handshake must be called on all sockets, including administrative background sockets to MongoDB + +- My language doesn't have `uname`, but does instead provide its own variation of these values, is that OK? + + - Absolutely. As long as the value is identifiable it is fine. The exact method and values are undefined by this + specification + +## Changelog + +- 2026-06-11: Clarify that there is no new behavior as a result of only using OP_MSG for all handshakes. +- 2026-06-05: Use OP_MSG for all handshakes. +- 2025-09-04: Clarify that drivers do not append the same metadata multiple times. +- 2025-06-09: Add requirement to allow appending to client metadata after `MongoClient` initialization. +- 2024-11-05: Move handshake prose tests from spec file to prose test file. +- 2024-10-09: Clarify that FaaS and container metadata must both be populated when both are present. +- 2024-08-16: Migrated from reStructuredText to Markdown. +- 2019-11-13: Added section about supporting wrapping libraries +- 2020-02-12: Added section about speculative authentication +- 2021-04-27: Updated to define `hello` and legacy hello +- 2022-01-13: Updated to disallow `hello` using `OP_QUERY` +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec. +- 2022-02-24: Rename Versioned API to Stable API +- 2022-10-05: Remove spec front matter and reformat changelog. +- 2023-03-13: Add `env` to `client` document +- 2023-04-03: Simplify truncation for metadata +- 2023-05-04: `AWS_EXECUTION_ENV` must start with `"AWS_Lambda_"` +- 2023-08-24: Added container awareness +- 2024-04-22: Clarify that driver should not validate `saslSupportedMechs` content. + +[^1]: `AWS_EXECUTION_ENV` must start with the string `"AWS_Lambda_"`. diff --git a/source/mongodb-handshake/handshake.rst b/source/mongodb-handshake/handshake.rst index 5e8383c11d..3146eef8f1 100644 --- a/source/mongodb-handshake/handshake.rst +++ b/source/mongodb-handshake/handshake.rst @@ -1,318 +1,4 @@ -================= -MongoDB Handshake -================= - -:Spec Title: MongoDB Handshake -:Spec Version: 1.0.0 -:Author: Hannes Magnusson -:Kernel Advisory: Mark Benvenuto -:Driver Advisory: Anna Herlihy, Justin Lee -:Status: Approved -:Type: Standards -:Minimum Server Version: 3.4 -:Last Modified: 2016-07-29 - - -.. contents:: - --------- - - -Abstract -======== - -MongoDB 3.4 has the ability to annotate connections with metadata provided by -the connecting client. The intent of this metadata is to be able to identify -client level information about the connection, such as application name, driver -name and version. The provided information will be logged through the -mongo[d|s].log and the profile logs; this should enable sysadmins to easily -backtrack log entries the offending application. The active connection data -will also be queryable through aggregation pipeline, to enable collecting and -analyzing driver trends. - -After connecting to a MongoDB node an isMaster command is issued, followed by -authentication, if appropriate. This specification augments this handshake and -defines certain arguments that clients provide as part of the handshake. - -This spec furthermore adds a new connection string argument for applications to -declare its application name to the server. - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - - -Specification -============= - --------------------- -Connection handshake --------------------- - -The ``isMaster`` handshake MUST be performed on every socket to any and all servers -upon establishing the connection to MongoDB, including reconnects of dropped -connections and newly discovered members of a cluster. It MUST be the first -command sent over the respective socket. If the command fails the client MUST -disconnect. - -``isMaster`` commands issued after the initial connection handshake MUST NOT -contain handshake arguments. Any subsequent ``isMaster`` calls, such as the ones -for topology monitoring purposes, MUST NOT include this argument. - - -The ``isMaster`` handshake, as of MongoDB 3.4, supports a new argument, ``client``, -provided as a BSON object. This object has the following structure:: - - { - isMaster: 1, - client: { - /* OPTIONAL. If present, the "name" is REQUIRED */ - application: { - name: "" - }, - /* REQUIRED, including all sub fields */ - driver: { - name: "", - version: "" - }, - /* REQUIRED */ - os: { - type: "", /* REQUIRED */ - name: "", /* OPTIONAL */ - architecture: "", /* OPTIONAL */ - version: "" /* OPTIONAL */ - }, - /* OPTIONAL */ - platform: "" - } - } - - - - -client.application.name -~~~~~~~~~~~~~~~~~~~~~~~ - -This value is application configurable. - -The application name is printed to the mongod logs upon establishing the -connection. It is also recorded in the slow query logs and profile collections. - -The recommended way for applications to provide this value is through the -connection URI. The connection string key is ``appname``. - -Example connection string:: - - mongodb://server:27017/db?appname=mongodump - -This option MAY also be provided on the MongoClient itself, if normal for the -driver. It is only valid to set this attribute before any connection has been -made to a server. Any attempt to set ``client.application.name`` MUST result in an -failure when doing so will either change the existing value, or have any -connection to MongoDB reporting inconsistent values. - -Drivers MUST NOT provide a default value for this key. - - -client.driver.name -~~~~~~~~~~~~~~~~~~ - -This value is required and is not application configurable. - -The internal driver name. For drivers written on-top of other core drivers, the -underlying driver will typically expose a function to append additional name to -this field. - -Example:: - - - "pymongo" - - "mongoc / phongo" - - -client.driver.version -~~~~~~~~~~~~~~~~~~~~~ - -This value is required and is not application configurable. - -The internal driver version. The version formatting is not defined. For drivers -written on-top of other core drivers, the underlying driver will typically -expose a function to append additional name to this field. - -Example:: - - - "1.1.2-beta0" - - "1.4.1 / 1.2.0" - - -client.os.type -~~~~~~~~~~~~~~ - -This value is required and is not application configurable. - -The Operating System primary identification type the client is running on. -Equivalent to ``uname -s`` on POSIX systems. This field is REQUIRED and clients -must default to ``unknown`` when an appropriate value cannot be determined. - -Example:: - - - "Linux" - - "Darwin" - - "Windows" - - "BSD" - - "Unix" - - -client.os.name -~~~~~~~~~~~~~~ - -This value is optional, but RECOMMENDED, it is not application configurable. - -Detailed name of the Operating System’s, such as fully qualified distribution -name. On systemd systems, this is typically ``PRETTY_NAME`` of ``os-release(5)`` -(``/etc/os-release``) or the ``DISTRIB_DESCRIPTION`` (``/etc/lsb-release``, -``lsb_release(1) --description``) on LSB systems. The exact value and method to -determine this value is undefined. - -Example:: - - - "Ubuntu 16.04 LTS" - - "macOS" - - "CygWin" - - "FreeBSD" - - "AIX" - - -client.os.architecture -~~~~~~~~~~~~~~~~~~~~~~ - -This value is optional, but RECOMMENDED, it is not application configurable. -The machine hardware name. Equivalent to ``uname -m`` on POSIX systems. - -Example:: - - - "x86_64" - - "ppc64le" - - -client.os.version -~~~~~~~~~~~~~~~~~ - -This value is optional and is not application configurable. - -The Operating System version. - -Example:: - - - "10" - - "8.1" - - "16.04.1" - - -client.platform -~~~~~~~~~~~~~~~ - -This value is optional and is not application configurable. - -Driver specific platform details. - -Example:: - - - clang 3.8.0 CFLAGS="-mcpu=power8 -mtune=power8 -mcmodel=medium" - - "Oracle JVM EE 9.1.1" - - -Limitations -=========== - -The entire metadata BSON document MUST NOT exceed 512 bytes. This includes all -BSON overhead. The ``client.application.name`` cannot exceed 128 bytes. MongoDB -will return an error if these limits are not adhered to, which will result in -handshake failure. Drivers MUST validate these values and truncate driver -provided values if necessary. - - - - - - - -Test Plan -========= - -Unknown. A set of YAML tests for the connection uri. It’ll implicitly test the -other fields being provided. - -Motivation For Change -===================== - -Being able to annotate individual connections with custom data will allow users -and sysadmins to easily correlate events happening on their MongoDB deployment -to a specific application. For support engineers, it furthermore helps identify -potential problems in drivers or client platforms, and paves the way for -providing proactive support via Cloud Manager and/or Atlas to advise customers -about out of date driver versions. - - -Design Rationale -================ - -Drivers run on a multitude of platforms, languages, environments and systems. -There is no defined list of data points that may or may not be valuable to -every system. Rather than specifying such a list it was decided we would report -the basics; something that everyone can discover and consider valuable. The -obvious requirement here being the driver itself and its version. Any -additional information is generally very system specific. Scala may care to -know the Java runtime, while Python would like to know if it was built with C -extensions - and C would like to know the compiler. - -Having to define dozens of arguments that may or may not be useful to one or -two drivers isn’t a good idea. Instead, we define a ``platform`` argument that is -driver dependent. This value will not have defined value across drivers and is -therefore not generically queryable -- however, it will gain defined schema for -that particular driver, and will therefore over time gain defined structure -that can be formatted and value extracted from. - -Backwards Compatibility -======================= - -The ``isMaster`` command currently ignores arguments (i.e. If arguments are -provided the ``isMaster`` command discards them without erroring out). This -functionality has therefore no backwards compatibility concerns. - -Reference Implementation -======================== - -`C Driver `_. - -Q&A -=== - -* The 128 bytes application.name limit, does that include BSON overhead - * No, just the string itself -* The 512 bytes limit, does that include BSON overhead? - * Yes -* The 512 bytes limit, does it apply to the full ``isMaster`` document or just the ``client`` subdocument - * Just the subdocument -* Should I really try to fill the 512 bytes with data? - * Not really. The server does not attempt to normalize or compress this data in anyway, so it will hold it in memory as-is per connection. 512 bytes for 20,000 connections is ~ 10mb of memory the server will need. -* What happens if I pass this new ``isMaster`` argument to previous MongoDB versions? - * Nothing. Arguments passed to ``isMaster`` prior to MongoDB 3.4 are not treated in any special way and have no effect one way or other -* Are there wire version bumps or anything accompanying this specification? - * No -* Is establishing the handshake required for connecting to MongoDB 3.4? - * No, it only augments the connection. MongoDB will not reject connections without it -* Does this affect SDAM implementations? - * Possibly. There are a couple of gotchas. If the application.name is not in the URI... - * The SDAM monitoring cannot be launched until the user has had the ability - to set the application name because the application name has to be sent to the - first ``isMaster``. This means that the connection pool cannot be established until - the first user initiated command, or else some connections will have the - application name while other won’t - * The ``isMaster`` handshake must be called on all sockets, including - administrative background sockets to MongoDB -* My language doesn't have ``uname``, but does instead provide its own variation of these values, is that OK? - * Absolutely. As long as the value is identifiable it is fine. The exact method and values are undefined by this specification +.. note:: + This specification has been converted to Markdown and renamed to + `handshake.md `_. diff --git a/source/mongodb-handshake/tests/README.md b/source/mongodb-handshake/tests/README.md new file mode 100644 index 0000000000..296c87ee02 --- /dev/null +++ b/source/mongodb-handshake/tests/README.md @@ -0,0 +1,514 @@ +# MongoDB Handshake Tests + +## Prose Tests + +### Test 1: Test that environment metadata is properly captured + +Drivers that capture values for `client.env` should test that a connection and hello command succeeds in the presence of +the following sets of environment variables: + +1. Valid AWS + + | Environment Variable | Value | + | --------------------------------- | ------------------ | + | `AWS_EXECUTION_ENV` | `AWS_Lambda_java8` | + | `AWS_REGION` | `us-east-2` | + | `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` | `1024` | + +2. Valid Azure + + | Environment Variable | Value | + | -------------------------- | ------ | + | `FUNCTIONS_WORKER_RUNTIME` | `node` | + +3. Valid GCP + + | Environment Variable | Value | + | ---------------------- | ------------- | + | `K_SERVICE` | `servicename` | + | `FUNCTION_MEMORY_MB` | `1024` | + | `FUNCTION_TIMEOUT_SEC` | `60` | + | `FUNCTION_REGION` | `us-central1` | + +4. Valid Vercel + + | Environment Variable | Value | + | -------------------- | ------ | + | `VERCEL` | `1` | + | `VERCEL_REGION` | `cdg1` | + +5. Invalid - multiple providers + + | Environment Variable | Value | + | -------------------------- | ------------------ | + | `AWS_EXECUTION_ENV` | `AWS_Lambda_java8` | + | `FUNCTIONS_WORKER_RUNTIME` | `node` | + +6. Invalid - long string + + | Environment Variable | Value | + | -------------------- | ---------------------- | + | `AWS_EXECUTION_ENV` | `AWS_Lambda_java8` | + | `AWS_REGION` | `a` repeated 512 times | + +7. Invalid - wrong types + + | Environment Variable | Value | + | --------------------------------- | ------------------ | + | `AWS_EXECUTION_ENV` | `AWS_Lambda_java8` | + | `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` | `big` | + +8. Invalid - `AWS_EXECUTION_ENV` does not start with `"AWS_Lambda_"` + + | Environment Variable | Value | + | -------------------- | ----- | + | `AWS_EXECUTION_ENV` | `EC2` | + +9. Valid container and FaaS provider. This test MUST verify that both the container metadata and the AWS Lambda metadata + is present in `client.env`. + + | Environment Variable | Value | + | --------------------------------- | ------------------ | + | `AWS_EXECUTION_ENV` | `AWS_Lambda_java8` | + | `AWS_REGION` | `us-east-2` | + | `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` | `1024` | + | `KUBERNETES_SERVICE_HOST` | `1` | + +### Test 2: Test that the driver accepts an arbitrary auth mechanism + +1. Mock the server response in a way that `saslSupportedMechs` array in the `hello` command response contains an + arbitrary string. + +2. Create and connect a `Connection` object that connects to the server that returns the mocked response. + +3. Assert that no error is raised. + +## Client Metadata Update Prose Tests + +Drivers that do not emit events for commands issued as part of the handshake with the server will need to create a +test-only backdoor mechanism to intercept the handshake `hello` command for verification purposes. + +Client metadata specified as part of a "Create a `MongoClient` instance" step MUST use an API which appends the +specified metadata _during_ `MongoClient` initialization. + +Client metadata specified as part of an "Append the `DriverInfoOptions`" step MUST use an API which appends the +specified metadata _after_ `MongoClient` initialization. + +### Test 1: Test that the driver updates metadata + +Drivers should verify that metadata provided after `MongoClient` initialization is appended, not replaced, and is +visible in the `hello` command of new connections. + +There are multiple test cases parameterized with `DriverInfoOptions` to be appended after `MongoClient` initialization. +Before each test case, perform the setup. + +#### Setup + +1. Create a `MongoClient` instance with the following: + + - `maxIdleTimeMS` set to `1ms` + + - Client metadata appended with the following `DriverInfoOptions`: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +2. Send a `ping` command to the server and verify that the command succeeds. + +3. Save intercepted `client` document as `initialClientMetadata`. + +4. Wait 5ms for the connection to become idle. + +#### Parameterized test cases + +| Case | Name | Version | Platform | +| ---- | --------- | ------- | ------------------ | +| 1 | framework | 2.0 | Framework Platform | +| 2 | framework | 2.0 | null | +| 3 | framework | null | Framework Platform | +| 4 | framework | null | null | + +#### Running a test case + +1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. + +2. Send a `ping` command to the server and verify: + + - The command succeeds. + + - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` + command, with values separated by a pipe `|`. + + - `client.driver.name`: + - If test case's name is non-null: `library|` + - Otherwise, the field remains unchanged: `library` + - `client.driver.version`: + - If test case's version is non-null: `1.2|` + - Otherwise, the field remains unchanged: `1.2` + - `client.platform`: + - If test case's platform is non-null: `Library Platform|` + - Otherwise, the field remains unchanged: `Library Platform` + + - All other subfields in the `client` document remain unchanged from `initialClientMetadata`. + +### Test 2: Multiple Successive Metadata Updates + +Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and +is visible in the `hello` command of new connections. + +There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update. +Before each test case, perform the setup. + +#### Setup + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + +2. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +3. Send a `ping` command to the server and verify that the command succeeds. + +4. Save intercepted `client` document as `clientMetadata`. + +5. Wait 5ms for the connection to become idle. + +##### Parameterized test cases + +| Case | Name | Version | Platform | +| ---- | --------- | ------- | ------------------ | +| 1 | framework | 2.0 | Framework Platform | +| 2 | framework | 2.0 | null | +| 3 | framework | null | Framework Platform | +| 4 | framework | null | null | + +##### Running a test case + +1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. + +2. Send a `ping` command to the server and verify: + + - The command succeeds. + + - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` + command, with values separated by a pipe `|`. + + - `client.driver.name`: + - If test case's name is non-null: `library|` + - Otherwise, the field remains unchanged: `library` + - `client.driver.version`: + - If test case's version is non-null: `1.2|` + - Otherwise, the field remains unchanged: `1.2` + - `client.platform`: + - If test case's platform is non-null: `Library Platform|` + - Otherwise, the field remains unchanged: `Library Platform` + + - All other subfields in the `client` document remain unchanged from `clientMetadata`. + +### Test 3: Multiple Successive Metadata Updates with Duplicate Data + +There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update. +Before each test case, perform the setup. + +#### Setup + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + +2. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +3. Send a `ping` command to the server and verify that the command succeeds. + +4. Save intercepted `client` document as `updatedClientMetadata`. + +5. Wait 5ms for the connection to become idle. + +##### Parameterized test cases + +| Case | Name | Version | Platform | +| ---- | --------- | ------- | ------------------ | +| 1 | library | 1.2 | Library Platform | +| 2 | framework | 1.2 | Library Platform | +| 3 | library | 2.0 | Library Platform | +| 4 | library | 1.2 | Framework Platform | +| 5 | framework | 2.0 | Library Platform | +| 6 | framework | 1.2 | Framework Platform | +| 7 | library | 2.0 | Framework Platform | + +##### Running a test case + +1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. + +2. Send a `ping` command to the server and verify: + + - The command succeeds. + + - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` + command, with values separated by a pipe `|`. To simplify assertions in these tests, strip out the default driver + info that is automatically added by the driver (ex: `metadata.name.split('|').slice(1).join('|')`). + + - If the test case's DriverInfo is identical to the driver info from setup step 2 (test case 1): + - Assert `metadata.driver.name` is equal to `library` + - Assert `metadata.driver.version` is equal to `1.2` + - Assert `metadata.platform` is equal to `LibraryPlatform` + - Otherwise: + - Assert `metadata.driver.name` is equal to `library|` + - Assert `metadata.driver.version` is equal to `1.2|` + - Assert `metadata.platform` is equal to `LibraryPlatform|` + + - All other subfields in the `client` document remain unchanged from `updatedClientMetadata`. + +### Test 4: Multiple Metadata Updates with Duplicate Data + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + +2. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +3. Send a `ping` command to the server and verify that the command succeeds. + +4. Wait 5ms for the connection to become idle. + +5. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ------------------ | + | name | framework | + | version | 2.0 | + | platform | Framework Platform | + +6. Send a `ping` command to the server and verify that the command succeeds. + +7. Save intercepted `client` document as `clientMetadata`. + +8. Wait 5ms for the connection to become idle. + +9. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +10. Send a `ping` command to the server and verify that the command succeeds. + +11. Save intercepted `client` document as `updatedClientMetadata`. + +12. Assert that `clientMetadata` is identical to `updatedClientMetadata`. + +### Test 5: Metadata is not appended if identical to initial metadata + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + + - Client metadata appended with the following `DriverInfoOptions`: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +2. Send a `ping` command to the server and verify that the command succeeds. + +3. Save intercepted `client` document as `clientMetadata`. + +4. Wait 5ms for the connection to become idle. + +5. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +6. Send a `ping` command to the server and verify that the command succeeds. + +7. Save intercepted `client` document as `updatedClientMetadata`. + +8. Assert that `clientMetadata` is identical to `updatedClientMetadata`. + +### Test 6: Metadata is not appended if identical to initial metadata (separated by non-identical metadata) + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + + - Client metadata appended with the following `DriverInfoOptions`: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +2. Send a `ping` command to the server and verify that the command succeeds. + +3. Wait 5ms for the connection to become idle. + +4. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | framework | + | version | 1.2 | + | platform | Library Platform | + +5. Send a `ping` command to the server and verify that the command succeeds. + +6. Save intercepted `client` document as `clientMetadata`. + +7. Wait 5ms for the connection to become idle. + +8. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + + | Field | Value | + | -------- | ---------------- | + | name | library | + | version | 1.2 | + | platform | Library Platform | + +9. Send a `ping` command to the server and verify that the command succeeds. + +10. Save intercepted `client` document as `updatedClientMetadata`. + +11. Assert that `clientMetadata` is identical to `updatedClientMetadata`. + +### Test 7: Empty strings are considered unset when appending duplicate metadata + +Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and +is visible in the `hello` command of new connections. + +There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update. +Before each test case, perform the setup. + +##### Parameterized test cases + +> [!NOTE] +> Drivers whose API allows `client.driver.name` to be unset MAY implement Case 1. + +###### Appended metadata + +| Case | Name | Version | Platform | +| ----- | -------- | ------- | -------------------- | +| ~~1~~ | ~~null~~ | ~~1.2~~ | ~~Library Platform~~ | +| 2 | library | null | Library Platform | +| 3 | library | 1.2 | null | + +###### Duplicate Metadata + +| Case | Name | Version | Platform | +| ----- | ------- | ------- | -------------------- | +| ~~1~~ | ~~""~~ | ~~1.2~~ | ~~Library Platform~~ | +| 2 | library | "" | Library Platform | +| 3 | library | 1.2 | "" | + +##### Running a test case + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + +2. Append the `DriverInfoOptions` from the selected test case from the appended metadata section. + +3. Send a `ping` command to the server and verify that the command succeeds. + +4. Save intercepted `client` document as `clientMetadata`. + +5. Wait 5ms for the connection to become idle. + +6. Append the `DriverInfoOptions` from the selected test case from the duplicate metadata section. + +7. Send a `ping` command to the server and verify the command succeeds. + +8. Store the response as `updatedClientMetadata`. + +9. Assert that `clientMetadata` is identical to `updatedClientMetadata`. + +### Test 8: Empty strings are considered unset when appending metadata identical to initial metadata + +Drivers should verify that after `MongoClient` initialization, metadata can be updated multiple times, not replaced, and +is visible in the `hello` command of new connections. + +There are multiple test cases parameterized with `DriverInfoOptions` to be appended after a previous metadata update. +Before each test case, perform the setup. + +##### Parameterized test cases + +###### Initial metadata + +| Case | Name | Version | Platform | +| ----- | -------- | ------- | -------------------- | +| ~~1~~ | ~~null~~ | ~~1.2~~ | ~~Library Platform~~ | +| 2 | library | null | Library Platform | +| 3 | library | 1.2 | null | + +###### Appended Metadata + +| Case | Name | Version | Platform | +| ----- | -------- | ------- | -------------------- | +| ~~1~~ | ~~null~~ | ~~1.2~~ | ~~Library Platform~~ | +| 2 | library | "" | Library Platform | +| 3 | library | 1.2 | "" | + +##### Running a test case + +1. Create a `MongoClient` instance with: + + - `maxIdleTimeMS` set to `1ms` + - Client metadata appended with the `DriverInfoOptions` from the selected test case from the initial metadata + section. + +2. Send a `ping` command to the server and verify that the command succeeds. + +3. Save intercepted `client` document as `initialClientMetadata`. + +4. Wait 5ms for the connection to become idle. + +5. Append the `DriverInfoOptions` from the selected test case from the appended metadata section. + +6. Send a `ping` command to the server and verify the command succeeds. + +7. Store the response as `updatedClientMetadata`. + +8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`. + +### Test 9: Handshake documents include `backpressure: true` + +These tests require a mechanism for observing handshake documents sent to the server. + +1. Create a `MongoClient` that is configured to record all handshake documents sent to the server as a part of + connection establishment. + +2. Send a `ping` command to the server and verify that the command succeeds. This ensure that a connection is + established on all topologies. + +3. Assert that for every handshake document intercepted: + + 1. The document has a field `backpressure` whose value is `true`. diff --git a/source/mongodb-handshake/tests/unified/metadata-not-propagated.json b/source/mongodb-handshake/tests/unified/metadata-not-propagated.json new file mode 100644 index 0000000000..7f70dd1488 --- /dev/null +++ b/source/mongodb-handshake/tests/unified/metadata-not-propagated.json @@ -0,0 +1,101 @@ +{ + "description": "client metadata is not propagated to the server", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "6.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandSucceededEvent", + "commandFailedEvent", + "connectionClosedEvent", + "connectionCreatedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + } + ], + "tests": [ + { + "description": "metadata append does not create new connections or close existing ones and no hello command is sent", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "appendMetadata", + "object": "client", + "arguments": { + "driverInfoOptions": { + "name": "framework", + "version": "2.0", + "platform": "Framework Platform" + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + } + ] + }, + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandSucceededEvent": { + "commandName": "ping" + } + }, + { + "commandSucceededEvent": { + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/source/mongodb-handshake/tests/unified/metadata-not-propagated.yml b/source/mongodb-handshake/tests/unified/metadata-not-propagated.yml new file mode 100644 index 0000000000..4c7b48a002 --- /dev/null +++ b/source/mongodb-handshake/tests/unified/metadata-not-propagated.yml @@ -0,0 +1,59 @@ +description: client metadata is not propagated to the server +schemaVersion: '1.9' +runOnRequirements: + - minServerVersion: '6.0' +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandSucceededEvent + - commandFailedEvent + - connectionClosedEvent + - connectionCreatedEvent + - database: + id: &database database + client: *client + databaseName: test +tests: + # Test that appending metadata after `MongoClient` initialization does not close existing + # connections, create new ones, and that no new `hello` command is sent. + - description: metadata append does not create new connections or close existing ones and no hello command is sent + operations: + - name: runCommand + object: *database + arguments: + commandName: ping + command: + ping: 1 + expectResult: + ok: 1 + - name: appendMetadata + object: *client + arguments: + driverInfoOptions: + name: framework + version: '2.0' + platform: Framework Platform + - name: runCommand + object: *database + arguments: + commandName: ping + command: + ping: 1 + expectResult: + ok: 1 + expectEvents: + - client: *client + eventType: cmap + events: + # Expect only one connection to be created for the first 'ping' command. + - connectionCreatedEvent: { } + - client: *client + eventType: command + events: + - commandSucceededEvent: + commandName: ping + - commandSucceededEvent: + commandName: ping + diff --git a/source/objectid.rst b/source/objectid.rst index 862eb95071..0ede5a7506 100644 --- a/source/objectid.rst +++ b/source/objectid.rst @@ -1,159 +1,4 @@ -.. role:: javascript(code) - :language: javascript -=============== -ObjectID format -=============== - -:Spec-ticket: WRITING-2823 -:Title: ObjectID -:Authors: Derick Rethans -:Status: Draft -:Type: Standards -:Last Modified: 2018-07-02 -:Version: 0.2 -:Spec Lead: n/a -:Advisory Group: n/a -:Approver(s): Andy Schwerin, Bernie Hackett, Eliot Horowitz, Jeff Yemin, Jeremy Mikola, Matt Broadstone - - -.. contents:: - --------- - -Abstract -======== - -This specification documents the format and data contents of ObjectID BSON -values that the drivers and the server generate when no field values have been -specified (e.g. creating an ObjectID BSON value when no _id field is present -in a document). It is primarily aimed to provide an alternative to the -historical use of the MD5 hashing algorithm for the machine information field -of the ObjectID, which is problematic when providing a FIPS compliant -implementation. It also documents existing best practices for the timestamp -and counter fields. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -The ObjectID_ BSON type is a 12-byte value consisting of three different -portions (fields): - -- a 4-byte value representing the seconds since the Unix epoch, -- a 5-byte random number, -- a 3-byte counter, starting with a random value. - -.. _ObjectID: https://docs.mongodb.com/manual/reference/method/ObjectId/ - -Timestamp Field ---------------- - -This 4-byte field represents the seconds since the Unix epoch (Jan 1st, 1970, -midnight UTC). It is an ever increasing value that will have a range until -about Jan 7th, 2106. - -Drivers MUST create ObjectIDs with this value representing the number of -seconds since the Unix epoch. - -Drivers MUST interpret this value as an **unsigned 32-bit integer** when -conversions to language specific date/time values are created, and when -converting this to a timestamp. - -Drivers SHOULD have an accessor method on an ObjectID class for obtaining the -timestamp value. - -Random Value ------------- - -A 5-byte field consisting of a random value generated once per process. - -Drivers MUST NOT have an accessor method on an ObjectID class for obtaining -this value. - -Counter -------- - -A 3-byte counter. - -This counter MUST be initialised to a random value when the driver is first -activated. After initialisation, the counter MUST be increased by 1 for every -ObjectID creation. - -When the counter overflows (i.e., hits 16777215+1), the counter MUST be reset -to 0. - -Drivers MUST NOT have an accessor method on an ObjectID class for obtaining -this value. - -Test Plan -========= - -Drivers MUST: - -- Ensure that the Timestamp field is represented as an unsigned 32-bit - representing the number of seconds since the Epoch for the Timestamp values: - - - ``0x00000000``: To match ``"Jan 1st, 1970 00:00:00 UTC"`` - - ``0x7FFFFFFF``: To match ``"Jan 19th, 2038 03:14:07 UTC"`` - - ``0x80000000``: To match ``"Jan 19th, 2038 03:14:08 UTC"`` - - ``0xFFFFFFFF``: To match ``"Feb 7th, 2106 06:28:15 UTC"`` - -- Ensure that the Counter field successfully overflows its sequence from - ``0xFFFFFFFF`` to ``0x00000000``. - -Motivation for Change -===================== - -Besides the specific exclusion of MD5 as an allowed hashing algorithm, the -information in this specification is meant to align the ObjectID generation -algorithm of both drivers and the server. - -Design Rationale -================ - -**Timestamp:** The timestamp is a 32-bit **unsigned** integer, as it allows us -to extend the furthest date that the timestamp can represent from the year 2038 -to 2106. There is no reason why MongoDB would generate a timestamp to mean a -date before 1970, as MongoDB did not exist back then. - -**Random Value:** Originally, this field consisted of the Machine ID and -Process ID fields. There were numerous divergences between drivers due to -implementation choices, and the Machine ID field traditionally used the MD5 -hashing algorithm which can't be used on FIPS compliant machines. In order to -allow for a similar behaviour among all drivers **and** the MongoDB Server, -these two fields have been collated together into a single 5-byte random value, -unique to a machine and process. - -**Counter:** The counter makes it possible to have multiple ObjectIDs per -second, per server, and per process. As the counter can overflow, there is a -possibility of having duplicate ObjectIDs if you create more than 16 million -ObjectIDs per second in the same process on a single machine. - -Backwards Compatibility -======================= - -This specification requires that the existing *Machine ID* and *Process ID* -fields are merged into a single 5-byte value. This will change the behaviour of -ObjectID generation, as well as the behaviour of drivers that currently have -getters and setters for the original *Machine ID* and *Process ID* fields. - -Reference Implementation -======================== - -Currently there is no full reference implementation yet. - -Changelog -========= - -2018-07-02 — Version 0.2 - Replaced Machine ID and Process ID fields with a single 5-byte unique value - -2018-05-22 — Version 0.1 - Initial Release +.. note:: + This specification has been converted to Markdown and renamed to + `objectid.md `_. diff --git a/source/ocsp-support/ocsp-support.md b/source/ocsp-support/ocsp-support.md new file mode 100644 index 0000000000..046ef60bb5 --- /dev/null +++ b/source/ocsp-support/ocsp-support.md @@ -0,0 +1,571 @@ +# OCSP Support + +- Status: Accepted +- Minimum Server Version: 4.4 + +______________________________________________________________________ + +## Abstract + +This specification is about the ability for drivers to to support +[OCSP](https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol)—Online Certificate Status Protocol +([RFC 6960](https://tools.ietf.org/html/rfc6960))—and two of its related extensions: +[OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) ([RFC 6066](https://tools.ietf.org/html/rfc6066)) and +[Must-Staple](https://scotthelme.co.uk/ocsp-must-staple/) ([RFC 7633](https://tools.ietf.org/html/rfc7633)). + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Required Server Versions + +The server supports attaching a stapled OCSP response with versions ≥ 4.4. Future backports will bring stapling support +to server versions ≥ 3.6. Drivers need not worry about the version of the server as a driver's TLS library should +automatically perform the proper certificate revocation checking behavior once OCSP is enabled. + +### Enabling OCSP Support by Default + +Drivers whose TLS libraries utilize application-wide settings for OCSP MUST respect the application's settings and MUST +NOT change any OCSP settings. Otherwise: + +- If a driver's TLS library supports OCSP, OCSP MUST be enabled by default whenever possible (even if this also enables + Certificate Revocation List (CRL) checking). +- If a driver's TLS library supports verifying stapled OCSP responses, this option MUST be enabled by default whenever + possible (even if this also enables CRL checking). + +### Suggested OCSP Behavior + +Drivers SHOULD implement the OCSP behavior defined below to the extent that their TLS library allows. At any point in +the steps defined below, if a certificate in or necessary to validate the chain is found to be invalid, the driver +SHOULD end the connection. + +1. If a driver's TLS library supports Stapled OCSP, the server has a Must-Staple certificate and the server does not + present a stapled OCSP response, a driver SHOULD end the connection. +2. If a driver's TLS library supports Stapled OCSP and the server staples an OCSP response that does not cover the + certificate it presents or is invalid per [RFC 6960 Section 3.2](https://tools.ietf.org/html/rfc6960#section-3.2), + a driver SHOULD end the connection. +3. If a driver's TLS library supports Stapled OCSP and the server staples an OCSP response that does cover the + certificate it presents, a driver SHOULD accept the stapled OCSP response and validate all of the certificates + that are presented in the response. +4. If any unvalidated certificates in the chain remain and the client possesses an OCSP cache, the driver SHOULD + attempt to validate the status of the unvalidated certificates using the cache. +5. If any unvalidated certificates in the chain remain and the driver has a user specified CRL, the driver SHOULD + attempt to validate the status of the unvalidated certificates using the user-specified CRL. +6. If any unvalidated certificates in the chain remain and the driver has access to cached CRLs (e.g. + OS-level/application-level/user-level caches), the driver SHOULD attempt to validate the status of the unvalidated + certificates using the cached CRLs. +7. If the server's certificate remains unvalidated, that certificate has a list of OCSP responder endpoints, and + `tlsDisableOCSPEndpointCheck` or `tlsDisableCertificateRevocationCheck` is false + ([if the driver supports these options](#mongoclient-configuration)), the driver SHOULD send HTTP requests to the + responders in parallel. The first valid response that concretely marks the certificate status as good or revoked + should be used. A timeout should be applied to requests per the + [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) + specification, with a default timeout of five seconds. The status for a response should only be checked if the + response is valid per [RFC 6960 Section 3.2](https://tools.ietf.org/html/rfc6960#section-3.2) +8. If any unvalidated intermediate certificates remain and those certificates have OCSP endpoints, for each + certificate, the driver SHOULD NOT reach out to the OCSP endpoint specified and attempt to validate that + certificate.\* +9. If any unvalidated intermediate certificates remain and those certificates have CRL distribution points, the driver + SHOULD NOT download those CRLs and attempt to validate the status of all the other certificates using those + CRLs.\* +10. Finally, the driver SHOULD continue the connection, even if the status of all the unvalidated certificates has not + been confirmed yet. This means that the driver SHOULD default to "soft fail" behavior, connecting as long as there + are no explicitly invalid certificates—i.e. the driver will connect even if the status of all the unvalidated + certificates has not been confirmed yet (e.g. because an OCSP responder is down). + +\*: See [Design Rationale: Suggested OCSP Behavior](#suggested-ocsp-behavior) + +### Suggested OCSP Response Validation Behavior + +Drivers SHOULD validate OCSP Responses in the manner specified in +[RFC 6960: 3.2](https://tools.ietf.org/html/rfc6960#section-3.2) to the extent that their TLS library allows. + +### Suggested OCSP Caching Behavior + +Drivers with sufficient control over their TLS library's OCSP behavior SHOULD implement an OCSP cache. The key for this +cache SHOULD be the certificate identifier (CertID) of the OCSP request as specified in +[RFC 6960: 4.1.1](https://tools.ietf.org/html/rfc6960#section-4.1.1). For convenience, the relevant section has been +duplicated below: + +```text +CertID ::= SEQUENCE { + hashAlgorithm AlgorithmIdentifier, + issuerNameHash OCTET STRING, -- Hash of issuer's DN + issuerKeyHash OCTET STRING, -- Hash of issuer's public key + serialNumber CertificateSerialNumber } +``` + +If a driver would accept a conclusive OCSP response (stapled or non-stapled), the driver SHOULD cache that response. We +define a conclusive OCSP response as an OCSP response that indicates that a certificate is either valid or revoked. +Thus, an unknown certificate status SHOULD NOT be considered conclusive, and the corresponding OCSP response SHOULD NOT +be cached. + +In accordance with [RFC: 6960: 3.2](https://tools.ietf.org/html/rfc6960#section-3.2), a cached response SHOULD be +considered valid up to and excluding the time specified in the response's `nextUpdate` field. In other words, if the +current time is *t*, then the cache entry SHOULD be considered valid if *thisUpdate ⩽ t < nextUpdate*. + +If a driver would accept a stapled OCSP response and that response has a later `nextUpdate` than the response already in +the cache, drivers SHOULD replace the older entry in the cache with the fresher response. + +### MongoClient Configuration + +This specification introduces the client-level configuration options defined below. + +#### tlsDisableOCSPEndpointCheck + +Drivers that can, on a per MongoClient basis, disable non-stapled OCSP while keeping stapled OCSP enabled MUST implement +this option. + +This boolean option determines whether a MongoClient should refrain from reaching out to an OCSP endpoint i.e. whether +non-stapled OCSP should be disabled. When set to true, a driver MUST NOT reach out to OCSP endpoints. When set to false, +a driver MUST reach out to OCSP endpoints if needed (as described in +[Specification: Suggested OCSP Behavior](#suggested-ocsp-behavior)). + +For drivers that pass the ["Soft Fail Test"](tests/README.md#integration-tests-permutations-to-be-tested), this option +MUST default to false. + +For drivers that fail the "Soft Fail Test" because their TLS library exhibits hard-fail behavior when a responder is +unreachable, this option MUST default to true, and a driver MUST document this behavior. If this hard-failure behavior +is specific to a particular platform (e.g. the TLS library hard-fails only on Windows) then this option MUST default to +true only on the platform where the driver exhibits hard-fail behavior, and a driver MUST document this behavior. + +#### tlsDisableCertificateRevocationCheck + +Drivers whose TLS libraries support an option to toggle general certificate revocation checking must implement this +option if enabling general certificate revocation checking causes hard-fail behavior when no revocation mechanisms are +available (i.e. no methods are defined or the CRL distribution points/OCSP endpoints are unreachable). + +This boolean option determines whether a MongoClient should refrain checking certificate revocation status. When set to +true, a driver MUST NOT check certificate revocation status via CRLs or OCSP. When set to false, a driver MUST check +certificate revocation status, reach out to OCSP endpoints if needed (as described in +[Specification: Suggested OCSP Behavior](#suggested-ocsp-behavior)). + +For drivers that pass the ["Soft Fail Test"](tests/README.md#integration-tests-permutations-to-be-tested) , this option +MUST default to false. + +If a driver does not support `tlsDisableOCSPEndpointCheck` and that driver fails the "Soft Fail Test" because their TLS +library exhibits hard-fail behavior when a responder is unreachable, then that driver must default +`tlsDisableCertificateRevocationCheck` to true. Such a driver also MUST document this behavior. If this hard-failure +behavior is specific to a particular platform (e.g. the TLS library hard-fails only on Windows) then this option MUST +default to true only on the platform where the driver exhibits hard-fail behavior, and a driver MUST document this +behavior. + +#### Naming Deviations + +Drivers MUST use the defined names of `tlsDisableOCSPEndpointCheck` and `tlsDisableCertificateRevocationCheck` for the +connection string parameters to ensure portability of connection strings across applications and drivers. If drivers +solicit MongoClient options through another mechanism (e.g. an options dictionary provided to the MongoClient +constructor), drivers SHOULD use the defined name but MAY deviate to comply with their existing conventions. For +example, a driver may use `tls_disable_ocsp_endpoint_check` instead of `tlsDisableOCSPEndpointCheck`. + +### How OCSP interacts with existing configuration options + +The following requirements apply only to drivers that are able to enable/disable OCSP on a per MongoClient basis. + +1. If a connection string specifies `tlsInsecure=true` then the driver MUST disable OCSP. +2. If a connection string contains both `tlsInsecure` and `tlsDisableOCSPEndpointCheck` then the driver MUST throw an + error. +3. If a driver supports `tlsAllowInvalidCertificates`, and a connection string specifies + `tlsAllowInvalidCertificates=true`, then the driver MUST disable OCSP. +4. If a driver supports `tlsAllowInvalidCertificates`, and a connection string specifies both + `tlsAllowInvalidCertificates` and `tlsDisableOCSPEndpointCheck`, then the driver MUST throw an error. + +The remaining requirements in this section apply only to drivers that expose an option to enable/disable certificate +revocation checking on a per MongoClient basis. + +1. Driver MUST enable OCSP support (with stapling if possible) when certificate revocation checking is enabled + **unless** their driver exhibits hard-fail behavior (see + [tlsDisableCertificateRevocationCheck](#tlsdisablecertificaterevocationcheck)). In such a case, a driver MUST + disable OCSP support on the platforms where its TLS library exhibits hard-fail behavior. +2. Drivers SHOULD throw an error if any of `tlsInsecure=true` or `tlsAllowInvalidCertificates=true` or + `tlsDisableOCSPEndpointCheck=true` is specified alongside the option to enable certificate revocation checking. +3. If a connection string contains both `tlsInsecure` and `tlsDisableCertificateRevocationCheck` then the driver MUST + throw an error. +4. If a driver supports `tlsAllowInvalidCertificates` and a connection string specifies both + `tlsAllowInvalidCertificates` and `tlsDisableCertificateRevocationCheck`, then the driver MUST throw an error. +5. If a driver supports `tlsDisableOCSPEndpointCheck`, and a connection string specifies + `tlsDisableCertificateRevocationCheck`, then the driver MUST throw an error. + +### TLS Requirements + +[Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) (SNI) MUST BE used in the TLS connection +that obtains the server's certificate, otherwise the server may present the incorrect certificate. This requirement is +especially relevant to drivers whose TLS libraries allow for finer-grained control over their TLS behavior (e.g. Python, +C). + +### Documentation Requirements + +Drivers that cannot support OCSP MUST document this lack of support. Additionally, such drivers MUST document the +following: + +- They MUST document that they will be unable to support certificate revocation checking with Atlas when Atlas moves to + OCSP-only certificates. +- They MUST document that users should be aware that if they use a Certificate Authority (CA) that issues OCSP-only + certificates, then the driver cannot perform certificate revocation checking. + +Drivers that support OCSP without stapling MUST document this lack of support for stapling. They also MUST document +their behavior when an OCSP responder is unavailable and a server has a Must-Staple certificate. If a driver is able to +connect in such a scenario due to the prevalence of +"[soft-fail](https://www.imperialviolet.org/2014/04/19/revchecking.html)" behavior in TLS libraries (where a certificate +is accepted when an answer from an OCSP responder cannot be obtained), they additionally MUST document that this ability +to connect to a server with a Must-Staple certificate when an OCSP responder is unavailable differs from the mongo shell +or a driver that does support OCSP-stapling, both of which will fail to connect (i.e. "hard-fail") in such a scenario. + +If a driver (e.g. [Python](https://www.mongodb.com/docs/languages/python/pymongo-driver/current/connect/tls/), +[C](http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html)) allows the user to provide their own certificate +revocation list (CRL), then that driver MUST document their TLS library's preference between the user-provided CRL and +OCSP. + +Drivers that cannot enable OCSP by default on a per MongoClient basis (e.g. Java) MUST document this limitation. + +Drivers that fail either of the "Malicious Server Tests" (i.e. the driver connects to a test server without TLS +constraints being relaxed) as defined in the test plan below MUST document that their chosen TLS library will connect in +the case that a server with a Must-Staple certificate does not staple a response. + +Drivers that fail "Malicious Server Test 2" (i.e. the driver connects to the test server without TLS constraints being +relaxed) as defined in the test plan below MUST document that their chosen TLS library will connect in the case that a +server with a Must-Staple certificate does not staple a response and the OCSP responder is down. + +Drivers that fail "Soft Fail Test" MUST document that their driver's TLS library utilizes "hard fail" behavior in the +case of an unavailable OCSP responder in contrast to the mongo shell and drivers that utilize "soft fail" behavior. They +also MUST document the change in defaults for the applicable options (see +[MongoClient Configuration](#mongoclient-configuration)). + +If any changes related to defaults for OCSP behavior are made after a driver version that supports OCSP has been +released, the driver MUST document potential backwards compatibility issues as noted in the +[Backwards Compatibility](#backwards-compatibility) section. + +## Test Plan + +See [tests/README](tests/README.md) for tests. + +## Motivation for Change + +MongoDB Atlas intends to use [LetsEncrypt](https://letsencrypt.org/), a Certificate Authority (CA) that does not use +CRLs and only uses OCSP. (Atlas currently uses DigiCert certificates which specify both OCSP endpoints and CRL +distribution points.) Therefore, the MongoDB server is adding support for OCSP, and drivers need to support OCSP in +order for applications to continue to have the ability to verify the revocation status of an Atlas server's certificate. +Other CAs have also stopped using CRLs, so enabling OCSP support will ensure that a customer's choice in CAs is not +limited by a driver's lack of OCSP support. + +OCSP stapling will also help applications deployed behind a firewall with an outbound allowList. It's a very natural +mistake to neglect to allowList the CRL distribution points and the OCSP endpoints, which can prevent an application +from connecting to a MongoDB instance if certificate revocation checking is enabled but the driver does not support OCSP +stapling. + +Finally, drivers whose TLS libraries support [OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) extension will +be able to minimize the number of network round trips for the client because the driver's TLS library will read an OCSP +response stapled to the server's certificate that the server provides as part of the TLS handshake. Drivers whose TLS +libraries support OCSP but not stapling will need to make an additional round trip to contact the OCSP endpoint. + +## Design Rationale + +We have chosen not to force drivers whose TLS libraries do not support OCSP/stapling "out of the box" to implement OCSP +support due to the extra work and research that this might require. Similarly, this specification uses "SHOULD" more +commonly (when other specs would prefer "MUST") to account for the fact that some drivers may not be able to fully +customize OCSP behavior in their TLS library. + +We are requiring drivers to support both stapled OCSP and non-stapled OCSP in order to support revocation checking for +server versions in Atlas that do not support stapling, especially after Atlas switches to Let's Encrypt certificates +(which do not have CRLs). Additionally, even when servers do support stapling, in the case of a non-"Must Staple" +certificate (which is the type that Atlas is planning to use), if the server is unable to contact the OCSP responder +(e.g. due to a network error) and staple a certificate, the driver being able to query the certificate's OCSP endpoint +allows for one final chance to attempt to verify the certificate's validity. + +### Malicious Server Tests + +"Malicious Server Test 2" is designed to reveal the behavior of TLS libraries of drivers in one of the worst case +scenarios. Since a majority of the drivers will not have fine-grained control over their OCSP behavior, this test case +provides signal about the soft/hard fail behavior in a driver's TLS library so that we can document this. + +A driver with control over its OCSP behavior will react the same in "Malicious Server Test 1" and "Malicious Server Test +2", terminating the connection as long as TLS constraints have not been relaxed. + +### Atlas Connectivity Tests + +No additional Atlas connectivity tests will be added because the existing tests should provide sufficient coverage +(provided that one of the non-free tier clusters is upgraded ≥ 3.6). + +### Suggested OCSP Behavior + +For drivers with finer-grain control over their OCSP behavior, the suggested OCSP behavior was chosen as a balance +between security and availability, erring on availability while minimizing network round trips. Therefore, in order to +minimize network round trips, drivers are advised not to reach out to OCSP endpoints and CRL distribution points in +order to verify the revocation status of intermediate certificates. + +## Backwards Compatibility + +An application behind a firewall with an outbound allowList that upgrades to a driver implementing this specification +may experience connectivity issues when OCSP is enabled. This is because the driver may need to contact OCSP endpoints +or CRL distribution points[^1] specified in the server's certificate and if these OCSP endpoints and/or CRL distribution +points are not accessible, then the connection to the server may fail. (N.B.: TLS libraries +[typically implement "soft fail"](https://blog.hboeck.de/archives/886-The-Problem-with-OCSP-Stapling-and-Must-Staple-and-why-Certificate-Revocation-is-still-broken.html) +such that connections can continue even if the OCSP server is inaccessible, so this issue is much more likely in the +case of a server with a certificate that only contains CRL distribution points.) In such a scenario, connectivity may be +able to be restored by disabling non-stapled OCSP via `tlsDisableOCSPEndpointCheck` or by disabling certificate +revocation checking altogether via `tlsDisableCertificateRevocationCheck`. + +An application that uses a driver that utilizes hard-fail behavior when there are no certificate revocation mechanisms +available may also experience connectivity issue. Cases in which no certificate revocation mechanisms being available +include: + +1. When a server's certificate defines neither OCSP endpoints nor CRL distribution points +2. When a certificate defines CRL distribution points and/or OCSP endpoints but these points are unavailable (e.g. the + points are down or the application is deployed behind a restrictive firewall). + +In such a scenario, connectivity may be able to be restored by disabling non-stapled OCSP via +`tlsDisableOCSPEndpointCheck` or by disabling certificate revocation checking via +`tlsDisableCertificateRevocationCheck`. + +## Reference Implementation + +The .NET/C#, Python, C, and Go drivers will provide the reference implementations. See +[CSHARP-2817](https://jira.mongodb.org/browse/CSHARP-2817), [PYTHON-2093](https://jira.mongodb.org/browse/PYTHON-2093), +[CDRIVER-3408](https://jira.mongodb.org/browse/CDRIVER-3408) and +[GODRIVER-1467](http://jira.mongodb.org/browse/GODRIVER-1467). + +## Security Implications + +Customers should be aware that if they choose to use CA that only supports OCSP, they will not be able to check +certificate validity in drivers that cannot support OCSP. + +In the case that the server has a Must-Staple certificate and its OCSP responder is down (for longer than the server is +able to cache and staple a previously acquired response), the mongo shell or a driver that supports OCSP stapling will +not be able to connect while a driver that supports OCSP but not stapling will be able to connect. + +TLS libraries may implement "[soft-fail](https://www.imperialviolet.org/2014/04/19/revchecking.html)" in the case of +non-stapled OCSP which may be undesirable in highly secure contexts. + +Drivers that fail the "Malicious Server" tests as defined in Test Plan will connect in the case that server with a +Must-Staple certificate does not staple a response. + +### Testing Against Valid Certificate Chains + +Some TLS libraries are stricter about the types of certificate chains they're willing to accept (and it can be difficult +to debug why a particular certificate chain is considered invalid by a TLS library). Clients and servers with more +control over their OCSP implementation may run into fewer up front costs, but this may be at the cost of not fully +implementing every single aspect of OCSP. + +For example, the server team's certificate generation tool generated X509 V1 certificates which were used for testing +OCSP without any issues in the server team's tests. However, while we were creating a test plan for drivers, we +discovered that Java's keytool refused to import X509 V1 certificates into its trust store and thus had to modify the +server team's certificate generation tool to generate V3 certificates. + +Another example comes from [.NET on Linux](https://github.com/dotnet/corefx/issues/41475), which currently enforces the +CA/Browser forum requirement that while a leaf certificate can be covered solely by OCSP, "public CAs have to have +CRL[s] covering their issuing CAs". This requirement is not enforced with Java's default TLS libraries. See also: +[Future Work: CA/Browser Forum Requirements Complications](#cabrowser-forum-requirements-complications). + +## Future Work + +When the server work is backported, drivers will need to update their prose tests so that tests are run against a wider +range of compatible servers. + +Automated Atlas connectivity tests ([DRIVERS-382](https://jira.mongodb.org/browse/DRIVERS-382)) may be updated with +additional OCSP-related URIs when 4.4 becomes available for Atlas; alternatively, the clusters behind those URIs may be +updated to 4.4 (or an earlier version where OCSP has been backported). Note: While the free tier cluster used for the +Automated Atlas connectivity tests will automatically get updated to 4.4 when it is available, Atlas currently does not +plan to enable OCSP for free and shared tier instances (i.e. Atlas Proxy). + +Options to configure failure behavior (e.g. to maximize security or availability) may be added in the future. + +### CA/Browser Forum Requirements Complications + +The test plan may need to be reworked if we discover that a driver's TLS library strictly implements CA/Browser forum +requirements (e.g. [.NET on Linux](https://github.com/dotnet/corefx/issues/41475)). This is because our current chain of +certificates does not fulfill the following requirement: while a leaf certificate can be covered solely by OCSP, "public +CAs have to have CRL[s] covering their issuing CAs." This rework of the test plan may happen during the initial +implementation of OCSP support or happen later if a driver's TLS library implements the relevant CA/Browser forum +requirement. + +Extending the chain to fulfill the CA/Browser requirement should solve this issue, although drivers that don't support +manually supplying a CRL may need to host a web server that serves the required CRL during testing. + +## Q&A + +### Can we use one Evergreen task combined with distinct certificates for each column in the test matrix to prevent OCSP caching from affecting testing? + +No. This is because Evergreen may reuse a host with an OCSP cache from a previous execution, so using distinct +certificates per column would not obviate the need to clear all relevant OCSP caches prior to each test run. Since +Evergreen does perform some cleanup between executions, having separate tasks for each test column offers an additional +layer of safety in protecting against stale data in OCSP caches. + +### Should drivers use a nonce when creating an OCSP request? + +A driver MAY use a nonce if desired, but +[including a nonce in an OCSP request](https://tools.ietf.org/html/rfc6960#section-4.4.1) is not required as the server +does not explicitly support nonces. + +### Should drivers utilize a tolerance period when accepting OCSP responses? + +No. Although +[RFC 5019, The Lightweight Online Certificate Status Protocol (OCSP) Profile for High-Volume Environments,](https://tools.ietf.org/html/rfc5019) +allows for the configuration of a tolerance period for the acceptance of OCSP responses after `nextUpdate`, this spec is +not adhering to that RFC. + +### Why was the decision made to allow OCSP endpoint checking to be enabled/disabled via a URI option? + +We initially hoped that we would be able to not expose any options specifically related to OCSP to the user, in +accordance with the "No Knobs" [drivers mantra](../driver-mantras.md) However, we later decided that users may benefit +from having the ability to disable OCSP endpoint checking when applications are deployed behind restrictive firewall +with outbound allowLists, and this benefit is worth adding another URI option. + +## Appendix + +### OS-Level OCSP Cache Manipulation + +#### Windows + +On Windows, the OCSP cache can be viewed like so: + +```bash +certutil -urlcache +``` + +To search the cache for "Lets Encrypt" OCSP cache entries, the following command could be used: + +```bash +certutil -urlcache | findstr letsencrypt.org +``` + +On Windows, the OCSP cache can be cleared like so: + +```bash +certutil -urlcache * delete +``` + +To delete only "Let's Encrypt" related entries, the following command could be used: + +```bash +certutil -urlcache letsencrypt.org delete +``` + +#### macOS + +On macOS 10.14, the OCSP cache can be viewed like so: + +```bash +find ~/profile/Library/Keychains -name 'ocspcache.sqlite3' \ +-exec sqlite3 "{}" 'SELECT responderURI FROM responses;' \; +``` + +To search the cache for "Let's Encrypt" OCSP cache entries, the following command could be used: + +```bash +find ~/profile/Library/Keychains \ +-name 'ocspcache.sqlite3' \ +-exec sqlite3 "{}" \ +'SELECT responderURI FROM responses WHERE responderURI LIKE "http://%.letsencrypt.org%";' \; +``` + +On macOS 10.14, the OCSP cache can be cleared like so: + +```bash +find ~/profile/Library/Keychains -name 'ocspcache.sqlite3' \ +-exec sqlite3 "{}" 'DELETE FROM responses ;' \; +``` + +To delete only "Let's Encrypt" related entries, the following command could be used: + +```bash +find ~/profile/Library/Keychains -name 'ocspcache.sqlite3' \ +-exec sqlite3 "{}" \ +'DELETE FROM responses WHERE responderURI LIKE "http://%.letsencrypt.org%";' \; +``` + +### Optional Quick Manual Validation Tests of OCSP Support + +These optional validation tests are not a required part of the test plan. However, these optional tests may be useful +for drivers trying to quickly determine if their TLS library supports OCSP and/or as an initial manual testing goal when +implementing OCSP support. + +#### Optional test to ensure that the driver's TLS library supports OCSP stapling + +Create a test application with a connection string with TLS enabled that connects to any server that has OCSP-only +certificate and supports OCSP stapling. + +For example, the test application could connect to CV, one of the special testing Atlas clusters with a valid +OCSP-only certificate. see Future Work for additional information). + +Alternatively, the test application can attempt to connect to a **non-mongod server** that supports OCSP-stapling and +has a valid an OCSP-only certificate. The connection will fail of course, but we are only interested in the TLS +handshake and the OCSP requests that may follow. For example, the following connection string could be used: +`mongodb://valid-isrgrootx1.letsencrypt.org:443/?tls=true` + +Run the test application and verify through packet analysis that the driver's ClientHello message's TLS extension +section includes the `status_request` extension, thus indicating that the driver is advertising that it supports OCSP +stapling. + +Note: If using [WireShark](https://www.wireshark.org/) as your chosen packet analyzer, the `tls` (case-sensitive) +display filter may be useful in this endeavor. + +#### OCSP Caching and the optional test to ensure that the driver's TLS library supports non-stapled OCSP + +The "Optional test to ensure that the driver's TLS library supports non-stapled OCSP" is complicated by the fact that +OCSP allows the client to [cache the OCSP responses](https://tools.ietf.org/html/rfc5019#section-6.1), so clearing an +OCSP cache may be needed in order to force the TLS library to reach out to an OCSP endpoint. This cache may exist at the +OS-level, application-level and/or at the user-level. + +#### Optional test to ensure that the driver's TLS library supports non-stapled OCSP + +Create a test application with a connection string with TLS enabled that connects to any server with an OCSP-only +certificate. + +Alternatively, the test application can attempt to connect to a **non-mongod server** that does not support +OCSP-stapling and has a valid an OCSP-only certificate. The connection will fail of course, but we are only interested +in the TLS handshake and the OCSP requests that may follow. + +Alternatively, if it's known that a driver's TLS library does not support stapling or if stapling support can be toggled +off, then any **non-mongod server** that has a valid an OCSP-only certificate will work, including the example shown in +the "Optional test to ensure that the driver's TLS library supports OCSP stapling." + +Clear the OS/user/application OCSP cache, if one exists and the TLS library makes use of it. + +Run the test application and ensure that the TLS handshake succeeds. connection succeeds. Ensure that the driver's TLS +library has contacted the OCSP endpoint specified in the server's certificate. Two simple ways of checking this are: + +- Use a packet analyzer while the test application is running to ensure that the driver's TLS library contacts the OCSP + endpoint. When using WireShark, the `ocsp` and `tls` (case-sensitive) display filters may be useful in this + endeavor. +- If the TLS library utilizes an OCSP cache and the cache was cleared prior to starting the test application, check the + OCSP cache for a response from an OCSP endpoint specified in the server's certificate. + +## Changelog + +- 2024-08-20: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec. + +- 2021-04-07: Updated terminology to use allowList. + +- 2020-07-01: Default tlsDisableOCSPEndpointCheck or tlsDisableCertificateRevocationCheck to true in the case that a + driver's TLS library exhibits hard-fail behavior and add provision for platform-specific defaults. + +- 2020-03-20: Clarify OCSP documentation requirements for drivers unable to enable OCSP by default on a per MongoClient + basis. + +- 2020-03-03: Add tlsDisableCertificateRevocationCheck URI option. Add Go as a reference implementation. Add hard-fail + backwards compatibility documentation requirements. + +- 2020-02-26: Add tlsDisableOCSPEndpointCheck URI option. + +- 2020-02-19: Clarify behavior for reaching out to OCSP responders. + +- 2020-02-10: Add cache requirement. + +- 2020-01-31: Add SNI requirement and clarify design rationale regarding minimizing round trips. + +- 2020-01-28: Clarify behavior regarding nonces and tolerance periods. + +- 2020-01-16: Initial commit. + +## Endnotes + +[^1]: Since this specification mandates that a driver must enable OCSP when possible, this may involve enabling + certificate revocation checking in general, and thus the accessibility of CRL distribution points can become a + factor. diff --git a/source/ocsp-support/ocsp-support.rst b/source/ocsp-support/ocsp-support.rst new file mode 100644 index 0000000000..8274abb9f3 --- /dev/null +++ b/source/ocsp-support/ocsp-support.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `ocsp-support.md `_. diff --git a/source/ocsp-support/tests/README.md b/source/ocsp-support/tests/README.md new file mode 100644 index 0000000000..0456eacfbb --- /dev/null +++ b/source/ocsp-support/tests/README.md @@ -0,0 +1,182 @@ +# OCSP Support Test Plan + +## Introduction + +The prose tests defined in this file are platform-independent tests that drivers can use to prove their conformance to +the OCSP Support specification. These tests MUST BE implemented by all drivers. + +Additional YAML and JSON tests have also been added to the [URI Options Tests](../../uri-options/tests/README.md). +Specifically, the [TLS Options Test](../../uri-options/tests/tls-options.yml) has been updated with additional tests for +the new URI options `tlsDisableOCSPEndpointCheck` and `tlsDisableCertificateRevocationCheck`. + +Tests involving the new URI options MUST BE implemented by drivers that are able to support those new URI options (see +[MongoClient Configuration](../ocsp-support.md#mongoclient-configuration)). + +## Testing Invalid Configurations + +The following tests only apply to drivers that expose an option to enable/disable certificate revocation checking on a +per MongoClient basis. + +1. Create a MongoClient with `tlsInsecure=true`. +2. Enable certificate revocation checking on the MongoClient. +3. Ensure that an error is thrown noting that revocation checking cannot be used in combination with `tlsInsecure=true`. + +The following test only applies to drivers that expose an option to enable/disable certificate revocation checking on a +per MongoClient basis and implement support for `tlsAllowInvalidCertificates`. + +1. Create a MongoClient with `tlsAllowInvalidCertificates=true`. +2. Enable certificate revocation checking on the MongoClient +3. Ensure that an error is thrown noting that revocation checking cannot be used in combination with + `tlsAllowInvalidCertificates=true`. + +## Integration Tests: Permutations to Be Tested + +For integration tests, we will test all combinations of URI options that influence a driver's OCSP behavior with both +validity states of the server's certificate (configured with the mock OCSP responder). We will also test the case where +an OCSP responder is unavailable and two malicious server cases. + +Drivers that do not default to enabling OCSP MUST enable OCSP for these tests. + +| **URI options** | **Test 1:** **Valid cert + server that staples** | **Test 2:** **Revoked cert + server that staples** | **Test 3:** **Valid cert + server that does not staple** | **Test 4:** **Revoked cert + server that does not staple** | **Soft Fail Test:** **No OCSP Responder + server that does not staple** | **Malicious Server Test 1:** **Revoked cert + server w/ Must- Staple cert that does not staple** | **Malicious Server Test 2: No OCSP Responder + server w/ Must-** **Staple cert that does not staple** | +| -------------------------------------------------- | ------------------------------------------------ | -------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | +| `tls=true` | OK | FAIL | OK | FAIL | OK\* | FAIL\* | FAIL\* | +| `tls=true`
`&tlsInsecure=true` | OK | OK | OK | OK | OK | OK | OK | +| `tls=true`
`&tlsAllowInvalidCertificates=true` | OK | OK | OK | OK | OK | OK | OK | + +See [Required Server Versions](../ocsp-support.md#required-server-versions) to determine which versions of the server +can be used for each column. + +Note: From the perspective of a driver that does not support OCSP stapling, the following sets of tests should be +identical: {Test 1, Test 3}, {Test 2, Test 4, Malicious Server Test 1}, and {Soft Fail Test, Malicious Server Test 2}. +For drivers with full control over their OCSP behavior, both malicious server tests are identical as well. However, it +does no harm to test these extra cases and may help reveal unexpected behavior. + +\*: Drivers that cannot pass these tests due to limitations in their TLS library's implementation of OCSP will need to +document these failures as described under [Documentation Requirements](../ocsp-support.md#documentation-requirements). +Additionally, drivers that fail the "Soft Fail Test" will need to change any applicable defaults as described under +[MongoClient Configuration](../ocsp-support.md#mongoclient-configuration) + +## Mock OCSP Responder Testing Suite + +The certificates and scripts needed for testing OCSP are part of +[driver-evergreen-tools](https://github.com/mongodb-labs/drivers-evergreen-tools) in `.evergreen/ocsp`. The `rsa` and +`ecdsa` directories contain the certificates and scripts needed to test with RSA and ECDSA certificates, respectively. + +Both of the above directories contain four scripts: `mock-valid.sh`, `mock-revoked.sh`, `mock-delegate-valid.sh`, and +`mock-delegate-revoked.sh`. + +`mock-valid.sh` starts up a mock OCSP responder that uses the issuing CA's certificate. This responder will report that +every certificate is valid. + +`mock-revoked.sh` starts up a mock OCSP responder that uses the issuing CA's certificate. This responder will report +that every certificate is revoked. + +`mock-delegate-valid-valid.sh` starts up a mock OCSP responder that uses a delegate certificate. This responder will +report that every certificate is valid. + +`mock-delegate-revoked.sh` starts up a mock OCSP that uses a delegate certificate. This responder will report that every +certificate is revoked. + +### mongo-orchestration configurations + +The mongo-orchestration configurations needed for testing can be found at `.evergreen/orchestration/configs/servers/`. + +#### RSA Tests + +Tests that specify that a server should staple MUST use `rsa-basic-tls-ocsp-mustStaple.json`. Tests that specify that a +server should not staple MUST use `rsa-basic-tls-ocsp-disableStapling.json`. The malicious server tests MUST use +`rsa-basic-tls-ocsp-mustStaple-disableStapling.json`. + +#### ECDSA Tests + +Tests that specify that a server should staple MUST use `ecdsa-basic-tls-ocsp-mustStaple.json`. Tests that specify that +a server should not staple MUST use `ecdsa-basic-tls-ocsp-disableStapling.json`. The malicious server tests MUST use +`ecdsa-basic-tls-ocsp-mustStaple-disableStapling.json`. + +## Test Procedure + +Each column that utilizes an OCSP responder represents four tests: + +1. A test with RSA certificates and an OCSP responder that uses the issuing CA's certificate +2. A test with RSA certificates and an OCSP responder that uses a delegate certificate +3. A test with ECDSA certificates and an OCSP responder that uses the issuing CA's certificate +4. A test with ECDSA certificates and an OCSP responder that uses a delegate certificate + +Each column that does not utilize an OCSP responder (i.e. "Soft Fail Test" and "Malicious Server Test 2") represent two +tests: + +1. A test with RSA certificates +2. A test with ECDSA certificates + +Each test MUST BE its own Evergreen task in order to minimize the impact of OCSP caching. OCSP caching can exist at the +OS-level, user-level and/or application-level; having separate Evergreen tasks should help minimize the impact of +user-level and application-level caching since Evergreen performs some cleanup between test runs. + +Since each test column represents four tests, and each test is run as a separate Evergreen task, each Evergreen task +SHOULD set a `batchtime` of 14 days to reduce how often these tests run (this will not affect patch builds). + +Any OCSP caches that persist between test runs (e.g. the OS-level OCSP cache) MUST be cleared before configuring a +certificate chain. This is important because the Evergreen instance that is running a driver's test may have a cached +response from a previous test run (Evergreen instances are generally reused between test runs), and this cached result +could lead the driver or server to read stale data. See the +[Appendix](../ocsp-support.md#os-level-ocsp-cache-manipulation) for instructions on how to clear OS-level OCSP caches. + +For each test, ensure that the mock OCSP responder is configured to use the correct certificate and to report the +expected revocation status for that certificate (see +[Mock OCSP Responder Testing Suite](#mock-ocsp-responder-testing-suite) for configuration details) and that a `mongod` +is running with the correct certificate chain. The mock OCSP responder MUST BE started before the `mongod` as the +`mongod` expects that a responder will be available upon startup. Again, each test MUST BE its own Evergreen task in +order to minimize the impact of user-level and application-level OCSP caching + +To assert whether a test passes or fails, drivers SHOULD create a MongoClient with the options specified under "URI +options", connect to a server and attempt to issue a ping command. The success or failure (due to a TLS error) of the +ping command should correlate with the expected test result. + +Drivers may wish to use a smaller value for `serverSelectionTimeoutMS` to speed up tests (otherwise server selection +will spin for the entire duration even after a driver encounters a TLS error early). + +### Testing on Linux + +Drivers MUST test on Linux platforms that have server support for OCSP stapling. + +- [SERVER-51364](https://jira.mongodb.org/browse/SERVER-51364) disables OCSP stapling on the server for Ubuntu 18.04. +- [SERVER-56848](https://jira.mongodb.org/browse/SERVER-56848) is a known bug with Go clients and versions of RHEL 8 + before 8.3. + +Consider using RHEL 7.0 or Ubuntu 20.04 as alternative platforms. + +### Testing on Windows and macOS + +Until [SPEC-1589](http://jira.mongodb.org/browse/SPEC-1589) and [SPEC-1645](https://jira.mongodb.org/browse/SPEC-1645) +are resolved, drivers can only test with ECDSA certificates on Linux and thus, on Windows and macOS, drivers can only +test with RSA certificates. Therefore, when testing on Windows and macOS, each column in the test matrix that utilizes +an OCSP responder represents only two tests: + +1. A test with RSA certificates and an OCSP responder that uses the issuing CA's certificate +2. A test with RSA certificates and an OCSP responder that uses a delegate certificate + +Additionally, because the Windows and macOS `mongod` do not support stapling when a client connects, the following sets +of tests will be identical even if a driver supports stapled OCSP: {Test 1, Test 3} and {Test 2, Test 4}. Therefore, +when testing on Windows and macOS, a driver MAY skip Test 1 and Test 2 if desired. A driver MAY also simply choose to +run all the tests in the table, irrespective of OS, in order to simplify the testing procedure. + +## Changelog + +**2024-08-20**: Migrated from reStructuredText to Markdown. + +**2021-11-08**: Clarify that not all Linux platforms support server stapling. + +**2020-07-01**: Clarify that drivers that do not enable OCSP by default MUST enable OCSP for the tests. + +**2020-03-20**: Clarify that the mock OCSP responder must be started before the mongod. + +**2020-03-11**: Reduce and clarify Windows testing requirements. + +**2020-03-05**: Add tests for tlsDisableCertificateRevocationCheck to URI Options tests. Move/add OCSP URI options +default tests to separate file. + +**2021-02-27**: Add delegate responders and ECDSA certificate testing. + +**2020-02-26**: Add additional URI Options Tests. + +**2020-01-16**: Initial commit. diff --git a/source/open-telemetry/open-telemetry.md b/source/open-telemetry/open-telemetry.md new file mode 100644 index 0000000000..f7fa6a9c59 --- /dev/null +++ b/source/open-telemetry/open-telemetry.md @@ -0,0 +1,429 @@ +# OpenTelemetry + +- Title: OpenTelemetry +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This specification defines requirements for drivers' OpenTelemetry integration and behavior. Drivers will trace database +commands and driver operations with a pre-defined set of attributes when OpenTelemetry is enabled and configured in an +application. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**Host Application** + +An application that uses the MongoDB driver. + +**Span** + +A Span represents a single operation within a trace. Spans can be nested to form a trace tree. Each trace contains a +root span, which typically describes the entire operation and, optionally, one or more sub-spans for its sub-operations. + +Spans encapsulate: + +- The span name +- An immutable SpanContext that uniquely identifies the Span +- A parent span in the form of a Span, SpanContext, or null +- A SpanKind +- A start timestamp +- An end timestamp +- Attributes +- A list of links to other Spans +- A list of timestamped Events +- A Status + +### Implementation Requirements + +#### External Dependencies + +**OpenTelemetry API and SDK** + +OpenTelemetry offers two components for implementing instrumentation – API and SDK. The OpenTelemetry API provides all +the necessary types and method signatures. If there is no OpenTelemetry SDK available at runtime, API methods are +no-ops. OpenTelemetry SDK is an actual implementation of the API. If the SDK is available, API methods do work. + +Drivers MAY add a dependency to the corresponding OpenTelemetry API. This is the recommended way for implementing +OpenTelemetry in libraries. Alternatively, drivers can implement OpenTelemetry support using any suitable tools within +the driver ecosystem. Drivers MUST NOT add a dependency to OpenTelemetry SDK. + +#### Enabling, Disabling, and Configuring OpenTelemetry + +OpenTelemetry SHOULD be disabled by default. + +Drivers SHOULD support configuring OpenTelemetry on multiple levels. + +- **MongoClient Level**: Drivers SHOULD provide a configuration option for `MongoClient`'s Configuration/Settings that + enables or disables tracing for operations and commands executed with this client. This option MUST override + settings on higher levels. This configuration can be implemented with a `MongoClient` option. See + [Client Options](#client-options) section below. +- **Driver Level**: Drivers SHOULD provide a global setting that enables or disables OpenTelemetry for all `MongoClient` + instances (excluding those that explicitly override the setting). This configuration SHOULD be implemented with an + environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED`. Drivers MAY provide other means to globally + disable OpenTelemetry that are more suitable for their language ecosystem. This option MUST override settings on the + higher level. +- **Host Application Level**: If the host application enables OpenTelemetry for all available instrumentations (e.g., + Ruby), and a driver can detect this, OpenTelemetry SHOULD be enabled in the driver. + +Drivers MUST NOT try to detect whether the OpenTelemetry SDK library is available, and enable tracing based on this. +Drivers MUST NOT add means that configure OpenTelemetry SDK (e.g., setting a specific exporter). Drivers MUST NOT add +means that configure OpenTelemetry on the host application level (e.g., setting a specific sampler). + + + +##### `MongoClient` Options for configuring OpenTelemetry on the client level + +Drivers SHOULD add new options to `MongoClient` for configuring OpenTelemetry as described above. These options SHOULD +be a dictionary that groups all OpenTelemetry-related options, the name of the dictionary SHOULD be `tracing`. The +dictionary SHOULD contain at least the following options: + +- `enabled`: A boolean that enables or disables OpenTelemetry for this `MongoClient` instance. Default is `false` (i.e., + use the driver-level setting). +- `query_text_max_length`: An integer that sets the maximum length of the `db.query.text` attribute of command spans. + Default is `0` (i.e., do not add the attribute). + +#### Environment Variables for configuring OpenTelemetry + +Drivers SHOULD support configuration of OpenTelemetry on the driver level via at least the following environment +variables: + +- `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED`: enables OpenTelemetry when set to `1`, `true`, `yes`. +- `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_QUERY_TEXT_MAX_LENGTH`: An integer that sets the maximum length of the + `db.query.text` attribute of command spans. Default is `0` (i.e., do not add the attribute). + +#### Tracer Attributes + +If a driver creates a Tracer using OpenTelemetry API, drivers MUST use the following attributes: + +- `name`: A string that identifies the driver. It can be the name of a driver's component (e.g., "mongo", "PyMongo") or + a package name (e.g., "com.mongo.Driver"). Drivers SHOULD select a name that is idiomatic for their language and + ecosystem. Drivers SHOULD follow the Instrumentation Scope guidance. +- `version`: A string that represents internal driver version. The version formatting is not defined; drivers SHOULD + apply the same formatting as the use for `client.driver.version` attribute of the + [handshake](https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md#clientdriverversion). + +#### Instrumenting Driver Operations + +When a user calls the driver's public API, the driver MUST create a span for every driver operation. Drivers MUST start +the span as soon as possible so that the span’s duration reflects all activities made by the driver, such as server +selection and serialization/deserialization. + +The span for the operation MUST be created within the current span of the host application, with the exceptions listed +below. + +##### Transactions + +When a user starts a transaction with `startTransaction`, the driver SHOULD create a span for the pseudo operation +`transaction`. This span MUST have only one attribute `db.system.name` with the value `mongodb`. All operations executed +within the transaction SHOULD be nested to the `transaction` span. + +When a user commits or aborts a transaction with `commitTransaction` or `abortTransaction`, the driver SHOULD finish the +`transaction` span. + +##### `withTransaction` + +In case of `withTransaction` operation spans for operations that are executed inside the callbacks SHOULD be nested into +the `withTransaction` span. + +##### Operation Span Name + +The span name SHOULD be: + +- `driver_operation_name db.collection_name` if the operation is executed on a collection (e.g., + `collection.findOneAndDelete(filter)` will report `findAndModify warehouse_db.users_coll`). + +**Note**: since the `findOneAndDelete` operation is implemented as a `findAndModify` command, the operation name in the +span is `findAndModify`. This ensures consistency between drivers when naming operations. See the +[covered operations](#covered-operations) table below for mapping of public API methods to operation names. + +- `driver_operation_name db` if there is no specific collection for the operation (e.g., `runCommand warehouse`). + +##### Operation Span Kind + +Span kind MUST be "client". + +##### Operation Span Attributes + +Spans SHOULD have the following attributes: + +| Attribute | Type | Description | Requirement Level | +| :--------------------- | :------- | :------------------------------------------------------------------------- | :-------------------- | +| `db.system.name` | `string` | MUST be 'mongodb' | Required | +| `db.namespace` | `string` | The database name | Required if available | +| `db.collection.name` | `string` | The collection being accessed within the database stated in `db.namespace` | Required if available | +| `db.operation.name` | `string` | The name of the driver operation being executed | Required | +| `db.operation.summary` | `string` | Equivalent to span name | Required | + +Not all attributes are available at the moment of span creation. Drivers need to add attributes at later stages, which +requires an operation span to be available throughout the complete operation lifecycle. + +###### db.namespace + +This attribute SHOULD be set to current database name except for operations executing against admin db ex: (transaction, +client `bulkWrite`) operations. + +Examples: + +- `find` on `test.users` → `test` +- `runCommand` on `admin` → `admin` +- `commitTransaction` → `admin` +- `abortTransaction` → `admin` +- client `bulkWrite` → `admin` + +The name of this attribute is defined in the +[OpenTelemetry Specifications](https://opentelemetry.io/docs/specs/semconv/registry/attributes/db/#db-namespace). In +order to be compliant with these, we use this name even though the term `namespace` has a different +[meaning](https://www.mongodb.com/docs/manual/reference/glossary/) for MongoDB. + +###### db.collection.name + +This attribute should be set to the user's collection if the operation is executing against a collection, this field is +omitted for commands running against `admin` database or commands that do not target a specific collection. + +Examples: + +- `find` on `test.users` → `users` +- `runCommand` on `admin` → *omitted* +- `commitTransaction` → *omitted* +- `abortTransaction` → *omitted* +- client `bulkWrite` → *omitted* + +##### Exceptions + +If the driver operation fails with an exception, drivers MUST record an exception to the current operation span. This +does not relate to exceptions that happen on the server command level (see below). Those exceptions MUST be recorded to +the corresponding command span. + +When recording an exception, drivers SHOULD add the following attributes to the span, when the content for the attribute +if available: + +- `exception.message` +- `exception.type` +- `exception.stacktrace` + +#### Instrumenting Server Commands + +Drivers MUST create a span for every server command sent to the server as a result of a public API call, except for +sensitive commands as listed in the command logging and monitoring specification. + +Spans for commands MUST be nested to the span for the corresponding driver operation span. If the command is being +retried, the driver MUST create a separate span for each retry; all the retries MUST be nested to the same operation +span. + +##### Command Span Name + +The span name SHOULD be the command name. For example, `find`, `insert`, `update`, etc. + +##### Command Span Kind + +Span kind MUST be "client". + +##### Command Span Attributes + +Spans SHOULD have the following attributes: + +| Attribute | Type | Description | Requirement Level | +| :-------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | +| `db.system.name` | `string` | MUST be 'mongodb' | Required | +| `db.namespace` | `string` | The database name | Required if available | +| `db.collection.name` | `string` | The collection being accessed within the database stated in `db.namespace` | Required if available | +| `db.command.name` | `string` | The name of the server command being executed | Required | +| `db.response.status_code` | `string` | MongoDB error code represented as a string. This attribute should be added only if an error happens. | Required if an error happens | +| `server.port` | `int64` | Server port number | Required | +| `server.address` | `string` | Name of the database host, or IP address if name is not known | Required | +| `network.transport` | `string` | MUST be 'tcp' or 'unix' depending on the protocol | Required | +| `db.query.summary` | `string` | (see explanation below) | Required | +| `db.mongodb.server_connection_id` | `int64` | Server connection id | Required if available | +| `db.mongodb.driver_connection_id` | `int64` | Local connection id | Required if available | +| `db.query.text` | `string` | Database command that was sent to the server. Content should be equivalent to the `document` field of the CommandStartedEvent of the command monitoring. | Conditional | +| `db.mongodb.cursor_id` | `int64` | If a cursor is created or used in the operation | Required if available | +| `db.mongodb.lsid` | `string` | Logical session id | Required if available | +| `db.mongodb.txn_number` | `int64` | Transaction number | Required if available | + +Besides the attributes listed in the table above, drivers MAY add other attributes from the +[Semantic Conventions for Databases](https://opentelemetry.io/docs/specs/semconv/registry/attributes/db/) that are +applicable to MongoDB. + +###### db.namespace + +This attribute SHOULD be set to current database name except for operations executing against admin db ex: (transaction, +client `bulkWrite`) operations. + +Examples: + +- `find` on `test.users` → `test` +- `runCommand` on `admin` → `admin` +- `commitTransaction` → `admin` +- `abortTransaction` → `admin` +- client `bulkWrite` → `admin` + +The name of this attribute is defined in the +[OpenTelemetry Specifications](https://opentelemetry.io/docs/specs/semconv/registry/attributes/db/#db-namespace). In +order to be compliant with these, we use this name even though the term `namespace` has a different +[meaning](https://www.mongodb.com/docs/manual/reference/glossary/) for MongoDB. + +###### db.collection.name + +This attribute should be set to the user's collection if the operation is executing against a collection, this field is +omitted for commands running against `admin` database or commands that do not target a specific collection. + +Examples: + +- `find` on `test.users` → `users` +- `runCommand` on `admin` → *omitted* +- `commitTransaction` → *omitted* +- `abortTransaction` → *omitted* +- client `bulkWrite` → *omitted* + +###### db.query.summary + +This attribute SHOULD contain: + +- `command_name db.collection_name` if the command is executed on a collection. +- `command_name db` if there is no specific collection for the command. +- `command_name admin` in other cases (e.g., commands executed against `admin` database, transaction or client + `bulkWrite`). + +###### db.query.text + +This attribute contains the full database command executed serialized to extended JSON. If not truncated, the content of +this attribute SHOULD be equivalent to the `document` field of the CommandStartedEvent of the command monitoring +excluding the following fields: `lsid`, `$db`, `$clusterTime`, `signature`. + +Drivers MUST NOT add this attribute by default. Drivers MUST provide a toggle to enable this attribute. This +configuration can be implemented with an environment variable +`OTEL_#{LANG}_INSTRUMENTATION_MONGODB_QUERY_TEXT_MAX_LENGTH` set to a positive integer value. The attribute will be +added and truncated to the provided value (similar to the Logging specification). + +On the `MongoClient` level this configuration can be implemented with a `MongoClient` option, for example, +`tracing.query_text_max_length`. + +###### db.mongodb.cursor_id + +If the command returns a cursor, or uses a cursor, the `cursor_id` attribute SHOULD be added. + +##### Exceptions + +If the server command fails with an exception, drivers MUST record an exception to the current command span. When +recording an exception, drivers SHOULD add the following attributes to the span, when the content for the attribute if +available: + +- `exception.message` +- `exception.type` +- `exception.stacktrace` + +## Motivation for Change + +A common complaint from our support team is that they don't know how to easily get debugging information from drivers. +Some drivers provide debug logging, but others do not. For drivers that do provide it, the log messages produced and the +mechanisms for enabling debug logging are inconsistent. + +OpenTelemetry is currently the industry standard for instrumenting, generating, and collecting telemetry data (metrics, +logs, and traces). By instrumenting our drivers natively, we allow our end users to collect traces in a +batteries-included way, with the additional benefits that the tracing is developed and maintained in-house, and conforms +to the open-source standard for tracing. This also ensures that traces generated on the client-side on driver operations +can tie into other traces, thus giving our end users a broader picture of the network hops that a single request might +take. + +## Test Plan + +See [OpenTelemetry Tests](tests/README.md) for the test plan. + +## Covered operations + +The OpenTelemetry specification covers all driver operations including but not limited to the following operations: + +| Operation | Test | +| :----------------------- | :----------------------------------------------------------------------------- | +| `aggregate` | [tests/operation/aggregate.yml](tests/operation/aggregate.yml) | +| `findAndModify` | [tests/operation/find_and_modify.yml](tests/operation/find_and_modify.yml) | +| `bulkWrite` | [tests/operation/bulk_write.yml](tests/operation/bulk_write.yml) | +| `commitTransaction` | [tests/transaction/core_api.yml](tests/transaction/core_api.yml) | +| `abortTransaction` | [tests/transaction/core_api.yml](tests/transaction/core_api.yml) | +| `withTransaction` | [tests/transaction/convenient.yml](tests/transaction/convenient.yml) | +| `createCollection` | [tests/operation/create_collection.yml](tests/operation/create_collection.yml) | +| `createIndexes` | [tests/operation/create_indexes.yml](tests/operation/create_indexes.yml) | +| `distinct` | [tests/operation/distinct.yml](tests/operation/distinct.yml) | +| `dropCollection` | [tests/operation/drop_collection.yml](tests/operation/drop_collection.yml) | +| `dropIndexes` | [tests/operation/drop_indexes.yml](tests/operation/drop_indexes.yml) | +| `find` | [tests/operation/find.yml](tests/operation/find.yml) | +| `listCollections` | [tests/operation/list_collections.yml](tests/operation/list_collections.yml) | +| `listDatabases` | [tests/operation/list_databases.yml](tests/operation/list_databases.yml) | +| `listIndexes` | [tests/operation/list_indexes.yml](tests/operation/list_indexes.yml) | +| `mapReduce` | [tests/operation/map_reduce.yml](tests/operation/map_reduce.yml) | +| `estimatedDocumentCount` | [tests/operation/count.yml](tests/operation/count.yml) | +| `insert` | [tests/operation/insert.yml](tests/operation/insert.yml) | +| `delete` | [tests/operation/delete.yml](tests/operation/delete.yml) | +| `update` | [tests/operation/update.yml](tests/operation/update.yml) | +| `createSearchIndexes` | [tests/operation/atlas_search.yml](tests/operation/atlas_search.yml) | +| `dropSearchIndex` | [tests/operation/atlas_search.yml](tests/operation/atlas_search.yml) | +| `updateSearchIndex` | [tests/operation/delete.yml](tests/operation/delete.yml) | +| `delete` | [tests/operation/atlas_search.yml](tests/operation/atlas_search.yml) | + +## Backwards Compatibility + +Introduction of OpenTelemetry in new driver versions should not significantly affect existing applications that do not +enable OpenTelemetry. However, since the no-op tracing operation may introduce some performance degradation (though it +should be negligible), customers should be informed of this feature and how to disable it completely. + +If a driver is used in an application that has OpenTelemetry enabled, customers will see traces from the driver in their +OpenTelemetry backends. This may be unexpected and MAY cause negative effects in some cases (e.g., the OpenTelemetry +backend MAY not have enough capacity to process new traces). Customers should be informed of this feature and how to +disable it completely. + +## Security Implication + +Drivers MUST take care to avoid exposing sensitive information (e.g. authentication credentials) in traces. Drivers +SHOULD follow the +[Security](https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.md#security) +guidance of the Command Logging and Monitoring spec. + +## Future Work + +### Query Parametrization + +It might be beneficial to implement query parametrization for the `db.query.text` attribute. + +One might want to replace dynamic values in queries with placeholders. For example, the query + +```json +{ find: "users", filter: { age: { $gt: 30 } } } +``` + +will be transformed to + +```json +{ find: "users", filter: { age: { $gt: "?" } } } +``` + +for purposes of obfuscating queries in traces or query aggregation. + +In the case of CSFLE, the query might already have BSON binary values (with the encrypted subtype) by the time the query +is sent along for tracing, which can also be considered a form of parameterization. In that case, a driver could easily +replace those binary values with placeholders (assuming the encrypted blobs are irrelevant for logging). + +## Design Rationale + +### No URI options + +Enabling tracing may have performance and security implications. Copying and using a connection string that enables +tracing may have unexpected negative outcome. + +Further, we already have two attributes that configure tracing, and we expect there might be more. + +A URI options can be added later if we realise our users need it, while the opposite is not easily accomplished. + +## Changelog + +- 2026-02-09: Renamed `db.system` to `db.system.name` according to the corresponding update of OpenTelemetry semantic + conventions. diff --git a/source/open-telemetry/tests/README.md b/source/open-telemetry/tests/README.md new file mode 100644 index 0000000000..99bcc8d293 --- /dev/null +++ b/source/open-telemetry/tests/README.md @@ -0,0 +1,60 @@ +# OpenTelemetry Tests + +______________________________________________________________________ + +## Testing + +### Automated Tests + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +the OpenTelemetry specification. These tests utilize the +[Unified Test Format](../../unified-test-format/unified-test-format.md). + +For each test, create a MongoClient, configure it to enable tracing. + +```yaml +createEntities: + - client: + id: client0 + observeTracingMessages: + enableCommandPayload: true +``` + +These tests require the ability to collect tracing [spans](../open-telemetry.md) data in a structured form as described +in the +[Unified Test Format specification.expectTracingMessages](../../unified-test-format/unified-test-format.md#expectTracingMessages). +For example the Java driver uses [Micrometer](https://jira.mongodb.org/browse/JAVA-5732) to collect tracing spans. + +```yaml +expectTracingMessages: + client: client0 + ignoreExtraSpans: false + spans: + ... +``` + +### Prose Tests + +*Test 1: Tracing Enable/Disable via Environment Variable* + +1. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED` to `false`. +2. Create a `MongoClient` without explicitly enabling tracing. +3. Perform a database operation (e.g., `find()` on a test collection). +4. Assert that no OpenTelemetry tracing spans are emitted for the operation. +5. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED` to `true`. +6. Create a new `MongoClient` without explicitly enabling tracing. +7. Perform the same database operation. +8. Assert that OpenTelemetry tracing spans are emitted for the operation. + +*Test 2: Command Payload Emission via Environment Variable* + +1. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_ENABLED` to `true`. +2. Set the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_QUERY_TEXT_MAX_LENGTH` to a positive integer + (e.g., 1024). +3. Create a `MongoClient` without explicitly enabling command payload emission. +4. Perform a database operation (e.g., `find()`). +5. Assert that the emitted tracing span includes the `db.query.text` attribute. +6. Unset the environment variable `OTEL_#{LANG}_INSTRUMENTATION_MONGODB_QUERY_TEXT_MAX_LENGTH`. +7. Create a new `MongoClient`. +8. Perform the same database operation. +9. Assert that the emitted tracing span does not include the `db.query.text` attribute. diff --git a/source/open-telemetry/tests/operation/aggregate.json b/source/open-telemetry/tests/operation/aggregate.json new file mode 100644 index 0000000000..07c30a6761 --- /dev/null +++ b/source/open-telemetry/tests/operation/aggregate.json @@ -0,0 +1,127 @@ +{ + "description": "operation aggregate", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-aggregate" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "aggregation", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "aggregate operation-aggregate.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-aggregate", + "db.collection.name": "test", + "db.operation.name": "aggregate", + "db.operation.summary": "aggregate operation-aggregate.test" + }, + "nested": [ + { + "name": "aggregate", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-aggregate", + "db.collection.name": "test", + "db.command.name": "aggregate", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "aggregate operation-aggregate.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/aggregate.yml b/source/open-telemetry/tests/operation/aggregate.yml new file mode 100644 index 0000000000..f43bca3322 --- /dev/null +++ b/source/open-telemetry/tests/operation/aggregate.yml @@ -0,0 +1,62 @@ +description: operation aggregate +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-aggregate + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: aggregation + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline0 + - $match: { _id: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: aggregate operation-aggregate.test + attributes: + db.system.name: mongodb + db.namespace: operation-aggregate + db.collection.name: test + db.operation.name: aggregate + db.operation.summary: aggregate operation-aggregate.test + + nested: + - name: aggregate + attributes: + db.system.name: mongodb + db.namespace: operation-aggregate + db.collection.name: *collectionName0 + db.command.name: aggregate + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: aggregate operation-aggregate.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + aggregate: test + pipeline: *pipeline0 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/atlas_search.json b/source/open-telemetry/tests/operation/atlas_search.json new file mode 100644 index 0000000000..e961e7a261 --- /dev/null +++ b/source/open-telemetry/tests/operation/atlas_search.json @@ -0,0 +1,317 @@ +{ + "description": "operation atlas_search", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-atlas-search" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "runOnRequirements": [ + { + "minServerVersion": "7.0.5", + "maxServerVersion": "7.0.99", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "7.2.0", + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "atlas search indexes", + "operations": [ + { + "name": "createSearchIndex", + "object": "collection0", + "arguments": { + "model": { + "definition": { + "mappings": { + "dynamic": true + } + }, + "type": "search" + } + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + }, + { + "name": "updateSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index", + "definition": {} + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + }, + { + "name": "dropSearchIndex", + "object": "collection0", + "arguments": { + "name": "test index" + }, + "expectError": { + "isError": true, + "errorContains": "Atlas" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createSearchIndexes operation-atlas-search.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "createSearchIndexes", + "db.operation.summary": "createSearchIndexes operation-atlas-search.test" + }, + "nested": [ + { + "name": "createSearchIndexes", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "createSearchIndexes", + "db.collection.name": "test", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "createSearchIndexes operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "createSearchIndexes": "test", + "indexes": [ + { + "type": "search", + "definition": { + "mappings": { + "dynamic": true + } + } + } + ] + } + } + } + } + } + ] + }, + { + "name": "updateSearchIndex operation-atlas-search.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "updateSearchIndex", + "db.operation.summary": "updateSearchIndex operation-atlas-search.test" + }, + "nested": [ + { + "name": "updateSearchIndex", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "updateSearchIndex", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "updateSearchIndex operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "updateSearchIndex": "test", + "name": "test index", + "definition": {} + } + } + } + } + } + ] + }, + { + "name": "dropSearchIndex operation-atlas-search.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.collection.name": "test", + "db.operation.name": "dropSearchIndex", + "db.operation.summary": "dropSearchIndex operation-atlas-search.test" + }, + "nested": [ + { + "name": "dropSearchIndex", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-atlas-search", + "db.command.name": "dropSearchIndex", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": true + }, + "exception.message": { + "$$exists": true + }, + "exception.type": { + "$$exists": true + }, + "exception.stacktrace": { + "$$exists": true + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "dropSearchIndex operation-atlas-search.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "dropSearchIndex": "test", + "name": "test index" + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/atlas_search.yml b/source/open-telemetry/tests/operation/atlas_search.yml new file mode 100644 index 0000000000..de3dff77c8 --- /dev/null +++ b/source/open-telemetry/tests/operation/atlas_search.yml @@ -0,0 +1,170 @@ +description: operation atlas_search +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-atlas-search + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + +runOnRequirements: + # Skip server versions without fix of SERVER-83107 to avoid error message "BSON field 'createSearchIndexes.indexes.type' is an unknown field." + # SERVER-83107 was not backported to 7.1. + - minServerVersion: "7.0.5" + maxServerVersion: "7.0.99" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + - minServerVersion: "7.2.0" + topologies: [ replicaset, load-balanced, sharded ] + serverless: forbid + + +tests: + - description: atlas search indexes + operations: + - name: createSearchIndex + object: *collection0 + arguments: + model: { definition: { mappings: { dynamic: true } } , type: 'search' } + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + - name: updateSearchIndex + object: *collection0 + arguments: + name: 'test index' + definition: {} + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + - name: dropSearchIndex + object: *collection0 + arguments: + name: 'test index' + expectError: + # This test always errors in a non-Atlas environment. The test functions as a unit test by asserting + # that the driver constructs and sends the correct command. + # The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages. + isError: true + errorContains: Atlas + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createSearchIndexes operation-atlas-search.test + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: createSearchIndexes + db.operation.summary: createSearchIndexes operation-atlas-search.test + nested: + - name: createSearchIndexes + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.command.name: createSearchIndexes + db.collection.name: test + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: createSearchIndexes operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + createSearchIndexes: test + indexes: [ { "type": "search", "definition": { "mappings": { "dynamic": true } } } ] + + - name: updateSearchIndex operation-atlas-search.test + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: updateSearchIndex + db.operation.summary: updateSearchIndex operation-atlas-search.test + + nested: + - name: updateSearchIndex + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.command.name: updateSearchIndex + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: updateSearchIndex operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + updateSearchIndex: test + name: test index + definition: {} + + - name: dropSearchIndex operation-atlas-search.test + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.collection.name: test + db.operation.name: dropSearchIndex + db.operation.summary: dropSearchIndex operation-atlas-search.test + nested: + - name: dropSearchIndex + attributes: + db.system.name: mongodb + db.namespace: operation-atlas-search + db.command.name: dropSearchIndex + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: true } + exception.message: { $$exists: true } + exception.type: { $$exists: true } + exception.stacktrace: { $$exists: true } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: dropSearchIndex operation-atlas-search.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + dropSearchIndex: test + name: test index diff --git a/source/open-telemetry/tests/operation/bulk_write.json b/source/open-telemetry/tests/operation/bulk_write.json new file mode 100644 index 0000000000..312a55f383 --- /dev/null +++ b/source/open-telemetry/tests/operation/bulk_write.json @@ -0,0 +1,333 @@ +{ + "description": "operation bulk_write", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-bulk-write-0" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "operation-bulk-write-1" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test1" + } + } + ], + "initialData": [ + { + "collectionName": "test0", + "databaseName": "operation-bulk-write-0", + "documents": [] + }, + { + "collectionName": "test1", + "databaseName": "operation-bulk-write-1", + "documents": [] + } + ], + "_yamlAnchors": { + "namespace0": "operation-bulk-write-0.test0", + "namespace1": "operation-bulk-write-1.test1" + }, + "tests": [ + { + "description": "bulkWrite", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "operation-bulk-write-0.test0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "operation-bulk-write-0.test0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "operation-bulk-write-0.test0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "operation-bulk-write-1.test1", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ] + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "bulkWrite admin", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "bulkWrite", + "db.operation.summary": "bulkWrite admin" + }, + "nested": [ + { + "name": "bulkWrite", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "bulkWrite", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "bulkWrite admin", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "multi": false, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + } + }, + { + "update": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + } + }, + { + "update": 1, + "multi": false, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true + }, + { + "delete": 0, + "multi": false, + "filter": { + "_id": 5 + } + }, + { + "delete": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + ], + "nsInfo": [ + { + "ns": "operation-bulk-write-0.test0" + }, + { + "ns": "operation-bulk-write-1.test1" + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/bulk_write.yml b/source/open-telemetry/tests/operation/bulk_write.yml new file mode 100644 index 0000000000..d9a569150d --- /dev/null +++ b/source/open-telemetry/tests/operation/bulk_write.yml @@ -0,0 +1,198 @@ +description: operation bulk_write +schemaVersion: '1.27' +runOnRequirements: + - minServerVersion: "8.0" + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-bulk-write-0 + - database: + id: &database1 database1 + client: *client0 + databaseName: &databaseName1 operation-bulk-write-1 + - collection: + id: collection0 + database: *database0 + collectionName: &collectionName0 test0 + - collection: + id: collection1 + database: *database1 + collectionName: &collectionName1 test1 + +initialData: + - collectionName: *collectionName0 + databaseName: *databaseName0 + documents: [ ] + - collectionName: *collectionName1 + databaseName: *databaseName1 + documents: [ ] + +_yamlAnchors: + namespace0: &namespace0 "operation-bulk-write-0.test0" + namespace1: &namespace1 "operation-bulk-write-1.test1" + +tests: + - description: bulkWrite + operations: + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace0 + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace1 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace1 + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace0 + filter: { _id: 5 } + - deleteMany: + namespace: *namespace1 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: bulkWrite admin + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: bulkWrite + db.operation.summary: bulkWrite admin + nested: + - name: bulkWrite + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.command.name: bulkWrite + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: bulkWrite admin + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "multi": false, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + } + }, + { + "update": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + } + }, + { + "update": 1, + "multi": false, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true + }, + { + "delete": 0, + "multi": false, + "filter": { + "_id": 5 + } + }, + { + "delete": 1, + "multi": true, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + ] + nsInfo: [ + { + "ns": *namespace0 + }, + { + "ns": *namespace1 + } + ] + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/count.json b/source/open-telemetry/tests/operation/count.json new file mode 100644 index 0000000000..97a12bda29 --- /dev/null +++ b/source/open-telemetry/tests/operation/count.json @@ -0,0 +1,123 @@ +{ + "description": "operation count", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-count" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-count", + "documents": [] + } + ], + "tests": [ + { + "description": "estimated document count", + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "arguments": {}, + "expectResult": 0 + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "count operation-count.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-count", + "db.collection.name": "test", + "db.operation.name": "count", + "db.operation.summary": "count operation-count.test" + }, + "nested": [ + { + "name": "count", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-count", + "db.collection.name": "test", + "db.command.name": "count", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "count operation-count.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "count": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/count.yml b/source/open-telemetry/tests/operation/count.yml new file mode 100644 index 0000000000..8a30bc5557 --- /dev/null +++ b/source/open-telemetry/tests/operation/count.yml @@ -0,0 +1,63 @@ +description: operation count +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: database0 + client: *client0 + databaseName: &database0Name operation-count + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] +tests: + - description: estimated document count + operations: + - object: *collection0 + name: estimatedDocumentCount + arguments: { } + expectResult: 0 + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: count operation-count.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: count + db.operation.summary: count operation-count.test + nested: + - name: count + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: count + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: count operation-count.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + count: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/create_collection.json b/source/open-telemetry/tests/operation/create_collection.json new file mode 100644 index 0000000000..2cbbe8c627 --- /dev/null +++ b/source/open-telemetry/tests/operation/create_collection.json @@ -0,0 +1,110 @@ +{ + "description": "operation create collection", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-create-collection" + } + } + ], + "tests": [ + { + "description": "create collection", + "operations": [ + { + "object": "database0", + "name": "createCollection", + "arguments": { + "collection": "newlyCreatedCollection" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createCollection operation-create-collection.newlyCreatedCollection", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-create-collection", + "db.collection.name": "newlyCreatedCollection", + "db.operation.name": "createCollection", + "db.operation.summary": "createCollection operation-create-collection.newlyCreatedCollection" + }, + "nested": [ + { + "name": "create", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-create-collection", + "db.collection.name": "newlyCreatedCollection", + "db.command.name": "create", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "create operation-create-collection.newlyCreatedCollection", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "create": "newlyCreatedCollection" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/create_collection.yml b/source/open-telemetry/tests/operation/create_collection.yml new file mode 100644 index 0000000000..18147f5fd5 --- /dev/null +++ b/source/open-telemetry/tests/operation/create_collection.yml @@ -0,0 +1,55 @@ +description: operation create collection +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-create-collection +tests: + - description: create collection + operations: + - object: *database0 + name: createCollection + arguments: + collection: &collectionName newlyCreatedCollection + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createCollection operation-create-collection.newlyCreatedCollection + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collectionName + db.operation.name: createCollection + db.operation.summary: createCollection operation-create-collection.newlyCreatedCollection + nested: + - name: create + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collectionName + db.command.name: create + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: create operation-create-collection.newlyCreatedCollection + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + create: newlyCreatedCollection + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/create_indexes.json b/source/open-telemetry/tests/operation/create_indexes.json new file mode 100644 index 0000000000..ff1e86d0b2 --- /dev/null +++ b/source/open-telemetry/tests/operation/create_indexes.json @@ -0,0 +1,127 @@ +{ + "description": "operation create_indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-create-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "create indexes", + "operations": [ + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "keys": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "createIndexes operation-create-indexes.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-create-indexes", + "db.collection.name": "test", + "db.operation.name": "createIndexes", + "db.operation.summary": "createIndexes operation-create-indexes.test" + }, + "nested": [ + { + "name": "createIndexes", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-create-indexes", + "db.collection.name": "test", + "db.command.name": "createIndexes", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "createIndexes operation-create-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "createIndexes": "test", + "indexes": [ + { + "key": { + "x": 1 + }, + "name": "x_1" + } + ] + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/create_indexes.yml b/source/open-telemetry/tests/operation/create_indexes.yml new file mode 100644 index 0000000000..363a22801a --- /dev/null +++ b/source/open-telemetry/tests/operation/create_indexes.yml @@ -0,0 +1,60 @@ +description: operation create_indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-create-indexes + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test +tests: + - description: create indexes + operations: + - object: *collection0 + name: createIndex + arguments: + keys: { x: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: createIndexes operation-create-indexes.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: createIndexes + db.operation.summary: createIndexes operation-create-indexes.test + nested: + - name: createIndexes + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: createIndexes + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: createIndexes operation-create-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + createIndexes: test + indexes: [ { key: { x: 1 }, name: "x_1" } ] + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/delete.json b/source/open-telemetry/tests/operation/delete.json new file mode 100644 index 0000000000..151c7abf98 --- /dev/null +++ b/source/open-telemetry/tests/operation/delete.json @@ -0,0 +1,132 @@ +{ + "description": "operation delete", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-delete" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "delete elements", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "delete operation-delete.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-delete", + "db.collection.name": "test", + "db.operation.name": "delete", + "db.operation.summary": "delete operation-delete.test" + }, + "nested": [ + { + "name": "delete", + "attributes": { + "db.system.name": "mongodb", + "db.command.name": "delete", + "db.namespace": "operation-delete", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "delete operation-delete.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "delete": "test", + "ordered": true, + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ] + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/delete.yml b/source/open-telemetry/tests/operation/delete.yml new file mode 100644 index 0000000000..50bd72ace9 --- /dev/null +++ b/source/open-telemetry/tests/operation/delete.yml @@ -0,0 +1,62 @@ +description: operation delete +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-delete + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: delete elements + operations: + - object: *collection0 + name: deleteMany + arguments: + filter: { _id: { $gt: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: delete operation-delete.test + attributes: + db.system.name: mongodb + db.namespace: *databaseName0 + db.collection.name: *collectionName0 + db.operation.name: delete + db.operation.summary: delete operation-delete.test + nested: + - name: delete + attributes: + db.system.name: mongodb + db.command.name: delete + db.namespace: operation-delete + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: delete operation-delete.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + delete: test + ordered: true + deletes: [ { q: { _id: { $gt: 1 } }, limit: 0 } ] diff --git a/source/open-telemetry/tests/operation/distinct.json b/source/open-telemetry/tests/operation/distinct.json new file mode 100644 index 0000000000..6fdabc682d --- /dev/null +++ b/source/open-telemetry/tests/operation/distinct.json @@ -0,0 +1,127 @@ +{ + "description": "operation distinct", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-distinct" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-distinct", + "documents": [] + } + ], + "tests": [ + { + "description": "distinct on a field", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "distinct operation-distinct.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-distinct", + "db.collection.name": "test", + "db.operation.name": "distinct", + "db.operation.summary": "distinct operation-distinct.test" + }, + "nested": [ + { + "name": "distinct", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-distinct", + "db.collection.name": "test", + "db.command.name": "distinct", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "distinct operation-distinct.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "distinct": "test", + "key": "x", + "query": {} + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/distinct.yml b/source/open-telemetry/tests/operation/distinct.yml new file mode 100644 index 0000000000..d0353280b4 --- /dev/null +++ b/source/open-telemetry/tests/operation/distinct.yml @@ -0,0 +1,66 @@ +description: operation distinct +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: database0 + client: *client0 + databaseName: operation-distinct + - collection: + id: &collection0 collection0 + database: database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-distinct + documents: [] +tests: + - description: distinct on a field + operations: + - object: *collection0 + name: distinct + arguments: + fieldName: x + filter: {} + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: distinct operation-distinct.test + attributes: + db.system.name: mongodb + db.namespace: operation-distinct + db.collection.name: test + db.operation.name: distinct + db.operation.summary: distinct operation-distinct.test + nested: + - name: distinct + attributes: + db.system.name: mongodb + db.namespace: operation-distinct + db.collection.name: test + db.command.name: distinct + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: distinct operation-distinct.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + distinct: test + key: x + query: { } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/drop_collection.json b/source/open-telemetry/tests/operation/drop_collection.json new file mode 100644 index 0000000000..cc5358c1f2 --- /dev/null +++ b/source/open-telemetry/tests/operation/drop_collection.json @@ -0,0 +1,122 @@ +{ + "description": "operation drop collection", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "7.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-drop-collection" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "drop collection", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "dropCollection operation-drop-collection.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-drop-collection", + "db.collection.name": "test", + "db.operation.name": "dropCollection", + "db.operation.summary": "dropCollection operation-drop-collection.test" + }, + "nested": [ + { + "name": "drop", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-drop-collection", + "db.collection.name": "test", + "db.command.name": "drop", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "drop operation-drop-collection.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "drop": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/drop_collection.yml b/source/open-telemetry/tests/operation/drop_collection.yml new file mode 100644 index 0000000000..1b0fed2593 --- /dev/null +++ b/source/open-telemetry/tests/operation/drop_collection.yml @@ -0,0 +1,65 @@ +description: operation drop collection +schemaVersion: '1.27' + +runOnRequirements: + - minServerVersion: "7.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-drop-collection + + - collection: + id: collection0 + database: *database0 + collectionName: &collection_name test +tests: + - description: drop collection + operations: + - object: *database0 + name: dropCollection + arguments: + collection: *collection_name + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: dropCollection operation-drop-collection.test + attributes: + db.system.name: mongodb + db.namespace: operation-drop-collection + db.collection.name: test + db.operation.name: dropCollection + db.operation.summary: dropCollection operation-drop-collection.test + + nested: + - name: drop + attributes: + db.system.name: mongodb + db.namespace: operation-drop-collection + db.collection.name: test + db.command.name: drop + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: drop operation-drop-collection.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + drop: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/drop_indexes.json b/source/open-telemetry/tests/operation/drop_indexes.json new file mode 100644 index 0000000000..91126c9227 --- /dev/null +++ b/source/open-telemetry/tests/operation/drop_indexes.json @@ -0,0 +1,145 @@ +{ + "description": "operation drop indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-drop-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "client": { + "id": "clientWithoutTracing", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "databaseWithoutTracing", + "client": "clientWithoutTracing", + "databaseName": "operation-drop-indexes" + } + }, + { + "collection": { + "id": "collectionWithoutTracing", + "database": "databaseWithoutTracing", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "drop indexes", + "operations": [ + { + "name": "createIndex", + "object": "collectionWithoutTracing", + "arguments": { + "keys": { + "x": 1 + }, + "name": "x_1" + } + }, + { + "name": "dropIndexes", + "object": "collection0" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "dropIndexes operation-drop-indexes.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-drop-indexes", + "db.collection.name": "test", + "db.operation.name": "dropIndexes", + "db.operation.summary": "dropIndexes operation-drop-indexes.test" + }, + "nested": [ + { + "name": "dropIndexes", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-drop-indexes", + "db.collection.name": "test", + "db.command.name": "dropIndexes", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "dropIndexes operation-drop-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "dropIndexes": "test", + "index": "*" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/drop_indexes.yml b/source/open-telemetry/tests/operation/drop_indexes.yml new file mode 100644 index 0000000000..b82f2a3c10 --- /dev/null +++ b/source/open-telemetry/tests/operation/drop_indexes.yml @@ -0,0 +1,78 @@ +description: operation drop indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-drop-indexes + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + - client: + id: &clientWithoutTracing clientWithoutTracing + useMultipleMongoses: false + - database: + id: &databaseWithoutTracing databaseWithoutTracing + client: *clientWithoutTracing + databaseName: operation-drop-indexes + - collection: + id: &collectionWithoutTracing collectionWithoutTracing + database: *databaseWithoutTracing + collectionName: test + +tests: + - description: drop indexes + operations: + - name: createIndex + object: *collectionWithoutTracing + arguments: + keys: + x: 1 + name: x_1 + + - name: dropIndexes + object: *collection0 + + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: dropIndexes operation-drop-indexes.test + attributes: + db.system.name: mongodb + db.namespace: operation-drop-indexes + db.collection.name: test + db.operation.name: dropIndexes + db.operation.summary: dropIndexes operation-drop-indexes.test + nested: + - name: dropIndexes + attributes: + db.system.name: mongodb + db.namespace: operation-drop-indexes + db.collection.name: test + db.command.name: dropIndexes + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: dropIndexes operation-drop-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + dropIndexes: test + index: '*' + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/find.json b/source/open-telemetry/tests/operation/find.json new file mode 100644 index 0000000000..555a972b9b --- /dev/null +++ b/source/open-telemetry/tests/operation/find.json @@ -0,0 +1,291 @@ +{ + "description": "operation find", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-find", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + }, + { + "description": "find an element retrying failed command", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": "89", + "exception.message": { + "$$type": "string" + }, + "exception.type": { + "$$type": "string" + }, + "exception.stacktrace": { + "$$type": "string" + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/find.yml b/source/open-telemetry/tests/operation/find.yml new file mode 100644 index 0000000000..d7259b1960 --- /dev/null +++ b/source/open-telemetry/tests/operation/find.yml @@ -0,0 +1,143 @@ +description: operation find +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-find + documents: [] +tests: + - description: find an element + operations: + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-find.test + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + + - description: find an element retrying failed command + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + errorCode: 89 + errorLabels: [ RetryableWriteError ] + + - name: find + object: *collection0 + arguments: + filter: { x: 1 } + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: find operation-find.test + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: '89' + exception.message: { $$type: string } + exception.type: { $$type: string } + exception.stacktrace: { $$type: string } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + - name: find + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: find operation-find.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/find_and_modify.json b/source/open-telemetry/tests/operation/find_and_modify.json new file mode 100644 index 0000000000..7bacba57f8 --- /dev/null +++ b/source/open-telemetry/tests/operation/find_and_modify.json @@ -0,0 +1,143 @@ +{ + "description": "operation find_one_and_update", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find-one-and-update" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "findOneAndUpdate", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "findAndModify operation-find-one-and-update.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find-one-and-update", + "db.collection.name": "test", + "db.operation.name": "findAndModify", + "db.operation.summary": "findAndModify operation-find-one-and-update.test" + }, + "nested": [ + { + "name": "findAndModify", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find-one-and-update", + "db.collection.name": "test", + "db.command.name": "findAndModify", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "findAndModify operation-find-one-and-update.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "findAndModify": "test", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/find_and_modify.yml b/source/open-telemetry/tests/operation/find_and_modify.yml new file mode 100644 index 0000000000..a9e08938d7 --- /dev/null +++ b/source/open-telemetry/tests/operation/find_and_modify.yml @@ -0,0 +1,70 @@ +description: operation find_one_and_update +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find-one-and-update + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + +tests: + - description: findOneAndUpdate + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: { x: 5 } + comment: "comment" + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: findAndModify operation-find-one-and-update.test + attributes: + db.system.name: mongodb + db.namespace: operation-find-one-and-update + db.collection.name: test + db.operation.name: findAndModify + db.operation.summary: findAndModify operation-find-one-and-update.test + + nested: + - name: findAndModify + attributes: + db.system.name: mongodb + db.namespace: operation-find-one-and-update + db.collection.name: test + db.command.name: findAndModify + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: findAndModify operation-find-one-and-update.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + findAndModify: test + query: *filter + update: *update + comment: "comment" + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/find_without_query_text.json b/source/open-telemetry/tests/operation/find_without_query_text.json new file mode 100644 index 0000000000..0a2464ba60 --- /dev/null +++ b/source/open-telemetry/tests/operation/find_without_query_text.json @@ -0,0 +1,119 @@ +{ + "description": "operation find without db.query.text", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": false + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-find" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-find", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "find operation-find.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-find.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-find", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-find.test", + "db.query.text": { + "$$exists": false + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/find_without_query_text.yml b/source/open-telemetry/tests/operation/find_without_query_text.yml new file mode 100644 index 0000000000..e51188e977 --- /dev/null +++ b/source/open-telemetry/tests/operation/find_without_query_text.yml @@ -0,0 +1,58 @@ +description: operation find without db.query.text +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: false + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-find + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-find + documents: [] +tests: + - description: find an element + operations: + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: find operation-find.test + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.operation.name: find + db.operation.summary: find operation-find.test + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: operation-find + db.collection.name: test + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [int, long] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: find operation-find.test + db.query.text: { $$exists: false } diff --git a/source/open-telemetry/tests/operation/insert.json b/source/open-telemetry/tests/operation/insert.json new file mode 100644 index 0000000000..6ab0450357 --- /dev/null +++ b/source/open-telemetry/tests/operation/insert.json @@ -0,0 +1,146 @@ +{ + "description": "operation insert", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-insert" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-insert", + "documents": [] + } + ], + "tests": [ + { + "description": "insert one element", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "insert operation-insert.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-insert", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert operation-insert.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system.name": "mongodb", + "db.command.name": "insert", + "db.namespace": "operation-insert", + "db.collection.name": "test", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "insert operation-insert.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": { + "$$unsetOrMatches": 1 + }, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "operation-insert", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/insert.yml b/source/open-telemetry/tests/operation/insert.yml new file mode 100644 index 0000000000..000eafadbf --- /dev/null +++ b/source/open-telemetry/tests/operation/insert.yml @@ -0,0 +1,72 @@ +description: operation insert +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-insert + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-insert + documents: [ ] +tests: + - description: insert one element + operations: + - object: *collection0 + name: insertOne + arguments: { document: { _id: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: insert operation-insert.test + attributes: + db.system.name: mongodb + db.namespace: operation-insert + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert operation-insert.test + nested: + - name: insert + attributes: + db.system.name: mongodb + db.command.name: insert + db.namespace: operation-insert + db.collection.name: test + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: insert operation-insert.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: { $$unsetOrMatches: 1 } + documents: + - _id: 1 + + outcome: + - collectionName: test + databaseName: operation-insert + documents: + - _id: 1 diff --git a/source/open-telemetry/tests/operation/list_collections.json b/source/open-telemetry/tests/operation/list_collections.json new file mode 100644 index 0000000000..e3a28dd36a --- /dev/null +++ b/source/open-telemetry/tests/operation/list_collections.json @@ -0,0 +1,108 @@ +{ + "description": "operation list_collections", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-list-collections" + } + } + ], + "tests": [ + { + "description": "List collections", + "operations": [ + { + "object": "database0", + "name": "listCollections" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listCollections operation-list-collections", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-list-collections", + "db.operation.name": "listCollections", + "db.operation.summary": "listCollections operation-list-collections", + "db.collection.name": { + "$$exists": false + } + }, + "nested": [ + { + "name": "listCollections", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-list-collections", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "listCollections", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listCollections operation-list-collections", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listCollections": 1 + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/list_collections.yml b/source/open-telemetry/tests/operation/list_collections.yml new file mode 100644 index 0000000000..f03a19629e --- /dev/null +++ b/source/open-telemetry/tests/operation/list_collections.yml @@ -0,0 +1,53 @@ +description: operation list_collections +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-list-collections +tests: + - description: List collections + operations: + - object: *database0 + name: listCollections + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listCollections operation-list-collections + attributes: + db.system.name: mongodb + db.namespace: operation-list-collections + db.operation.name: listCollections + db.operation.summary: listCollections operation-list-collections + db.collection.name: { $$exists: false } + + nested: + - name: listCollections + attributes: + db.system.name: mongodb + db.namespace: operation-list-collections + db.collection.name: { $$exists: false } + db.command.name: listCollections + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listCollections operation-list-collections + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listCollections: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/list_databases.json b/source/open-telemetry/tests/operation/list_databases.json new file mode 100644 index 0000000000..ae86b87073 --- /dev/null +++ b/source/open-telemetry/tests/operation/list_databases.json @@ -0,0 +1,104 @@ +{ + "description": "operation list_databases", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + } + ], + "tests": [ + { + "description": "list databases", + "operations": [ + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listDatabases admin", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.operation.name": "listDatabases", + "db.operation.summary": "listDatabases admin", + "db.collection.name": { + "$$exists": false + } + }, + "nested": [ + { + "name": "listDatabases", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.command.name": "listDatabases", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listDatabases admin", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listDatabases": 1 + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/list_databases.yml b/source/open-telemetry/tests/operation/list_databases.yml new file mode 100644 index 0000000000..3dcfa491bc --- /dev/null +++ b/source/open-telemetry/tests/operation/list_databases.yml @@ -0,0 +1,50 @@ +description: operation list_databases +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true +tests: + - description: list databases + operations: + - object: *client0 + name: listDatabases + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listDatabases admin + attributes: + db.system.name: mongodb + db.namespace: admin + db.operation.name: listDatabases + db.operation.summary: listDatabases admin + db.collection.name: { $$exists: false } + + nested: + - name: listDatabases + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.command.name: listDatabases + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listDatabases admin + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listDatabases: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/list_indexes.json b/source/open-telemetry/tests/operation/list_indexes.json new file mode 100644 index 0000000000..d37138d275 --- /dev/null +++ b/source/open-telemetry/tests/operation/list_indexes.json @@ -0,0 +1,118 @@ +{ + "description": "operation list_indexes", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-list-indexes" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-list-indexes", + "documents": [] + } + ], + "tests": [ + { + "description": "List indexes", + "operations": [ + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "listIndexes operation-list-indexes.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-list-indexes", + "db.collection.name": "test", + "db.operation.name": "listIndexes", + "db.operation.summary": "listIndexes operation-list-indexes.test" + }, + "nested": [ + { + "name": "listIndexes", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-list-indexes", + "db.collection.name": "test", + "db.command.name": "listIndexes", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "listIndexes operation-list-indexes.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "listIndexes": "test" + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/list_indexes.yml b/source/open-telemetry/tests/operation/list_indexes.yml new file mode 100644 index 0000000000..2f4c642a9a --- /dev/null +++ b/source/open-telemetry/tests/operation/list_indexes.yml @@ -0,0 +1,61 @@ +description: operation list_indexes +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-list-indexes + - collection: + id: &collection0 collection0 + database: database0 + collectionName: test +initialData: + - collectionName: test + databaseName: operation-list-indexes + documents: [ ] +tests: + - description: List indexes + operations: + - object: *collection0 + name: listIndexes + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: listIndexes operation-list-indexes.test + attributes: + db.system.name: mongodb + db.namespace: operation-list-indexes + db.collection.name: test + db.operation.name: listIndexes + db.operation.summary: listIndexes operation-list-indexes.test + + nested: + - name: listIndexes + attributes: + db.system.name: mongodb + db.namespace: operation-list-indexes + db.collection.name: test + db.command.name: listIndexes + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: listIndexes operation-list-indexes.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + listIndexes: test + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/map_reduce.json b/source/open-telemetry/tests/operation/map_reduce.json new file mode 100644 index 0000000000..9230f7fd86 --- /dev/null +++ b/source/open-telemetry/tests/operation/map_reduce.json @@ -0,0 +1,177 @@ +{ + "description": "operation map_reduce", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-map-reduce" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-map-reduce", + "documents": [ + { + "_id": 1, + "x": 0 + }, + { + "_id": 2, + "x": 1 + }, + { + "_id": 3, + "x": 2 + } + ] + } + ], + "tests": [ + { + "description": "mapReduce", + "operations": [ + { + "object": "collection0", + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "expectResult": [ + { + "_id": 0, + "value": 6 + } + ] + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "mapReduce operation-map-reduce.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-map-reduce", + "db.collection.name": "test", + "db.operation.name": "mapReduce", + "db.operation.summary": "mapReduce operation-map-reduce.test" + }, + "nested": [ + { + "name": "mapReduce", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-map-reduce", + "db.collection.name": "test", + "db.command.name": "mapReduce", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "mapReduce operation-map-reduce.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "mapReduce": "test", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/map_reduce.yml b/source/open-telemetry/tests/operation/map_reduce.yml new file mode 100644 index 0000000000..4646dd8308 --- /dev/null +++ b/source/open-telemetry/tests/operation/map_reduce.yml @@ -0,0 +1,99 @@ +description: operation map_reduce +schemaVersion: '1.27' +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + # serverless proxy does not support mapReduce operation + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-map-reduce + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test +initialData: + - + collectionName: test + databaseName: operation-map-reduce + documents: + - + _id: 1 + x: 0 + - + _id: 2 + x: 1 + - + _id: 3 + x: 2 +tests: + - description: mapReduce + operations: + - object: *collection0 + name: mapReduce + arguments: + map: + $code: 'function inc() { return emit(0, this.x + 1) }' + reduce: + $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' + out: + inline: 1 + expectResult: + - + _id: 0 + value: 6 + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: mapReduce operation-map-reduce.test + attributes: + db.system.name: mongodb + db.namespace: operation-map-reduce + db.collection.name: test + db.operation.name: mapReduce + db.operation.summary: mapReduce operation-map-reduce.test + + nested: + - name: mapReduce + attributes: + db.system.name: mongodb + db.namespace: operation-map-reduce + db.collection.name: test + db.command.name: mapReduce + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: mapReduce operation-map-reduce.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + mapReduce: test + map: { $code: 'function inc() { return emit(0, this.x + 1) }' } + reduce: { $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' } + out: { inline: 1 } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/retries.json b/source/open-telemetry/tests/operation/retries.json new file mode 100644 index 0000000000..d42f83f333 --- /dev/null +++ b/source/open-telemetry/tests/operation/retries.json @@ -0,0 +1,209 @@ +{ + "description": "retries", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-retries" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "operation-retries", + "documents": [] + } + ], + "tests": [ + { + "description": "find an element with retries", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": true, + "spans": [ + { + "name": "find operation-retries.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.operation.name": "find", + "db.operation.summary": "find operation-retries.test" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": "89", + "exception.message": { + "$$type": "string" + }, + "exception.type": { + "$$type": "string" + }, + "exception.stacktrace": { + "$$type": "string" + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "find operation-retries.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-retries", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find operation-retries.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "find": "test", + "filter": { + "x": 1 + } + } + } + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/retries.yml b/source/open-telemetry/tests/operation/retries.yml new file mode 100644 index 0000000000..0b8b514dc3 --- /dev/null +++ b/source/open-telemetry/tests/operation/retries.yml @@ -0,0 +1,105 @@ +description: retries +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name operation-retries + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test +initialData: + - collectionName: test + databaseName: operation-retries + documents: [ ] +tests: + - description: find an element with retries + operations: + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + errorCode: 89 + errorLabels: [ RetryableWriteError ] + + - name: find + object: *collection0 + arguments: + filter: { x: 1 } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: true + spans: + - name: find operation-retries.test + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.operation.name: find + db.operation.summary: find operation-retries.test + + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: find + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: '89' + exception.message: { $$type: string } + exception.type: { $$type: string } + exception.stacktrace: { $$type: string } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: find operation-retries.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + - name: find + attributes: + db.system.name: mongodb + db.namespace: *database0Name + db.collection.name: *collection0Name + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.query.summary: find operation-retries.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + find: test + filter: + x: 1 + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] diff --git a/source/open-telemetry/tests/operation/update.json b/source/open-telemetry/tests/operation/update.json new file mode 100644 index 0000000000..509c98be30 --- /dev/null +++ b/source/open-telemetry/tests/operation/update.json @@ -0,0 +1,140 @@ +{ + "description": "operation update", + "schemaVersion": "1.27", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-update" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + } + ], + "tests": [ + { + "description": "update one element", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "update operation-update.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-update", + "db.collection.name": "test", + "db.operation.name": "update", + "db.operation.summary": "update operation-update.test" + }, + "nested": [ + { + "name": "update", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "operation-update", + "db.collection.name": "test", + "db.command.name": "update", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "update operation-update.test", + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "update": "test", + "ordered": true, + "txnNumber": { + "$$unsetOrMatches": 1 + }, + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "x": 1 + } + } + } + ] + } + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/operation/update.yml b/source/open-telemetry/tests/operation/update.yml new file mode 100644 index 0000000000..0f9248470c --- /dev/null +++ b/source/open-telemetry/tests/operation/update.yml @@ -0,0 +1,66 @@ +description: operation update +schemaVersion: '1.27' +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName0 operation-update + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName0 test + +tests: + - description: update one element + operations: + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: update operation-update.test + attributes: + db.system.name: mongodb + db.namespace: *databaseName0 + db.collection.name: *collectionName0 + db.operation.name: update + db.operation.summary: update operation-update.test + + nested: + - name: update + attributes: + db.system.name: mongodb + db.namespace: operation-update + db.collection.name: *collectionName0 + db.command.name: update + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: update operation-update.test + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + update: test + ordered: true + txnNumber: { $$unsetOrMatches: 1 } + updates: [ { "q": { "_id": 1 }, "u": { "$inc": { "x": 1 } } } ] diff --git a/source/open-telemetry/tests/transaction/convenient.json b/source/open-telemetry/tests/transaction/convenient.json new file mode 100644 index 0000000000..a8f0c212d1 --- /dev/null +++ b/source/open-telemetry/tests/transaction/convenient.json @@ -0,0 +1,326 @@ +{ + "description": "convenient transactions", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "convenient-transaction-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test" + } + }, + { + "session": { + "id": "session", + "client": "client" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "convenient-transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction", + "operations": [ + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + }, + "session": "session" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system.name": "mongodb" + }, + "nested": [ + { + "name": "insert convenient-transaction-tests.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert convenient-transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert convenient-transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session" + }, + "db.mongodb.txn_number": 1, + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "commitTransaction admin", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "commitTransaction", + "db.operation.summary": "commitTransaction admin" + }, + "nested": [ + { + "name": "commitTransaction", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "commitTransaction admin", + "db.command.name": "commitTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "commitTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + }, + { + "name": "find convenient-transaction-tests.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.operation.summary": "find convenient-transaction-tests.test", + "db.operation.name": "find" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "convenient-transaction-tests", + "db.collection.name": "test", + "db.command.name": "find", + "network.transport": "tcp", + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.summary": "find convenient-transaction-tests.test", + "db.query.text": { + "$$exists": true + } + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "convenient-transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/transaction/convenient.yml b/source/open-telemetry/tests/transaction/convenient.yml new file mode 100644 index 0000000000..9b220a507e --- /dev/null +++ b/source/open-telemetry/tests/transaction/convenient.yml @@ -0,0 +1,167 @@ +description: convenient transactions + +schemaVersion: "1.27" + +runOnRequirements: + - minServerVersion: "4.4" + topologies: ["replicaset", "sharded"] + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database database + client: *client + databaseName: &databaseName convenient-transaction-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + - session: + id: &session session + client: *client + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "withTransaction" + operations: + - name: withTransaction + object: *session + arguments: + callback: + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + session: *session + - name: find + object: *collection + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system.name: mongodb + nested: + - name: insert convenient-transaction-tests.test + attributes: + db.system.name: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.operation.name: insert + db.operation.summary: insert convenient-transaction-tests.test + + nested: + - name: insert + attributes: + db.system.name: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.query.summary: insert convenient-transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session } + db.mongodb.txn_number: 1 + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: commitTransaction admin + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: commitTransaction + db.operation.summary: commitTransaction admin + + nested: + - name: commitTransaction + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: commitTransaction admin + db.command.name: commitTransaction + db.mongodb.lsid: { $$sessionLsid: *session } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + commitTransaction: 1 + txnNumber: 1 + autocommit: false + + - name: find convenient-transaction-tests.test + attributes: + db.system.name: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.operation.summary: find convenient-transaction-tests.test + db.operation.name: find + + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: *databaseName + db.collection.name: *collectionName + db.command.name: find + network.transport: tcp + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + server.address: { $$type: string } + server.port: { $$type: [ long, string ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.summary: find convenient-transaction-tests.test + db.query.text: { $$exists: true } + + outcome: + - collectionName: test + databaseName: convenient-transaction-tests + documents: + - _id: 1 diff --git a/source/open-telemetry/tests/transaction/core_api.json b/source/open-telemetry/tests/transaction/core_api.json new file mode 100644 index 0000000000..925cdb5162 --- /dev/null +++ b/source/open-telemetry/tests/transaction/core_api.json @@ -0,0 +1,545 @@ +{ + "description": "transaction spans", + "schemaVersion": "1.27", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeTracingMessages": { + "enableCommandPayload": true + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commit transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "x": 1 + } + } + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system.name": "mongodb" + }, + "nested": [ + { + "name": "insert transaction-tests.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "network.transport": "tcp", + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "commitTransaction admin", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "commitTransaction", + "db.operation.summary": "commitTransaction admin" + }, + "nested": [ + { + "name": "commitTransaction", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "commitTransaction admin", + "db.command.name": "commitTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "commitTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + }, + { + "name": "find transaction-tests.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.summary": "find transaction-tests.test", + "db.operation.name": "find" + }, + "nested": [ + { + "name": "find", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "find", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$exists": true + }, + "db.query.summary": "find transaction-tests.test" + } + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "abort transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectTracingMessages": [ + { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "transaction", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": { + "$$exists": false + }, + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": { + "$$exists": false + }, + "db.operation.summary": { + "$$exists": false + } + }, + "nested": [ + { + "name": "insert transaction-tests.test", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.operation.name": "insert", + "db.operation.summary": "insert transaction-tests.test" + }, + "nested": [ + { + "name": "insert", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "transaction-tests", + "db.collection.name": "test", + "db.command.name": "insert", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "long", + "string" + ] + }, + "db.query.summary": "insert transaction-tests.test", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "insert": "test", + "ordered": true, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "documents": [ + { + "_id": 1 + } + ] + } + } + } + } + } + ] + }, + { + "name": "abortTransaction admin", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.operation.name": "abortTransaction", + "db.operation.summary": "abortTransaction admin" + }, + "nested": [ + { + "name": "abortTransaction", + "attributes": { + "db.system.name": "mongodb", + "db.namespace": "admin", + "db.collection.name": { + "$$exists": false + }, + "db.query.summary": "abortTransaction admin", + "db.command.name": "abortTransaction", + "db.mongodb.lsid": { + "$$sessionLsid": "session0" + }, + "db.mongodb.txn_number": 1, + "db.mongodb.cursor_id": { + "$$exists": false + }, + "db.response.status_code": { + "$$exists": false + }, + "exception.message": { + "$$exists": false + }, + "exception.type": { + "$$exists": false + }, + "exception.stacktrace": { + "$$exists": false + }, + "network.transport": "tcp", + "server.address": { + "$$type": "string" + }, + "server.port": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.server_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.mongodb.driver_connection_id": { + "$$type": [ + "int", + "long" + ] + }, + "db.query.text": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "abortTransaction": 1, + "txnNumber": 1, + "autocommit": false + } + } + } + } + } + ] + } + ] + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/open-telemetry/tests/transaction/core_api.yml b/source/open-telemetry/tests/transaction/core_api.yml new file mode 100644 index 0000000000..cad2aaddbc --- /dev/null +++ b/source/open-telemetry/tests/transaction/core_api.yml @@ -0,0 +1,269 @@ +description: transaction spans +schemaVersion: '1.27' +runOnRequirements: + - minServerVersion: '4.0' + topologies: + - replicaset + - minServerVersion: '4.1.8' + topologies: + - sharded + - load-balanced +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeTracingMessages: + enableCommandPayload: true + - database: + id: &database0 database0 + client: *client0 + databaseName: transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: test + - session: + id: &session0 session0 + client: client0 +initialData: + - collectionName: test + databaseName: transaction-tests + documents: [] +tests: + - description: commit transaction + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + - object: *session0 + name: commitTransaction + - name: find + object: *collection0 + arguments: { filter: { x: 1 } } + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system.name: mongodb + nested: + - name: insert transaction-tests.test + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert transaction-tests.test + + nested: + - name: insert + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.query.summary: insert transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + network.transport: tcp + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: commitTransaction admin + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: commitTransaction + db.operation.summary: commitTransaction admin + + nested: + - name: commitTransaction + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: commitTransaction admin + db.command.name: commitTransaction + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + commitTransaction: 1 + txnNumber: 1 + autocommit: false + - name: find transaction-tests.test + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.summary: find transaction-tests.test + db.operation.name: find + nested: + - name: find + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: find + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: { $$exists: true } + db.query.summary: find transaction-tests.test + outcome: + - collectionName: test + databaseName: transaction-tests + documents: + - _id: 1 + + - description: abort transaction + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + - object: *session0 + name: abortTransaction + + expectTracingMessages: + - client: *client0 + ignoreExtraSpans: false + spans: + - name: transaction + attributes: + db.system.name: mongodb + db.namespace: { $$exists: false } + db.collection.name: { $$exists: false } + db.operation.name: { $$exists: false } + db.operation.summary: { $$exists: false } + nested: + - name: insert transaction-tests.test + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.operation.name: insert + db.operation.summary: insert transaction-tests.test + nested: + - name: insert + attributes: + db.system.name: mongodb + db.namespace: transaction-tests + db.collection.name: test + db.command.name: insert + server.address: { $$type: string } + server.port: { $$type: [long, string] } + db.query.summary: insert transaction-tests.test + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + insert: test + ordered: true + txnNumber: 1 + startTransaction: true + autocommit: false + documents: + - _id: 1 + - name: abortTransaction admin + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.operation.name: abortTransaction + db.operation.summary: abortTransaction admin + nested: + - name: abortTransaction + attributes: + db.system.name: mongodb + db.namespace: admin + db.collection.name: { $$exists: false } + db.query.summary: abortTransaction admin + db.command.name: abortTransaction + db.mongodb.lsid: { $$sessionLsid: *session0 } + db.mongodb.txn_number: 1 + db.mongodb.cursor_id: { $$exists: false } + db.response.status_code: { $$exists: false } + exception.message: { $$exists: false } + exception.type: { $$exists: false } + exception.stacktrace: { $$exists: false } + network.transport: tcp + server.address: { $$type: string } + server.port: { $$type: [ int, long ] } + db.mongodb.server_connection_id: + $$type: [ int, long ] + db.mongodb.driver_connection_id: + $$type: [ int, long ] + db.query.text: + $$matchAsDocument: + $$matchAsRoot: + abortTransaction: 1 + txnNumber: 1 + autocommit: false + + outcome: + - collectionName: test + databaseName: transaction-tests + documents: [] diff --git a/source/out-aggregation-pipeline-operator.rst b/source/out-aggregation-pipeline-operator.rst deleted file mode 100644 index a3178ac7a3..0000000000 --- a/source/out-aggregation-pipeline-operator.rst +++ /dev/null @@ -1,120 +0,0 @@ -=================================== -$out Aggregation Pipeline Operator -=================================== - -:Title: $out Aggregation Pipeline Operator -:Author: Emily Stolfo -:Status: Draft -:Type: Standards -:Last Modified: January 12, 2015 - -.. contents:: - --------------------- - -Server Specification --------------------- - -The aggregation framework is used to process data and return computed results. Up until release 2.6, the result from an aggregation was a single document. That single document had a 'result' field with an array containing the aggregation results. It was therefore subject to the BSON Document size limit, which is 16 megabytes at the time of release 2.6. - -The **$out** pipeline option is new in 2.6 and allows you to specify the name of a collection to which the result of the aggregation should be written. With the **$out** option, there is no 16mb limit on the result set. -A new collection will be created if the one specified in **$out** does not already exist in the current database. Note that if the collection already exists, it will be dropped before written to. - -The **$out** option is sometimes referred to as "unsharded **$out**" because it only allows results to be piped to a non-sharded collection. On the other hand, the collection on which the aggregation is performed can be sharded. - -References: - -* SERVER ticket: https://jira.mongodb.org/browse/SERVER-3253 -* DRIVERS ticket: https://jira.mongodb.org/browse/DRIVERS-111 - -Server Return Value -'''''''''''''''''''' -If the aggregation with **$out** specified completes successfully, the result will be a document in the following format:: - - { - "result" : [ ], - "ok" : 1 - } - -Fields -~~~~~~ - -**result** - This field will be an empty array. - -**ok** - 1 indicates successful aggregation, 0 indicates failure. - -Notes and Restrictions -'''''''''''''''''''''' - -**1. The $out collection cannot be sharded** - -The collection to which the aggregation is piped cannot be sharded. An error will be returned if an aggregation is attempted with **$out** specified as a sharded collection, or if that collection becomes sharded while the aggregation is running. The error is the following:: - - - aggregate failed: { - "errmsg" : "exception: namespace 'records.users' is sharded so it can't be used for $out'", - "code" : 17017, - "ok" : 0 - } - -**2. The $out option must be the last pipeline operator** - -An ordered list is presumably already used to specify the pipeline passed to the aggregation. The **$out** option must be last in the list, otherwise, an error will be returned:: - - - aggregate failed: { - "code" : 16991, - "ok" : 0, - "errmsg" : "exception: $out can only be the final stage in the pipeline" - } - -**3. Both the cursor option and $out option are specified** - -If both a cursor and **$out** are requested, the results will be written to the collection specified but no cursor will be created (cursor id == 0):: - - { - "cursor" : { - "id" : NumberLong(0), - "ns" : "records.users", - "firstBatch" : [ ] - }, - "ok" : 1 - } - - -Driver API ----------- - -The **$out** option is provided just as any other pipeline operator is:: - - pipeline = [{ $project: { uid: 1, email: 1 } }, { $out: "users" }] - collection.aggregate(pipeline) - - -Driver return value -''''''''''''''''''' - -The driver will return the raw document received from the server:: - - { - "result" : [ ], - "ok" : 1 - } - - -The user can decide whether to instantiate a collection using the name specified in the **$out** operator. - -Read preferences -'''''''''''''''' - -The only replica set member that can be used with **$out** is the primary because the operation writes to a collection. If a read preference other than primary is specified, the driver will route the aggregation to the primary and log a warning that it has done so. - -See DRIVERS ticket: https://jira.mongodb.org/browse/DRIVERS-84 - -If **$out** is not specified, the read preference will be respected. - -Recall what your driver does with Map-Reduce if out is specified and it's not 'inline' while thinking about how to handle this scenario. - -Reason for warning: Rerouting the aggregation with **$out** to the primary could present a problem such that the collection is written and then queried by the user with read preference non-primary before replication has completed. The user risks querying the collection before it is fully replicated. diff --git a/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md new file mode 100644 index 0000000000..2805a9c23c --- /dev/null +++ b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md @@ -0,0 +1,175 @@ +# Polling SRV Records for mongos Discovery + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +Currently the [Initial DNS Seedlist Discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) +functionality provides a static seedlist when a MongoClient is constructed. Periodically polling the DNS SRV records +would allow for the mongos proxy list to be updated without having to change client configuration. + +This specification builds on top of the original Initial DNS Seedlist Discovery specification, and modifies the +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) specification's +definition of monitoring a set of mongos servers in a Sharded TopologyType. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### rescan, rescanning + +A rescan is the periodic scan of all DNS SRV records to discover a new set of mongos hosts. + +#### rescanSRVIntervalMS + +An internal value representing how often the DNS SRV records should be queried for. + +### Implementation + +If the initial topology was created through a `mongodb+srv://` URI, then drivers MUST implement this specification by +periodically rescanning the SRV DNS records. There MUST NOT be an option to turn this behaviour off. + +Drivers MUST NOT implement this specification if they do not adhere fully to the +[Initial DNS Seedlist Discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) specification. + +This feature is only available when the Server Discovery has determined that the TopologyType is Sharded, or Unknown. +Drivers MUST NOT rescan SRV DNS records when the Topology is not Sharded (i.e. Single, ReplicaSetNoPrimary, or +ReplicaSetWithPrimary). + +The discovery of a set of mongos servers is explained in the +[seedlist](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#seedlist-discovery) discovery section of +the original specification. The behaviour of the periodic rescan is similar, but not identical to the behaviour of +initial seedlist discovery. Periodic scan MUST follow these rules: + +- The driver will query the DNS server for SRV records on `{hostname}.{domainname}`, prefixed with the SRV service name + and protocol. The SRV service name is provided in the + [srvServiceName](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvservicename) URI option and + defaults to `mongodb`. The protocol is always `tcp`. After prefixing, the URI should look like: + `_{srvServiceName}._tcp.{hostname}.{domainname}`. +- A driver MUST verify that the host names returned through SRV records have the same parent `{domainname}`. When this + verification fails, a driver: + - MUST NOT add such a non-compliant host name to the topology + - MUST NOT raise an error + - SHOULD log the non-compliance, including the host name + - MUST NOT initiate a connection to any such host +- If the DNS request returns no verified hosts in SRV records, no SRV records at all, or a DNS error happens, the + driver: + - MUST NOT change the topology + - MUST NOT raise an error + - SHOULD log this situation, including the reason why the DNS records could not be found, if possible + - MUST temporarily set *rescanSRVIntervalMS* to *heartbeatFrequencyMS* until at least one verified SRV record is + obtained. +- For all verified host names, as returned through the DNS SRV query, the driver: + - MUST remove all hosts that are part of the topology, but are no longer in the returned set of valid hosts + - MUST NOT remove all hosts, and then re-add the ones that were returned. Hosts that have not changed, MUST be left + alone and unchanged. + - If [srvMaxHosts](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvmaxhosts) is zero or greater + than or equal to the number of valid hosts, each valid new host MUST be added to the topology as Unknown. + - If [srvMaxHosts](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvmaxhosts) is greater than + zero and less than the number of valid hosts, valid new hosts MUST be randomly selected and added to the topology + as Unknown until the topology has `srvMaxHosts` hosts. Drivers MUST use the same randomization algorithm as they + do for [initial selection](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#querying-dns). +- Priorities and weights in SRV records MUST continue to be ignored, and MUST NOT dictate which mongos server is used + for new connections. + +The rescan needs to happen periodically. As SRV records contain a TTL value, this value can be used to indicate when a +rescan needs to happen. Different SRV records can have different TTL values. The *rescanSRVIntervalMS* value MUST be set +to the lowest of the individual TTL values associated with the different SRV records in the most recent rescan, but MUST +NOT be lower than *60 seconds*. If a driver is unable to access the TTL values of SRV records, it MUST rescan every 60 +seconds. + +Drivers SHOULD endeavour to rescan and obtain a new list of mongos servers every *rescanSRVIntervalMS* value. The +*rescanSRVIntervalMS* period SHOULD be calculated from the **end** of the previous rescan (or the **end** of the initial +DNS seedlist discovery scan). + +### Multi-Threaded Drivers + +A threaded driver MUST use a separate monitoring thread for scanning the DNS records so that DNS lookups don't block +other operations. + +### Single-Threaded Drivers + +The rescan MUST happen **before** scanning all servers as part of the normal +[scanning](../server-discovery-and-monitoring/server-monitoring.md#scanning) functionality, but only if +*rescanSRVIntervalMS* has passed. + +## Test Plan + +See README.md in the accompanying [test directory](tests/README.md). + +## Motivation for Change + +The original [Initial DNS Seedlist Discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) +specification only regulates the initial list of mongos hosts to be used instead of a single hostname from a connection +URI. Although this makes the initial configuration of a set of mongos servers a lot easier, it does not provide a method +for updating the list of mongos servers in the topology. + +Since the introduction of the `mongodb+srv://` schema to provide an initial seedlist, some users have requested +additional functionality to be able to update the configured list of mongos hosts that make up the initially seeded +topology: + +- + +## Design Rationale + +### From the scope document + +#### Should DNS polling use heartbeatFrequencyMS or DNS cache TTLs? + +We have selected to use lowest TTLs among all DNS SRV records, with a caveat that the rescan frequency is not lower than +60 seconds. + +#### Should DNS polling also have a "fast polling" mode when no servers are available? + +We have not opted to have a "fast polling" mode, but we did include a provision that a rescan needs to happen when DNS +records are not available. In that case, a rescan would happen every *heartbeatFrequencyMS*. The rationale being that +polling DNS really often really fast does not make a lot of sense due to DNS caching, which often uses the TTL already +anyway, but when we have no TTL records to reference we still need a fallback frequency. + +### For the design + +#### No option to turn off periodic rescanning + +The design does not allow for an option to turn off the periodic rescanning of SRV records on the basis that we try to +have as few options as possible: the "no knobs" philosophy. + +## Backwards Compatibility + +This specification changes the behaviour of server monitoring by introducing a repeating DNS lookup of the SRV records. +Although this is an improvement in the `mongodb+srv://` scheme it can nonetheless break expectations with users that +were familiar with the old behaviour. We do not expect this to negatively impact users. + +## Reference Implementation + +Reference implementations are made for the following drivers: + +- Perl +- C# + +## Security Implication + +This specification has no security implications beyond the ones associated with the original +[Initial DNS Seedlist Discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) specification. + +## Future work + +No future work is expected. + +## Changelog + +- 2024-08-22: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Revise spec front matter and reformat changelog. + +- 2021-10-14: Specify behavior for `srvMaxHosts` MongoClient option. + +- 2021-09-15: Clarify that service name only defaults to `mongodb`, and should be defined by the `srvServiceName` URI + option. diff --git a/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.rst b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.rst new file mode 100644 index 0000000000..6b72de7beb --- /dev/null +++ b/source/polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `polling-srv-records-for-mongos-discovery.md `_. diff --git a/source/polling-srv-records-for-mongos-discovery/tests/README.md b/source/polling-srv-records-for-mongos-discovery/tests/README.md new file mode 100644 index 0000000000..e30c43d092 --- /dev/null +++ b/source/polling-srv-records-for-mongos-discovery/tests/README.md @@ -0,0 +1,202 @@ +# SRV Polling Tests + +## Introduction + +This directory contains prose test descriptions that drivers can use to prove their conformance to the SRV polling +specification. + +Without (automated) access to a DNS server configuration, it is nearly impossible to implement functional tests for a +correct implementation of this specification. However, it might be possible to mock changes to DNS SRV records such that +automated testing is doable. In any case, the following tests should be executed, either manually, or programmatically. + +## Configuring the Test Environment + +To test, start a sharded cluster with mongos servers on ports 27017, 27018, 27019, and 27020. + +For each test, take as a starting point the test1, test3, and test22 SRV records from the +[test set-up](../../initial-dns-seedlist-discovery/tests/README.md) from the +[Initial DNS Seedlist Discovery](../../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) specification: + +```dns +Record TTL Class Address +localhost.test.test.build.10gen.cc. 86400 IN A 127.0.0.1 + +Record TTL Class Port Target +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +_mongodb._tcp.test3.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +_customname._tcp.test22.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. +``` + +## Prose Tests + +### Changing DNS records with test1.test.build.10gen.cc + +The following tests MUST setup a MongoClient using the `test1.test.build.10gen.cc` SRV record. Each test MUST mock the +described DNS changes and then verify that the new list of hosts is present. + +#### 1. Addition of a new DNS record + +Add the following record: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +``` + +#### 2. Removal of an existing DNS record + +Remove the following record: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +``` + +#### 3. Replacement of a DNS record + +Replace the following record: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +``` + +with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +``` + +#### 4. Replacement of both existing DNS records with *one* new record + +Replace both records with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +``` + +#### 5. Replacement of both existing DNS records with *two* new records + +Replace both records with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27020 localhost.test.build.10gen.cc. +``` + +### Error scenarios with test1.test.build.10gen.cc + +The following tests MUST setup a MongoClient using the `test1.test.build.10gen.cc` SRV record. Each test MUST mock the +described error situation and assert that the internal list of discovered mongos servers has not changed. + +#### 6. DNS record lookup timeout + +Mock a DNS record lookup timeout error. + +#### 7. DNS record lookup failure + +Mock a DNS record lookup failure (i.e. domain no longer exists because it's no longer registered). + +#### 8. Removal of all DNS SRV records + +Mock a DNS record lookup that returns zero SRV records. + +### Changing DNS records with test3.test.build.10gen.cc + +The following tests MUST setup a MongoClient using the `test3.test.build.10gen.cc` SRV record. Each test MUST mock the +described situation and make the specified assertions. + +#### 9. Test that SRV polling is not done for load balalanced clusters + +Connect to `mongodb+srv://test3.test.build.10gen.cc/?loadBalanced=true`, mock the addition of the following DNS record: + +```dns +_mongodb._tcp.test3.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +``` + +Wait until `2*rescanSRVIntervalMS` and assert that the final topology description only contains one server: +`localhost.test.build.10gen.cc.` at port `27017`. + +### SRV polling with srvMaxHosts MongoClient option + +The following tests MUST setup a MongoClient using the `srvMaxHosts` option and `test1.test.build.10gen.cc` SRV record. +Each test MUST mock the described DNS changes and then verify that the new list of hosts is present. + +#### 10. All DNS records are selected (srvMaxHosts = 0) + +Configure the MongoClient with `srvMaxHosts=0`. + +Replace the following record: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +``` + +with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27020 localhost.test.build.10gen.cc. +``` + +Wait until `2*rescanSRVIntervalMS` and assert that the final topology description contains the following hosts: + +- localhost.test.build.10gen.cc:27017 +- localhost.test.build.10gen.cc:27019 +- localhost.test.build.10gen.cc:27020 + +#### 11. All DNS records are selected (srvMaxHosts >= records) + +Configure the MongoClient with `srvMaxHosts=2`. + +Replace both records with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27020 localhost.test.build.10gen.cc. +``` + +Wait until `2*rescanSRVIntervalMS` and assert that the final topology description contains the following hosts: + +- localhost.test.build.10gen.cc:27019 +- localhost.test.build.10gen.cc:27020 + +#### 12. New DNS records are randomly selected (srvMaxHosts > 0) + +Configure the MongoClient with `srvMaxHosts=2`. + +Replace the following record: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. +``` + +with: + +```dns +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27020 localhost.test.build.10gen.cc. +``` + +Wait until `2*rescanSRVIntervalMS` and assert that the topology has two hosts present and that one of the hosts is +`localhost.test.build.10gen.cc:27017`. The second, new host will have been randomly selected and cannot be +deterministically asserted. + +### SRV polling with srvServiceName MongoClient option + +The following test MUST setup a MongoClient using the `srvServiceName` option and the `test22.test.build.10gen.cc` SRV +record. The test MUST mock the described DNS changes and then verify that the new list of hosts is present. + +#### 13. DNS record with custom service name can be found + +Configure the MongoClient with `srvServiceName=customname`. + +Replace both records with: + +```dns +_customname._tcp.test22.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. +_customname._tcp.test22.test.build.10gen.cc. 86400 IN SRV 27020 localhost.test.build.10gen.cc. +``` + +Wait until `2*rescanSRVIntervalMS` and assert that the final topology description contains the following hosts: + +- localhost.test.build.10gen.cc:27019 +- localhost.test.build.10gen.cc:27020 diff --git a/source/read-write-concern/read-write-concern.md b/source/read-write-concern/read-write-concern.md new file mode 100644 index 0000000000..8d1929ceef --- /dev/null +++ b/source/read-write-concern/read-write-concern.md @@ -0,0 +1,613 @@ +# Read and Write Concern + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Abstract + +A driver must support configuring and sending read concern and write concerns to a server. This specification defines +the API drivers must implement as well as how that API is translated into messages for communication with the server. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Terminology + +MaxWireVersion The `maxWireVersion` value reported by the `hello` command. + +Server Selection The process of selecting a server to read or write from. See +[the server selection specification](../server-selection/server-selection.md). + +## Specification + +This specification includes guidance for implementing [Read Concern](#read-concern) and [Write Concern](#write-concern) +in a driver. It does not define how read and write concern behave or are implemented on the server. + +### Read Concern + +For naming and deviation guidance, see the [CRUD specification](../crud/crud.md#naming). Defined below are the +constructs for drivers. + +```typescript +enum ReadConcernLevel { + /** + * This is rendered as "local" (lower-case) on the wire. + */ + local, + + /** + * This is rendered as "majority" (lower-case) on the wire. + */ + majority, + + /** + * This is rendered as "linearizable" (lower-case) on the wire. + */ + linearizable, + + /** + * This is rendered as "available" (lower-case) on the wire. + */ + available, + + /** + * This is rendered as "snapshot" (lower-case) on the wire. + */ + snapshot +} + +class ReadConcern { + /** + * The level of the read concern. + */ + level: Optional +} +``` + +The read concern option is available for the following operations: + +- `aggregate` command +- `count` command +- `distinct` command +- `find` command +- `mapReduce` command where the `out` option is `{ inline: 1 }` +- `parallelCollectionScan` command +- `geoNear` command +- `geoSearch` command + +Starting in MongoDB 4.2, an `aggregate` command with a write stage (e.g. `$out`, `$merge`) supports a `readConcern`; +however, it does not support the "linearizable" level (attempting to do so will result in a server error). + +Server versions before 4.2 do not support a `readConcern` at all for `aggregate` commands with a write stage. + +The `mapReduce` command where the `out` option is anything other than `{ inline: 1 }` does not support a `readConcern`. + +The read concern document is also used to specify `afterClusterTime` for read and write operations in +causally-consistent sessions. See [`afterClusterTime`](#afterclustertime) for more information. + +#### Unknown Levels and Additional Options for String Based ReadConcerns + +For forward compatibility, a driver MUST NOT raise an error when a user provides an unknown `level` or additional +options. The driver relies on the server to validate levels and other contents of the read concern. + +#### Server's Default Read Concern + +When a `ReadConcern` is created but no values are specified, it should be considered the server's default `ReadConcern`. +`readconcern>: { }` is not the same as `readconcern>: { level="local"}`. The former is the server's default +`ReadConcern` while the latter is the user explicitly specifying a `ReadConcern` with a `level` of "local". + +#### Snapshot Read Concern + +When a `ReadConcern` `level` `snapshot` is used, `atClusterTime` may be specified to indicate the desired point in time +for reading. `find`, `aggregate` and `distinct` operations executed with `ReadConcern` `snapshot` but without +`atClusterTime` will return `atClusterTime` timestamp in the server response. The obtained `atClusterTime` timestamp can +be used for subsequent read operations. `ReadConcern` `level` `snapshot` with `clusterTime` is supported in `find`, +`aggregate` and `distinct` operations. + +#### `afterClusterTime` + +When running operations in a causally-consistent session, the read concern document is used to specify +`afterClusterTime`. In addition to the read operations listed above, a read concern document containing only +`afterClusterTime` is also supported for the following write operations: + +- `insert` command +- `update` command +- `findAndModify` command +- `delete` command +- `bulkWrite` command +- `create` command +- `createIndexes` command +- `drop` command +- `dropDatabase` command +- `dropIndexes` command + +See the [Causal Consistency specification](../causal-consistency/causal-consistency.md#server-commands) for details +about when to send `afterClusterTime`. + +#### On the Wire + +##### Read Commands + +Read commands that support `ReadConcern` take a named parameter spelled (case-sensitively) `readConcern`. See command +documentation for further examples. + +If the `Client`, `Database`, or `Collection` being operated on either has no `ReadConcern` set, or has the server +default `ReadConcern` - `readconcern>: { }`: + +- If the `ReadConcern` specified for the command is the server default `readConcern: { }`, the driver MUST omit it when + sending the command. +- If the `ReadConcern` specified for the command is any `ReadConcern` besides the server default, including an + explicitly specified `ReadConcern` of `readConcern: { level: "local" }`, the driver MUST include the `ReadConcern` + when sending the command. + +If the `Client`, `Database`, or `Collection` being operated on has a non-default `ReadConcern` specified, then the +driver MUST include the command's `ReadConcern` when sending the command. This includes if the command specifies the +server default `ReadConcern`, so that the command can override the `Client`, `Database`, or `Collection`'s `ReadConcern` +to use the server default instead. + + + +##### Generic Command Method + +If your driver offers a generic `RunCommand` method on your `database` object, `ReadConcern` MUST NOT be applied +automatically to any command. A user wishing to use a `ReadConcern` in a generic command must supply it manually. + +##### Errors + +`ReadConcern` errors from a server MUST NOT be handled by a driver. There is nothing a driver can do about them and any +such errors will get propagated to the user via normal error handling. + +#### Location Specification + +##### Via Code + +`ReadConcern` SHOULD be specifiable at the `Client`, `Database`, and `Collection` levels. Unless specified, the value +MUST be inherited from its parent and SHOULD NOT be modifiable on an existing `Client`, `Database` or `Collection`. In +addition, a driver MAY allow it to be specified on a per-operation basis in accordance with the CRUD specification. + +For example: + +```typescript +var client = new MongoClient({ readConcern: { level: "local" } }); + +// db1's readConcern level is "local". +var db1 = client.getDatabase("db1"); + +// col1's readConcern level is "local" +var col1 = db1.getCollection("col_name"); + +// db2's readConcern level is "majority". +var db2 = client.getDatabase("db_name", { readConcern: { level: "majority" } }); + +// col2's readConcern level is "majority" +var col2 = db2.getCollection("col_name"); + +// col3's readConcern level is the server's default read concern +var col3 = db2.getCollection("col_name", { readConcern: { } }); +``` + +##### Via Connection String + +Options + +- `readConcernLevel` - defines the level for the read concern. + +For example: + +```text +mongodb://server:27017/db?readConcernLevel=majority +``` + +#### Errors + +- MaxWireVersion < 4 Only the server's default `ReadConcern` is support by MaxWireVersion < 4. When using other + `readConcernLevels` with clients reporting `MaxWireVersion` < 4, the driver MUST raise an error. This check MUST + happen after server selection has occurred in the case of mixed version clusters. It is up to users to appropriately + define a `ReadPreference` such that intermittent errors do not occur. + +> [!NOTE] +> `ReadConcern` is only supported for commands. + +### Write Concern + +When a driver sends a write concern document to the server, the structure of the write concern document MUST be as +follows: + +```typescript +class WriteConcern { + /** + * If true, wait for the the write operation to get committed to the + * journal. When unspecified, a driver MUST NOT send "j". + * + * @see https://www.mongodb.com/docs/manual/core/write-concern/#journaled + */ + j: Optional, + + /** + * When an integer, specifies the number of nodes that should acknowledge + * the write and MUST be greater than or equal to 0. + * When a string, indicates tags. "majority" is defined, but users + * could specify other custom error modes. + * When not specified, a driver MUST NOT send "w". + */ + w: Optional, + + /** + * If provided, and the write concern is not satisfied within the + * specified timeout (in milliseconds), the server will return an error + * for the operation. When unspecified, a driver SHOULD NOT send "wtimeout". + * + * The value, if provided, MUST be greater than or equal to 0. + * + * @see https://www.mongodb.com/docs/manual/core/write-concern/#timeouts + */ + wtimeout: Optional +} +``` + +When a driver provides a way for the application to specify the write concern, the following data structure SHOULD be +used. For acceptable naming and deviation guidance, see the [CRUD specification](../crud/crud.md#naming). + +```typescript +class WriteConcern { + /** + * Corresponds to the "j" field in the WriteConcern document sent to + * the server. + */ + journal: Optional, + + /** + * Corresponds to the "w" field in the WriteConcern document sent to + * the server. + */ + w: Optional, + + /** + * Corresponds to the "wtimeout" field in the WriteConcern document sent to + * the server. + * + * NOTE: This option is deprecated in favor of timeoutMS. + */ + wtimeoutMS: Optional +} +``` + +#### FSync + +FSync SHOULD be considered deprecated. Those drivers supporting the deprecated `fsync` option SHOULD treat `fsync` +identically to `journal` in terms of consistency with `w` and whether a `WriteConcern` that specifies `fsync` is +acknowledged or unacknowledged. + +#### wtimeoutMS + +`wtimeoutMS` MUST be considered deprecated in favor of +[timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms). + +#### Server's Default WriteConcern + +When a `WriteConcern` is created but no values are specified, it should be considered the server's default +`WriteConcern`. + +The server has a settings field called `getLastErrorDefaults` which allows a user to customize the default behavior of a +`WriteConcern`. Because of this, `writeConcern: { }` is not the same as - `writeconcern: {w: 1}`. Sending `{w:1}` +overrides that default. As another example, `writeConcern: { }` is not the same as `writeConcern: {journal: false}`. + +#### Inconsistent WriteConcern + +Drivers MUST raise an error when an inconsistent `WriteConcern` is specified. The following is an exhaustive list of +inconsistent `WriteConcerns`: + +```typescript +writeConcern = { w: 0, journal: true }; +``` + +#### Unacknowledged WriteConcern + +An `Unacknowledged WriteConcern` is when (`w` equals 0) AND (`journal` is not set or is `false`). + +These criteria indicates that the user does not care about errors from the server. + +Examples: + +```typescript +writeConcern = { w: 0 }; // Unacknowledged +writeConcern = { w: 0, journal: false }; // Unacknowledged +writeConcern = { w: 0, wtimeoutMS: 100 }; // Unacknowledged +``` + +#### On the Wire + +##### Write Commands + +The `insert`, `delete`, and `update` commands take a named parameter, `writeConcern`. See the command documentation for +further examples. + +When a user has not specified a `WriteConcern` or has specified the server's default `WriteConcern`, drivers MUST omit +the `writeConcern` parameter from the command. + +All other `WriteConcerns`, including the `Unacknowledged WriteConcern`, MUST be sent with the `writeConcern` parameter. + +> [!NOTE] +> Drivers MAY use `OP_INSERT`, `OP_UPDATE`, and `OP_DELETE` when an `Unacknowledged WriteConcern` is used. + +##### Generic Command Method + +If your driver offers a generic `RunCommand` method on your `database` object, `WriteConcern` MUST NOT be applied +automatically to any command. A user wishing to use a `WriteConcern` in a generic command must manually include it in +the command document passed to the method. + +The generic command method MUST NOT check the user's command document for a `WriteConcern` nor check whether the server +is new enough to support a write concern for the command. The method simply sends the user's command to the server +as-is. + +##### Find And Modify + +The `findAndModify` command takes a named parameter, `writeConcern`. See command documentation for further examples. + +If writeConcern is specified for the Collection, `writeConcern` MUST be omitted when sending `findAndModify` with +MaxWireVersion < 4. + +If the findAndModify helper accepts writeConcern as a parameter, the driver MUST raise an error with MaxWireVersion \< +4\. + +> [!NOTE] +> Driver documentation SHOULD include a warning in their server 3.2 compatible releases that an elevated `WriteConcern` +> may cause performance degradation when using `findAndModify`. This is because `findAndModify` will now be honoring a +> potentially high latency setting where it did not before. + +##### Other commands that write + +Command helper methods for commands that write, other than those discussed above, MAY accept a write concern or write +concern options in their parameter list. If the helper accepts a write concern, the driver MUST error if the selected +server's MaxWireVersion < 5 and a write concern has explicitly been specified. + +Helper methods that apply the write concern inherited from the Collection or Database, SHOULD check whether the selected +server's MaxWireVersion >= 5 and if so, include the inherited write concern in the command on the wire. If the selected +server's MaxWireVersion < 5, these methods SHOULD silently omit the write concern from the command on the wire. + +These commands that write are: + +> - `aggregate` with write stage (e.g. `$out`, `$merge`) +> - `copydb` +> - `create` +> - `createIndexes` +> - `drop` +> - `dropDatabase` +> - `dropIndexes` +> - `mapReduce` where the `out` option is not `{ inline: 1 }` +> - `clone` +> - `cloneCollection` +> - `cloneCollectionAsCapped` +> - `collMod` +> - `convertToCapped` +> - `renameCollection` +> - `createUser` +> - `updateUser` +> - `dropUser` + +##### Errors + +In general, server errors associated with `WriteConcern` return successful (`"ok": 1`) responses with a +`writeConcernError` field indicating the issue. For example, + +```typescript +rs0:PRIMARY> db.runCommand({insert: "foo", documents: [{x:1}], writeConcern: { w: "blah"}}) +{ + n: 1, + opTime: { + ts: Timestamp(1583026145, 1), + t: NumberLong(5) + }, + electionId: ObjectId("7fffffff0000000000000005"), + ok: 1, + writeConcernError: { + code: 79, + codeName: "UnknownReplWriteConcern", + errmsg: "No write concern mode named 'blah' found in replica set configuration", + errInfo: { + writeConcern: { + w: "blah", + wtimeout: 0, + provenance: "clientSupplied" + } + } + }, + $clusterTime: { + clusterTime: Timestamp(1583026145, 1), + signature: { + hash: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="), + keyId: NumberLong(0) + } + }, + operationTime: Timestamp(1583026145, 1) +} +``` + +Drivers SHOULD parse server replies for a "writeConcernError" field and report the error only in the command-specific +helper methods for commands that write, from the list above. For example, helper methods for "findAndModify" or +"aggregate" SHOULD parse the server reply for "writeConcernError". + +Drivers SHOULD report writeConcernErrors however they report other server errors: by raising an exception, returning +"false", or another idiom that is consistent with other server errors. Drivers SHOULD report writeConcernErrors with a +`WriteConcernError` defined in the [CRUD specification](../crud/crud.md#error-handling). + +Drivers SHOULD NOT parse server replies for "writeConcernError" in generic command methods. + +(Reporting of writeConcernErrors is more complex for bulk operations, see the Bulk API Spec.) + +###### writeConcernError Examples + +The set of possible writeConcernErrors is quite large because they can include errors caused by shutdown, stepdown, +interruption, maxTimeMS, and wtimeout. This section attempts to list all known error codes that may appear within a +writeConcernError but may not be exhaustive. Note that some errors have been abbreviated: + +- `{ok:1, writeConcernError: {code: 91, codeName: "ShutdownInProgress"}}` +- `{ok:1, writeConcernError: {code: 189, codeName: "PrimarySteppedDown"}}` +- `{ok:1, writeConcernError: {code: 11600, codeName: "InterruptedAtShutdown"}}` +- `{ok:1, writeConcernError: {code: 11601, codeName: "Interrupted"}}` +- `{ok:1, writeConcernError: {code: 11602, codeName: "InterruptedDueToReplStateChange"}}` +- `{ok:1, writeConcernError: {code: 64, codeName: "WriteConcernTimeout", errmsg: "waiting for replication timed out", errInfo: {wtimeout: True}}}` +- `{ok:1, writeConcernError: {code: 64, codeName: "WriteConcernTimeout", errmsg: "multiple errors reported : {...} at shardName1 :: and :: {...} at shardName2"}}`[^1] +- `{ok:1, writeConcernError: {code: 50, codeName: "MaxTimeMSExpired"}}` +- `{ok:1, writeConcernError: {code: 100, codeName: "UnsatisfiableWriteConcern", errmsg: "Not enough data-bearing nodes"}}` +- `{ok:1, writeConcernError: {code: 79, codeName: "UnknownReplWriteConcern"}}` + +Note also that it is possible for a writeConcernError to be attached to a command failure. For example: + +- `{ok:0, code: 251, codeName: "NoSuchTransaction", writeConcernError: {code: 91, codeName: "ShutdownInProgress"}}`[^2] + +#### Location Specification + +##### Via Code + +`WriteConcern` SHOULD be specifiable at the `Client`, `Database`, and `Collection` levels. Unless specified, the value +MUST be inherited from its parent and SHOULD NOT be modifiable on an existing `Client`, `Database` or `Collection`. In +addition, a driver MAY allow it to be specified on a per-operation basis in accordance with the CRUD specification. + +For example: + +```typescript +var client = new MongoClient({ writeConcern: { w: 2 } }); + +// db1's writeConcern is {w: 2}. +var db1 = client.getDatabase("db1"); + +// col1's writeConcern is {w: 2}. +var col1 = db1.getCollection("col_name"); + +// db2's writeConcern is {journal: true}. +var db2 = client.getDatabase("db_name", { writeConcern: { journal: true } }); + +// col2's writeConcern {journal: true}. +var col2 = db2.getCollection("col_name"); + +// col3's writeConcern is the server's default write concern. +var col3 = db2.getCollection("col_name", { writeConcern: { } }); + +// Override col3's writeConcern. +col3.drop({ writeConcern: { w: 3 } }); +``` + +##### Via Connection String + +Options + +- `w` - corresponds to `w` in the class definition. +- `journal` - corresponds to `journal` in the class definition. +- `wtimeoutMS` - corresponds to `wtimeoutMS` in the class definition. + +For example: + +```text +mongodb://server:27017/db?w=3 + +mongodb://server:27017/db?journal=true + +mongodb://server:27017/db?wtimeoutMS=1000 + +mongodb://server:27017/db?w=majority&wtimeoutMS=1000 +``` + +## Backwards Compatibility + +There should be no backwards compatibility concerns. This specification merely deals with how to specify read and write +concerns. + +## Test Plan + +Yaml tests are located [here](../read-write-concern/tests/README.md) + +Below are English descriptions of other items that should be tested: + +### ReadConcern + +1. Commands supporting a read concern MUST raise an error when MaxWireVersion is less than 4 and a non-default, + non-local read concern is specified. +2. Commands supporting a read concern MUST NOT send the default read concern to the server. +3. Commands supporting a read concern MUST send any non-default read concern to the server. + +### WriteConcern + +1. Commands supporting a write concern MUST NOT send the default write concern to the server. +2. Commands supporting a write concern MUST send any non-default write concern to the server in the command. + +## Reference Implementation + +These are currently under construction. + +## Q & A + +Q: Why is specifying a non-default `ReadConcern` for servers < 3.2 an error while a non-default write concern gets +ignored in `findAndModify`? `findAndModify` is an existing command and since `WriteConcern` may be defined globally, +anyone using `findAndModify` in their applications with a non-default `WriteConcern` defined globally would have all +their `findAndModify` operations fail. + +Q: Why does a driver send `{ readConcern: { level: "local" } }` to the server when that is the server's default? First, +to mirror how `WriteConcern` already works, `ReadConcern() does not equal ReadConcern(level=local)` in the same way that +`WriteConcern() does not equal WriteConcern(w=1)`. This is true for `WriteConcern` because the server's default could be +set differently. While this setting does not currently exist for `ReadConcern`, it is a possible eventuality and it +costs a driver nothing to be prepared for it. Second, it makes sense that if a user doesn't specify a `ReadConcern`, we +don't send one and if a user does specify a `ReadConcern`, we do send one. If the user specifies level="local", for +instance, we send it. + +## Changelog + +- 2026-05-04: Describe how `readConcern.afterClusterTime` is used in causally-consistent sessions on reads and writes. + +- 2025-02-25: Rename WriteConcernFailed to WriteConcernTimeout + +- 2015-10-16: ReadConcern of local is no longer allowed to be used when talking with MaxWireVersion < 4. + +- 2016-05-20: Added note about helpers for commands that write accepting a writeConcern parameter. + +- 2016-06-17: Added "linearizable" to ReadConcern levels. + +- 2016-07-15: Command-specific helper methods for commands that write SHOULD check the server's MaxWireVersion and + decide whether to send writeConcern. Advise drivers to parse server replies for writeConcernError and raise an + exception if found, only in command-specific helper methods that take a writeConcern parameter, not in generic + command methods. Don't mention obscure commands with no helpers. + +- 2016-08-06: Further clarify that command-specific helper methods for commands that write take write concern options in + their parameter lists, and relax from SHOULD to MAY. + +- 2017-03-13: reIndex silently ignores writeConcern in MongoDB 3.4 and returns an error if writeConcern is included with + MongoDB 3.5+. See [SERVER-27891](https://jira.mongodb.org/browse/SERVER-27891). + +- 2017-11-17: Added list of commands that support readConcern + +- 2017-12-18: Added "available" to Readconcern level. + +- 2017-05-29: Added user management commands to list of commands that write + +- 2019-01-29: Added section listing all known examples of writeConcernError. + +- 2019-06-07: Clarify language for aggregate and mapReduce commands that write. + +- 2019-10-31: Explicitly define write concern option mappings. + +- 2020-02-13: Inconsistent write concern must be considered an error. + +- 2021-04-07: Updated to use hello command. + +- 2021-06-15: Added "snapshot" to Readconcern level + +- 2021-07-12: Add missing commas after ReadConcernLevel enum values + +- 2022-01-19: Deprecate wTimeoutMS in favor of timeoutMS. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2024-08-23: Migrated from reStructuredText to Markdown. + +- 2024-10-30: Remove reference to getLastError + +[^1]: This is only possible in a sharded cluster. When a write is routed to multiple shards and more than one shard + returns a writeConcernError, then mongos will construct a new writeConcernError with the "WriteConcernTimeout" error + code and an errmsg field contains the stringified writeConcernError from each shard. Note that each shard may return + a different writeConcernError. + +[^2]: See diff --git a/source/read-write-concern/read-write-concern.rst b/source/read-write-concern/read-write-concern.rst index 0de22676b3..74e7b4625c 100644 --- a/source/read-write-concern/read-write-concern.rst +++ b/source/read-write-concern/read-write-concern.rst @@ -1,504 +1,4 @@ -.. role:: javascript(code) - :language: javascript - -====================== -Read and Write Concern -====================== - -:Spec: 135 -:Title: Read and Write Concern -:Authors: Craig Wilson -:Advisors: Jesse Davis, Hannes Magnusson, Anna Herlihy -:Status: Approved -:Type: Standards -:Server Versions: 2.4+ -:Last Modified: December 18, 2017 -:Version: 1.5 - -.. contents:: - --------- - -Abstract -======== - -A driver must support configuring and sending read concern and write concerns -to a server. This specification defines the API drivers must implement as well as how that API is translated into messages for communication with the server. - -META -==== - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Terminology -=========== - -MaxWireVersion - The ``maxWireVersion`` value reported by the ``ismaster`` command. -Server Selection - The process of selecting a server to read or write from. See https://github.com/mongodb/specifications/tree/master/source/server-selection. - -Specification -============= - - -This specification includes guidance for implementing `Read Concern`_ and `Write Concern`_ in a driver. It does not define how read and write concern behave or are implemented on the server. - ------------- -Read Concern ------------- - -For naming and deviation guidance, see the `CRUD specification `_. Defined below are the constructs for drivers. - -.. code:: typescript - - enum ReadConcernLevel { - /** - * This is rendered as "local" (lower-case) on the wire. - */ - local, - - /** - * This is rendered as "majority" (lower-case) on the wire. - */ - majority, - - /** - * This is rendered as "linearizable" (lower-case) on the wire. - */ - linearizable - - /** - * This is rendered as "available" (lower-case) on the wire. - */ - available - } - - class ReadConcern { - /** - * The level of the read concern. - */ - level: Optional - } - -The read concern option is available for the following operations: - -- ``find`` command -- ``aggregate`` command -- ``distinct`` command -- ``count`` command -- ``parallelCollectionScan`` command -- ``geoNear`` command -- ``geoSearch`` command -- ``mapReduce`` command with {out : inline} output option - -``aggregate`` command with $out and ``mapReduce`` command with "out" set to anything other than "inline" do not support readConcern. - -Unknown Levels and Additional Options for String Based ReadConcerns -------------------------------------------------------------------- - -For forward compatibility, a driver MUST NOT raise an error when a user provides an unknown ``level`` or additional options. The driver relies on the server to validate levels and other contents of the read concern. - - -Server’s Default Read Concern ------------------------------ - -When a ``ReadConcern`` is created but no values are specified, it should be considered the server’s default ``ReadConcern``. - -:javascript:`readConcern: { }` is not the same as :javascript:`readConcern: { level=“local” }`. The former is the server’s default ``ReadConcern`` while the latter is the user explicitly specifying a ``ReadConcern`` with a ``level`` of “local”. - - -On the Wire ------------ - -Read Commands -~~~~~~~~~~~~~ - -Read commands that support ``ReadConcern`` take a named parameter spelled (case-sensitively) ``readConcern``. See command documentation for further examples. - -If the ``Client``, ``Database``, or ``Collection`` being operated on either has no ``ReadConcern`` set, or has the server default ``ReadConcern`` :javascript:`readConcern: { }`: - -- If the ``ReadConcern`` specified for the command is the server default :javascript:`readConcern: { }`, the driver MUST omit it when sending the command. -- If the ``ReadConcern`` specified for the command is any ``ReadConcern`` besides the server default, including an explicitly specified ``ReadConcern`` of :javascript:`readConcern: { level: "local" }`, the driver MUST include the ``ReadConcern`` when sending the command. - -If the ``Client``, ``Database``, or ``Collection`` being operated on has a non-default ``ReadConcern`` specified, then the driver MUST include the command's ``ReadConcern`` when sending the command. This includes if the command specifies the server default ``ReadConcern``, so that the command can override the ``Client``, ``Database``, or ``Collection``'s ``ReadConcern`` to use the server default instead. - - -Generic Command Method -~~~~~~~~~~~~~~~~~~~~~~ - -If your driver offers a generic ``RunCommand`` method on your ``database`` object, ``ReadConcern`` MUST NOT be applied automatically to any command. A user wishing to use a ``ReadConcern`` in a generic command must supply it manually. - - -Errors -~~~~~~ - -``ReadConcern`` errors from a server MUST NOT be handled by a driver. There is nothing a driver can do about them and any such errors will get propagated to the user via normal error handling. - - -Location Specification ----------------------- - -Via Code -~~~~~~~~ - -``ReadConcern`` SHOULD be specifiable at the ``Client``, ``Database``, and ``Collection`` levels. Unless specified, the value MUST be inherited from its parent and SHOULD NOT be modifiable on an existing ``Client``, ``Database``, and ``Collection``. In addition, a driver MAY allow it to be specified on a per-operation basis in accordance with the CRUD specification. - -For example: - -.. code:: typescript - - var client = new MongoClient({ readConcern: { level: "local" } }); - - // db1's readConcern level is "local". - var db1 = client.getDatabase("db1"); - - // col1's readConcern level is "local" - var col1 = db1.getCollection("col_name"); - - // db2's readConcern level is "majority". - var db2 = client.getDatabase("db_name", { readConcern: { level: "majority" } }); - - // col2's readConcern level is "majority" - var col2 = db2.getCollection("col_name"); - - // col3's readConcern level is the server’s default read concern - var col3 = db2.getCollection("col_name", { readConcern: { } }); - - -Via Connection String -~~~~~~~~~~~~~~~~~~~~~ - -Options - * ``readConcernLevel`` - defines the level for the read concern. - -For example: - -.. code:: - - mongodb://server:27017/db?readConcernLevel=majority - -Errors ------- - -MaxWireVersion < 4 - Only the server’s default ``ReadConcern`` is support by ``MaxWireVersion`` < 4. When using other ``readConcernLevels`` with clients reporting ``MaxWireVersion`` < 4, the driver MUST raise an error. This check MUST happen after server selection has occurred in the case of mixed version clusters. It is up to users to appropriately define a ``ReadPreference`` such that intermittent errors do not occur. - -.. note:: - - ``ReadConcern`` is only supported for commands. - -------------- -Write Concern -------------- - -For naming and deviation guidance, see the `CRUD specification `_. Below are defined the constructs for drivers. - -.. code:: typescript - - class WriteConcern { - /** - * If true, wait for the the write operation to get committed to the journal. When unspecified, a driver - * MUST NOT send "j". - * - * @see http://docs.mongodb.org/manual/core/write-concern/#journaled - */ - journal: Optional, - - /** - * When an integer, specifies the number of nodes that should acknowledge the write - * and MUST be greater than or equal to 0. - * When a string, indicates tags. "majority" is defined, but users - * could specify other custom error modes. - * When not specified, a driver MUST NOT send "w". - */ - w: Optional, - - /** - * If the write concern is not satisfied within the specified timeout (in milliseconds), - * the operation will return an error. - * The value MUST be greater than or equal to 0. - * When not specified, a driver should not send "wtimeout". - * - * @see http://docs.mongodb.org/manual/core/write-concern/#timeouts - */ - wtimeoutMS: Optional - } - - -FSync ------ - -FSync SHOULD be considered deprecated. Those drivers supporting the deprecated ``fsync`` option SHOULD treat ``fsync`` identically to ``journal`` in terms of consistency with ``w`` and whether a ``WriteConcern`` that specifies ``fsync`` is acknowledged or unacknowledged. - - -Server’s Default WriteConcern ------------------------------ - -When a ``WriteConcern`` is created but no values are specified, it should be considered the server’s default ``WriteConcern``. - -The server has a settings field called ``getLastErrorDefaults`` which allows a user to customize the default behavior of a ``WriteConcern``. Because of this, :javascript:`writeConcern: { }` is not the same as :javascript:`writeConcern: {w: 1}`. Sending :javascript:`{w:1}` overrides that default. As another example, :javascript:`writeConcern: { }` is not the same as :javascript:`writeConcern: {journal: false}`. - - -Inconsistent WriteConcern -------------------------- - -Drivers SHOULD raise an error when an inconsistent ``WriteConcern`` is specified. The following is an exhaustive list of inconsistent ``WriteConcerns``: - -.. code:: typescript - - writeConcern = { w: 0, journal: true }; - -If, for the sake of backwards compatibility, the driver allows inconsistent write concerns where ``w`` equals 0 but ``journal`` is set to ``true``, the driver MUST treat it as an ``Acknowledged WriteConcern``. - - -Unacknowledged WriteConcern ---------------------------- - -An ``Unacknowledged WriteConcern`` is when (``w`` equals 0) AND (``journal`` is not set or is ``false``). - -These criteria indicates that the user does not care about errors from the server. - -Examples: - -.. code:: typescript - - writeConcern = { w: 0 }; // Unacknowledged - writeConcern = { w: 0, journal: false }; // Unacknowledged - writeConcern = { w: 0, wtimeoutMS: 100 }; // Unacknowledged - - -On the Wire ------------ - -OP_INSERT, OP_DELETE, OP_UPDATE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``WriteConcern`` is implemented by sending the ``getLastError``(GLE) command directly after the operation. Drivers SHOULD piggy-back the GLE onto the same buffer as the operation. Regardless, GLE MUST be sent on the same connection as the initial write operation. - -When a user has not specified a ``WriteConcern`` or has specified the server’s default ``WriteConcern``, drivers MUST send the GLE command without arguments. For example: :javascript:`{ getLastError: 1 }` - -Drivers MUST NOT send a GLE for an ``Unacknowledged WriteConcern``. In this instance, the server will not send a reply. - -See the ``getLastError`` command documentation for other formatting. - - -Write Commands -~~~~~~~~~~~~~~ - -The ``insert``, ``delete``, and ``update`` commands take a named parameter, ``writeConcern``. See the command documentation for further examples. - -When a user has not specified a ``WriteConcern`` or has specified the server’s default ``WriteConcern``, drivers MUST omit the ``writeConcern`` parameter from the command. - -All other ``WriteConcerns``, including the ``Unacknowledged WriteConcern``, MUST be sent with the ``writeConcern`` parameter. .. note:: - Drivers MAY use ``OP_INSERT``, ``OP_UPDATE``, and ``OP_DELETE`` when an ``Unacknowledged WriteConcern`` is used. - -Generic Command Method -~~~~~~~~~~~~~~~~~~~~~~ - -If your driver offers a generic ``RunCommand`` method on your ``database`` object, ``WriteConcern`` MUST NOT be applied automatically to any command. A user wishing to use a ``WriteConcern`` in a generic command must manually include it in the command document passed to the method. - -The generic command method MUST NOT check the user's command document for a ``WriteConcern`` nor check whether the server is new enough to support a write concern for the command. The method simply sends the user's command to the server as-is. - -Find And Modify -~~~~~~~~~~~~~~~ - -The ``findAndModify`` command takes a named parameter, ``writeConcern``. See command documentation for further examples. - -If writeConcern is specified for the Collection, ``writeConcern`` MUST be omitted when sending ``findAndModify`` with MaxWireVersion < 4. - -If the findAndModify helper accepts writeConcern as a parameter, the driver MUST raise an error with MaxWireVersion < 4. - -.. note :: - Driver documentation SHOULD include a warning in their server 3.2 compatible releases that an elevated ``WriteConcern`` may cause performance degradation when using ``findAndModify``. This is because ``findAndModify`` will now be honoring a potentially high latency setting where it did not before. - -Other commands that write -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Command helper methods for commands that write, other than those discussed above, -MAY accept a write concern or write concern options in their parameter list. -If the helper accepts a write concern, the driver MUST error if the selected server's MaxWireVersion < 5 and a -write concern has explicitly been specified. - -Helper methods that apply the write concern inherited from the Collection or Database, SHOULD check whether the -selected server's MaxWireVersion >= 5 and if so, include the inherited write concern in the command on the wire. -If the selected server's MaxWireVersion < 5, these methods SHOULD silently omit the write concern from the command -on the wire. - -These commands that write are: - * ``aggregate`` with ``$out`` - * ``copydb`` - * ``create`` - * ``createIndexes`` - * ``drop`` - * ``dropDatabase`` - * ``dropIndexes`` - * ``mapReduce`` with ``$out`` - * ``clone`` - * ``cloneCollection`` - * ``cloneCollectionAsCapped`` - * ``collMod`` - * ``convertToCapped`` - * ``renameCollection`` - * ``createUser`` - * ``updateUser`` - * ``dropUser`` - -Errors -~~~~~~ - -Server errors associated with ``WriteConcern`` return successful responses with a ``writeConcernError`` field indicating the issue. For example, - -.. code:: typescript - - rs0:PRIMARY> db.runCommand({insert: "foo", documents: [{x:1}], writeConcern: { w: "blah"}}) - { - "ok" : 1, - "n" : 1, - "lastOp" : Timestamp(1441992923, 1), - "electionId" : ObjectId("55f30e4cffffffffffffffff"), - "writeConcernError" : { - "code" : 79, - "errmsg" : "No write concern mode named 'blah' found in replica set configuration" - } - } - -Drivers SHOULD parse server replies for a "writeConcernError" field and report the error -only in the command-specific helper methods for commands that write, from the list above. -For example, helper methods for "findAndModify" or "aggregate" SHOULD parse the server reply -for "writeConcernError". - -Drivers SHOULD report writeConcernErrors however they report other server errors: -by raising an exception, returning "false", or another idiom that is consistent with other server errors. - -Drivers SHOULD NOT parse server replies for "writeConcernError" in generic command methods. - -(Reporting of writeConcernErrors is more complex for bulk operations, -see the Bulk API Spec.) - -Location Specification ----------------------- - -Via Code -~~~~~~~~ - -``WriteConcern`` SHOULD be specifiable at the ``Client``, ``Database``, and ``Collection`` levels. Unless specified, the value MUST be inherited from its parent and SHOULD NOT be modifiable on an existing ``Client``, ``Database``, and ``Collection``. In addition, a driver MAY allow it to be specified on a per-operation basis in accordance with the CRUD specification. - -For example: - -.. code:: typescript - - var client = new MongoClient({ writeConcern: { w: 2 } }); - - // db1's writeConcern is {w: 2}. - var db1 = client.getDatabase("db1"); - - // col1's writeConcern is {w: 2}. - var col1 = db1.getCollection("col_name"); - - // db2's writeConcern is {journal: true}. - var db2 = client.getDatabase("db_name", { writeConcern: { journal: true } }); - - // col2's writeConcern {journal: true}. - var col2 = db2.getCollection("col_name"); - - // col3's writeConcern is the server’s default write concern. - var col3 = db2.getCollection("col_name", { writeConcern: { } }); - - // Override col3's writeConcern. - col3.drop({ writeConcern: { w: 3 } }); - - -Via Connection String -~~~~~~~~~~~~~~~~~~~~~ - -Options - * ``w`` - corresponds to ``w`` in the class definition. - * ``journal`` - corresponds to ``journal`` in the class definition. - * ``wtimeoutMS`` - corresponds to ``wtimeoutMS`` in the class definition. - -For example: - -.. code:: - - mongodb://server:27017/db?w=3 - - mongodb://server:27017/db?journal=true - - mongodb://server:27017/db?wtimeoutMS=1000 - - mongodb://server:27017/db?w=majority&wtimeoutMS=1000 - - - -Backwards Compatibility -======================= - -There should be no backwards compatibility concerns. This specification merely deals with how to specify read and write concerns. - -Test Plan -========= - -Yaml tests are located here: https://github.com/mongodb/specifications/tree/master/source/read-write-concern/tests - -Below are English descriptions of other items that should be tested: - ------------ -ReadConcern ------------ - -1. Commands supporting a read concern MUST raise an error when MaxWireVersion is less than 4 and a non-default, non-local read concern is specified. -2. Commands supporting a read concern MUST NOT send the default read concern to the server. -3. Commands supporting a read concern MUST send any non-default read concern to the server. - ------------- -WriteConcern ------------- - -1. Commands supporting a write concern MUST NOT send the default write concern to the server. -2. Commands supporting a write concern MUST send any non-default acknowledged write concern to the server, either in the command or as a getLastError. -3. On ServerVersion less than 2.6, drivers MUST NOT send a getLastError command for an Unacknowledged write concern. -4. FindAndModify helper methods MUST NOT send a write concern when the MaxWireVersion is less than 4. -5. Helper methods for other commands that write MUST NOT send a write concern when the MaxWireVersion is less than 5. - -Reference Implementation -======================== - -These are currently under construction. - - -Q & A -===== - -Q: Why is specifying a non-default ``ReadConcern`` for servers < 3.2 an error while a non-default write concern gets ignored in ``findAndModify``? - ``findAndModify`` is an existing command and since ``WriteConcern`` may be defined globally, anyone using ``findAndModify`` in their applications with a non-default ``WriteConcern`` defined globally would have all their ``findAndModify`` operations fail. - -Q: Why does a driver send :javascript:`{ readConcern: { level: “local” } }` to the server when that is the server’s default? - First, to mirror how ``WriteConcern`` already works, ``ReadConcern() does not equal ReadConcern(level=local)`` in the same way that ``WriteConcern() does not equal WriteConcern(w=1)``. This is true for ``WriteConcern`` because the server’s default could be set differently. While this setting does not currently exist for ``ReadConcern``, it is a possible eventuality and it costs a driver nothing to be prepared for it. - Second, it makes sense that if a user doesn’t specify a ``ReadConcern``, we don’t send one and if a user does specify a ``ReadConcern``, we do send one. If the user specifies level=”local”, for instance, we send it. - -Version History -=============== - - - 2015-10-16: ReadConcern of local is no longer allowed to be used when talking with MaxWireVersion < 4. - - 2016-05-20: Added note about helpers for commands that write accepting a writeConcern parameter. - - 2016-06-17: Added "linearizable" to ReadConcern levels. - - 2016-07-15: Command-specific helper methods for commands that write SHOULD check the server's MaxWireVersion - and decide whether to send writeConcern. - Advise drivers to parse server replies for writeConcernError - and raise an exception if found, - only in command-specific helper methods that take a writeConcern parameter, - not in generic command methods. - Don't mention obscure commands with no helpers. - - 2016-08-06: Further clarify that command-specific helper methods for commands that write - take write concern options in their parameter lists, and relax from SHOULD to MAY. - - 2017-03-13: reIndex silently ignores writeConcern in MongoDB 3.4 and returns - an error if writeConcern is included with MongoDB 3.5+. See - `SERVER-27891 `_. - - 2017-11-17 : Added list of commands that support readConcern - - 2017-12-18 : Added "available" to Readconcern level. - - 2017-05-29 : Added user management commands to list of commands that write + This specification has been converted to Markdown and renamed to + `read-write-concern.md `_. diff --git a/source/read-write-concern/tests/README.md b/source/read-write-concern/tests/README.md new file mode 100644 index 0000000000..7dc5a460a6 --- /dev/null +++ b/source/read-write-concern/tests/README.md @@ -0,0 +1,55 @@ +# Read and Write Concern Tests + +The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their +conformance to the Read and Write Concern specification. + +## Version + +Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +## Format + +### Connection String + +These tests are designed to exercise the connection string parsing related to read concern and write concern. + +Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which +have the following keys: + +- `description`: A string describing the test. +- `uri`: A string containing the URI to be parsed. +- `valid:`: a boolean indicating if parsing the uri should result in an error. +- `writeConcern:` A document indicating the expected write concern. +- `readConcern:` A document indicating the expected read concern. + +If a test case includes a null value for one of these keys, or if the key is missing, no assertion is necessary. This +both simplifies parsing of the test files and allows flexibility for drivers that might substitute default values +*during* parsing. + +### Document + +These tests are designed to ensure compliance with the spec in relation to what should be sent to the server. + +Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which +have the following keys: + +- `description`: A string describing the test. +- `valid:`: a boolean indicating if the write concern created from the document is valid. +- `writeConcern:` A document indicating the write concern to use. +- `writeConcernDocument:` A document indicating the write concern to be sent to the server. +- `readConcern:` A document indicating the read concern to use. +- `readConcernDocument:` A document indicating the read concern to be sent to the server. +- `isServerDefault:` Indicates whether the read or write concern is considered the server's default. +- `isAcknowledged:` Indicates if the write concern should be considered acknowledged. + +### Operation + +These tests check that the default write concern is omitted in operations. + +The tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +#### Use as unit tests + +Testing whether a URI is valid or not should simply be a matter of checking whether URI parsing raises an error or +exception. Testing for emitted warnings may require more legwork (e.g. configuring a log handler and watching for +output). diff --git a/source/read-write-concern/tests/README.rst b/source/read-write-concern/tests/README.rst deleted file mode 100644 index 7386df8687..0000000000 --- a/source/read-write-concern/tests/README.rst +++ /dev/null @@ -1,61 +0,0 @@ -======================= -Connection String Tests -======================= - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the Read and Write Concern -specification. - -Version -------- - -Files in the "specifications" repository have no version scheme. They are not -tied to a MongoDB server version. - -Format ------- - -Connection String -~~~~~~~~~~~~~~~~~ - -These tests are designed to exercise the connection string parsing related -to read concern and write concern. - -Each YAML file contains an object with a single ``tests`` key. This key is an -array of test case objects, each of which have the following keys: - -- ``description``: A string describing the test. -- ``uri``: A string containing the URI to be parsed. -- ``valid:``: a boolean indicating if parsing the uri should result in an error. -- ``writeConcern:`` A document indicating the expected write concern. -- ``readConcern:`` A document indicating the expected read concern. - -If a test case includes a null value for one of these keys, or if the key is missing, -no assertion is necessary. This both simplifies parsing of the test files and allows flexibility -for drivers that might substitute default values *during* parsing. - -Document -~~~~~~~~ - -These tests are designed to ensure compliance with the spec in relation to what should be -sent to the server. - -Each YAML file contains an object with a single ``tests`` key. This key is an -array of test case objects, each of which have the following keys: - -- ``description``: A string describing the test. -- ``valid:``: a boolean indicating if the write concern created from the document is valid. -- ``writeConcern:`` A document indicating the write concern to use. -- ``writeConcernDocument:`` A document indicating the write concern to be sent to the server. -- ``readConcern:`` A document indicating the read concern to use. -- ``readConcernDocument:`` A document indicating the read concern to be sent to the server. -- ``isServerDefault:`` Indicates whether the read or write concern is considered the server's default. -- ``isAcknowledged:`` Indicates if the write concern should be considered acknowledged. - -Use as unit tests -================= - -Testing whether a URI is valid or not should simply be a matter of checking -whether URI parsing raises an error or exception. -Testing for emitted warnings may require more legwork (e.g. configuring a log -handler and watching for output). diff --git a/source/read-write-concern/tests/operation/default-write-concern-2.6.json b/source/read-write-concern/tests/operation/default-write-concern-2.6.json new file mode 100644 index 0000000000..0d8f9c98a1 --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-2.6.json @@ -0,0 +1,636 @@ +{ + "description": "default-write-concern-2.6", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "2.6" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "default-write-concern-tests", + "databaseOptions": { + "writeConcern": {} + } + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll", + "collectionOptions": { + "writeConcern": {} + } + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "DeleteOne omits default write concern", + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "DeleteMany omits default write concern", + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "BulkWrite with all models omits default write concern", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "ordered": true, + "requests": [ + { + "deleteMany": { + "filter": {} + } + }, + { + "insertOne": { + "document": { + "_id": 1 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 2 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 2 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3 + } + } + }, + { + "updateMany": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 3 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": {}, + "limit": 0 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 1 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 2 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 2 + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 3 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 3 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "InsertOne and InsertMany omit default write concern", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3 + } + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 3 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "coll", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + ] + }, + { + "description": "UpdateOne, UpdateMany, and ReplaceOne omit default write concern", + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": 2 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "replacement": { + "x": 3 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 1 + } + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": 2 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "coll", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "x": 3 + }, + "upsert": { + "$$unsetOrMatches": false + }, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 1 + }, + { + "_id": 2, + "x": 3 + } + ] + } + ] + } + ] +} diff --git a/source/read-write-concern/tests/operation/default-write-concern-2.6.yml b/source/read-write-concern/tests/operation/default-write-concern-2.6.yml new file mode 100644 index 0000000000..d18962a7fb --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-2.6.yml @@ -0,0 +1,296 @@ +# Test that setting a default write concern does not add a write concern to the command sent over the wire. +# Test operations that require 2.6+ server. + +description: default-write-concern-2.6 + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "2.6" + +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name default-write-concern-tests + databaseOptions: + writeConcern: {} + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + collectionOptions: + writeConcern: {} + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: DeleteOne omits default write concern + operations: + - + name: deleteOne + object: *collection0 + arguments: + filter: {} + expectResult: + deletedCount: 1 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: [ { q: {}, limit: 1 } ] + writeConcern: { $$exists: false } + - + description: DeleteMany omits default write concern + operations: + - + name: deleteMany + object: *collection0 + arguments: + filter: {} + expectResult: + deletedCount: 2 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: [ { q: {}, limit: 0 } ] + writeConcern: { $$exists: false } + - + description: BulkWrite with all models omits default write concern + operations: + - + name: bulkWrite + object: *collection0 + arguments: + ordered: true + requests: + - + deleteMany: + filter: {} + - + insertOne: + document: { _id: 1 } + - + updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } } + - + insertOne: + document: { _id: 2 } + - + replaceOne: + filter: { _id: 1 } + replacement: { x: 2 } + - + insertOne: + document: { _id: 3 } + - + updateMany: + filter: { _id: 1 } + update: { $set: { x: 3 } } + - + deleteOne: + filter: { _id: 3 } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: [ { q: {}, limit: 0 } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + insert: *collection_name + documents: [ { _id: 1 } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $set: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + insert: *collection_name + documents: [ { _id: 2 } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { x: 2 } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + insert: *collection_name + documents: [ { _id: 3 } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $set: { x: 3 } } + multi: true + upsert: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + delete: *collection_name + deletes: [ { q: { _id: 3 }, limit: 1 } ] + writeConcern: { $$exists: false } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 3 } + - { _id: 2 } + - + description: InsertOne and InsertMany omit default write concern + operations: + - + name: insertOne + object: *collection0 + arguments: + document: { _id: 3 } + - + name: insertMany + object: *collection0 + arguments: + documents: + - { _id: 4 } + - { _id: 5 } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: [ { _id: 3 } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + insert: *collection_name + documents: [ { _id: 4 }, { _id: 5 } ] + writeConcern: { $$exists: false } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + - + description: UpdateOne, UpdateMany, and ReplaceOne omit default write concern + operations: + - + name: updateOne + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 } } + - + name: updateMany + object: *collection0 + arguments: + filter: { _id: 2 } + update: { $set: { x: 2 } } + - + name: replaceOne + object: *collection0 + arguments: + filter: { _id: 2 } + replacement: { x: 3 } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $set: { x: 1 } } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 2 } + u: { $set: { x: 2 } } + multi: true + upsert: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 2 } + u: { x: 3 } + upsert: { $$unsetOrMatches: false } + multi: { $$unsetOrMatches: false } + writeConcern: { $$exists: false } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1 } + - { _id: 2, x: 3 } diff --git a/source/read-write-concern/tests/operation/default-write-concern-3.2.json b/source/read-write-concern/tests/operation/default-write-concern-3.2.json new file mode 100644 index 0000000000..166a184916 --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-3.2.json @@ -0,0 +1,164 @@ +{ + "description": "default-write-concern-3.2", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.2" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "default-write-concern-tests", + "databaseOptions": { + "writeConcern": {} + } + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll", + "collectionOptions": { + "writeConcern": {} + } + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "findAndModify operations omit default write concern", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "replacement": { + "x": 2 + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll", + "query": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + }, + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll", + "query": { + "_id": 2 + }, + "update": { + "x": 2 + }, + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll", + "query": { + "_id": 2 + }, + "remove": true, + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/read-write-concern/tests/operation/default-write-concern-3.2.yml b/source/read-write-concern/tests/operation/default-write-concern-3.2.yml new file mode 100644 index 0000000000..eb109cb9fb --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-3.2.yml @@ -0,0 +1,91 @@ +# Test that setting a default write concern does not add a write concern to the command sent over the wire. +# Test operations that require 3.2+ server, where findAndModify started to accept a write concern. + +description: default-write-concern-3.2 + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "3.2" + +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name default-write-concern-tests + databaseOptions: + writeConcern: {} + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + collectionOptions: + writeConcern: {} + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: findAndModify operations omit default write concern + operations: + - + name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $set: { x: 1 } } + - + name: findOneAndReplace + object: *collection0 + arguments: + filter: { _id: 2 } + replacement: { x: 2 } + - + name: findOneAndDelete + object: *collection0 + arguments: + filter: { _id: 2 } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 1 } + update: { $set: { x: 1 } } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 2 } + update: { x: 2 } + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 2 } + remove: true + writeConcern: { $$exists: false } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 1 } diff --git a/source/read-write-concern/tests/operation/default-write-concern-3.4.json b/source/read-write-concern/tests/operation/default-write-concern-3.4.json new file mode 100644 index 0000000000..e18cdfc0c4 --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-3.4.json @@ -0,0 +1,278 @@ +{ + "description": "default-write-concern-3.4", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.4" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "default-write-concern-tests", + "databaseOptions": { + "writeConcern": {} + } + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll", + "collectionOptions": { + "writeConcern": {} + } + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $out omits default write concern", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_collection_name" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "other_collection_name" + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_collection_name", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "RunCommand with a write command omits default write concern (runCommand should never inherit write concern)", + "operations": [ + { + "object": "database0", + "name": "runCommand", + "arguments": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ] + }, + "commandName": "delete" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll", + "deletes": [ + { + "q": {}, + "limit": 1 + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "CreateIndex and dropIndex omits default write concern", + "operations": [ + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "keys": { + "x": 1 + } + } + }, + { + "object": "collection0", + "name": "dropIndex", + "arguments": { + "name": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "createIndexes": "coll", + "indexes": [ + { + "name": "x_1", + "key": { + "x": 1 + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "dropIndexes": "coll", + "index": "x_1", + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "MapReduce omits default write concern", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "mapReduce", + "object": "collection0", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + }, + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/read-write-concern/tests/operation/default-write-concern-3.4.yml b/source/read-write-concern/tests/operation/default-write-concern-3.4.yml new file mode 100644 index 0000000000..7596208868 --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-3.4.yml @@ -0,0 +1,144 @@ +# Test that setting a default write concern does not add a write concern to the command sent over the wire. +# Test operations that require 3.4+ server, where all commands started to accept a write concern. + +description: default-write-concern-3.4 + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "3.4" + +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name default-write-concern-tests + databaseOptions: + writeConcern: {} + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + collectionOptions: + writeConcern: {} + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: Aggregate with $out omits default write concern + # Serverless does not support $out stage + runOnRequirements: + - serverless: forbid + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: &out_pipeline + - $match: { _id: { $gt: 1 } } + - $out: &other_collection_name "other_collection_name" + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *out_pipeline + writeConcern: { $$exists: false } + outcome: + - + collectionName: *other_collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: RunCommand with a write command omits default write concern (runCommand should never inherit write concern) + operations: + - + object: *database0 + name: runCommand + arguments: + command: + delete: *collection_name + deletes: [ { q: {}, limit: 1 } ] + commandName: delete + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: [ { q: {}, limit: 1 } ] + writeConcern: { $$exists: false } + - + description: CreateIndex and dropIndex omits default write concern + operations: + - + object: *collection0 + name: createIndex + arguments: + keys: { x: 1 } + - + object: *collection0 + name: dropIndex + arguments: + name: x_1 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + createIndexes: *collection_name + indexes: [ { name: "x_1", key: { x: 1 } } ] + writeConcern: { $$exists: false } + - + commandStartedEvent: + command: + dropIndexes: *collection_name + index: x_1 + writeConcern: { $$exists: false } + - + description: MapReduce omits default write concern + # Serverless does not support mapReduce operation + runOnRequirements: + - serverless: forbid + operations: + - + name: mapReduce + object: *collection0 + arguments: + map: { $code: "function inc() { return emit(0, this.x + 1) }" } + reduce: { $code: "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" } + out: { inline: 1 } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + mapReduce: *collection_name + map: { $code: "function inc() { return emit(0, this.x + 1) }" } + reduce: { $code: "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" } + out: { inline: 1 } + writeConcern: { $$exists: false } diff --git a/source/read-write-concern/tests/operation/default-write-concern-4.2.json b/source/read-write-concern/tests/operation/default-write-concern-4.2.json new file mode 100644 index 0000000000..e8bb78d91d --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-4.2.json @@ -0,0 +1,125 @@ +{ + "description": "default-write-concern-4.2", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.2" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "default-write-concern-tests", + "databaseOptions": { + "writeConcern": {} + } + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll", + "collectionOptions": { + "writeConcern": {} + } + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $merge omits default write concern", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_collection_name" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "other_collection_name" + } + } + ], + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "other_collection_name", + "databaseName": "default-write-concern-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/read-write-concern/tests/operation/default-write-concern-4.2.yml b/source/read-write-concern/tests/operation/default-write-concern-4.2.yml new file mode 100644 index 0000000000..3ce742e655 --- /dev/null +++ b/source/read-write-concern/tests/operation/default-write-concern-4.2.yml @@ -0,0 +1,66 @@ +# Test that setting a default write concern does not add a write concern to the command sent over the wire. +# Test operations that require 4.2+ server. + +description: default-write-concern-4.2 + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.2" + +createEntities: + - + client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name default-write-concern-tests + databaseOptions: + writeConcern: {} + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + collectionOptions: + writeConcern: {} + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: Aggregate with $merge omits default write concern + operations: + - + object: *collection0 + name: aggregate + arguments: + pipeline: &merge_pipeline + - $match: { _id: { $gt: 1 } } + - $merge: { into: &other_collection_name "other_collection_name" } + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *merge_pipeline + writeConcern: { $$exists: false } + outcome: + - + collectionName: *other_collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } diff --git a/source/requirements.txt b/source/requirements.txt new file mode 100644 index 0000000000..ec8303d15a --- /dev/null +++ b/source/requirements.txt @@ -0,0 +1,4 @@ +mkdocs +mkdocs-github-admonitions-plugin +mkdocs-htmlproofer-plugin +pymdown-extensions==10.11.2 \ No newline at end of file diff --git a/source/retryable-reads/retryable-reads.md b/source/retryable-reads/retryable-reads.md new file mode 100644 index 0000000000..61f7ed34ce --- /dev/null +++ b/source/retryable-reads/retryable-reads.md @@ -0,0 +1,621 @@ +# Retryable Reads + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +This specification is about the ability for drivers to automatically retry any read operation that has not yet received +any results—due to a transient network error, a "not writable primary" error after a replica set failover, etc. + +This specification will + +- outline how an API for retryable read operations will be implemented in drivers +- define an option to enable retryable reads for an application. + +The changes in this specification are related to but distinct from the retryability behaviors defined in the +[Client Backpressure Specification](../client-backpressure/client-backpressure.md), which defines a retryability +mechanism for all commands under certain server conditions. Unless otherwise noted, the changes in this specification +refer only to the retryability behaviors summarized above. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### Retryable Error + +An error is considered retryable if it meets any of the criteria defined under +[Retryable Writes: Terms: Retryable Error](../retryable-writes/retryable-writes.md#terms), minus the final criterion +about write concern errors. For convenience, the relevant criteria have been adapted to retryable reads and reproduced +below. + +An error is considered retryable if it meets any of the following criteria: + +- any network exception (e.g. socket timeout or error) +- a server error response with any the following codes: + +| **Error Name** | **Error Code** | +| ---------------------------------- | -------------- | +| ExceededTimeLimit | 262 | +| InterruptedAtShutdown | 11600 | +| InterruptedDueToReplStateChange | 11602 | +| NotWritablePrimary | 10107 | +| NotPrimaryNoSecondaryOk | 13435 | +| NotPrimaryOrSecondary | 13436 | +| PrimarySteppedDown | 189 | +| ReadConcernMajorityNotAvailableYet | 134 | +| ShutdownInProgress | 91 | +| HostNotFound | 7 | +| HostUnreachable | 6 | +| NetworkTimeout | 89 | +| SocketException | 9001 | + +- a [PoolClearedError](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-errors) +- Any of the above retryable errors that occur during a connection handshake (including the authentication step). For + example, a network error or ShutdownInProgress error encountered when running the hello or saslContinue commands. + +### MongoClient Configuration + +This specification introduces the following client-level configuration option. + +#### retryReads + +This boolean option determines whether retryable behavior will be applied to all read operations executed within the +MongoClient. This option MUST default to true. +[As with retryable writes](../retryable-writes/retryable-writes.md#retrywrites), this option MUST NOT be configurable at +the level of an individual read operation, collection object, or database object. Drivers that expose a "high" and +"core" API (e.g. Java and C# driver) MUST NOT expose a configurable option at the level of an individual read operation, +collection object, or database object in "high", but MAY expose the option in "core." + +##### Naming Deviations + +[As with retryable writes](../retryable-writes/retryable-writes.md#retrywrites), drivers MUST use the defined name of +`retryReads` for the connection string parameter to ensure portability of connection strings across applications and +drivers. If drivers solicit MongoClient options through another mechanism (e.g. an options dictionary provided to the +MongoClient constructor), drivers SHOULD use the defined name but MAY deviate to comply with their existing conventions. +For example, a driver may use `retry_reads` instead of `retryReads`. For any other names in the spec, drivers SHOULD use +the defined name but MAY deviate to comply with their existing conventions. + +### Requirements for Retryable Reads + +#### Supported Server Versions + +Drivers MUST verify server eligibility by ensuring that `maxWireVersion` is at least 6 because retryable reads require a +MongoDB 3.6 standalone, replica set or shard cluster, MongoDB 3.6 server wire version is 6 as defined in the +[Server Wire version and Feature List specification](../wireversion-featurelist/wireversion-featurelist.md). + +The minimum server version is 3.6 because + +1. It gives us version parity with retryable writes. +2. It forces the retry attempt(s) to use the same implicit session, which would make it it easier to track operations + and kill any errant longer running operation. +3. It limits the scope of the implementation (`OP_QUERY` will not need to be supported). + +#### Supported Read Operations + +Drivers MUST support retryability for the following operations: + +- All read operations defined in the CRUD specification i.e. + + - `Collection.find()` + + - This includes the `find` operations backing the GridFS API. + + - `Collection.aggregate()` + + - Only if the pipeline does not include a write stage (e.g. `$out`, `$merge`) + + - `Collection.distinct()` + + - `Collection.count()` + + - Only required if the driver already provides `count()` + + - `Collection.estimatedDocumentCount()` + + - `Collection.countDocuments()` + +- All read operation helpers in the change streams specification i.e. + + - `Collection.watch()` + - `Database.watch()` + - `MongoClient.watch()` + +- All enumeration commands e.g. + + - `MongoClient.listDatabases()` + - `Database.listCollections()` + - `Collection.listIndexes()` + +- Any read operations not defined in the aforementioned specifications: + + - Any read operation helpers e.g. `Collection.findOne()` + +Drivers SHOULD support retryability for the following operations: + +- Any driver that provides generic command runners for read commands (with logic to inherit a client-level read + concerns) SHOULD implement retryability for the read-only command runner. + +Most of the above methods are defined in the following specifications: + +- [Change Streams](../change-streams/change-streams.md) +- [CRUD](../crud/crud.md) +- [Enumerating Collections](../enumerate-collections/enumerate-collections.md) +- [Enumerating Indexes](../index-management/index-management.md#enumerate-indexes) +- [Enumerating Databases](../enumerate-databases/enumerate-databases.md) +- [GridFS Spec](../gridfs/gridfs-spec.md) + +#### Unsupported Read Operations + +Drivers MUST NOT retry the following operations: + +- `Collection.mapReduce()` + - This is due to the "Early Failure on Socket Disconnect" feature not supporting `mapReduce`. + - N.B. If `mapReduce` is executed via a generic command runner for read commands, drivers SHOULD NOT inspect the + command to prevent `mapReduce` from retrying. +- Cursor.getMore() + - See [Why is retrying Cursor.getMore() not supported?](#why-is-retrying-cursorgetmore-not-supported) +- The generic runCommand helper, even if it is passed a read command. + - N.B.: This applies only to a generic command runner, which is agnostic about the read/write nature of the command. + +### Implementing Retryable Reads + +#### Executing Retryable Read Commands + +Executing retryable read commands is extremely similar to +[executing retryable write commands](../retryable-writes/retryable-writes.md#executing-retryable-write-commands). The +following explanation for executing retryable read commands has been adapted from the explanation for executing +retryable write commands. + +##### 1. Selecting the initial server + +The driver selects the initial server for the command as usual. When selecting a server for the first attempt of a +retryable read command, drivers MUST allow a server selection error to propagate. In this case, the caller is able to +infer that no attempt was made. + +##### 2. Determining whether retry should be allowed + +A driver then determines if it should attempt to retry next. + +###### 2a. When not to allow retry + +Drivers MUST attempt to execute the read command exactly once and allow any errors to propagate under any of the the +following conditions: + +- if retryable reads is not enabled **or** +- if the selected server does not support retryable reads **or** +- if the session in a transaction + +By allowing the error to propagate, the caller is able to infer that one attempt was made. + +###### 2b. When to allow retry + +Drivers MUST only attempt to retry a read command if + +- retryable reads are enabled **and** +- the selected server supports retryable reads **and** +- the previous attempt yields a retryable error + +##### 3. Deciding to allow retry, encountering the initial retryable error, and selecting a server + +If the driver decides to allow retry and the previous attempt of a retryable read command encounters a retryable error, +the driver MUST update its topology according to the Server Discovery and Monitoring spec (see +[SDAM: Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)) and +capture this original retryable error. Drivers should then proceed with selecting a server for a retry attempt. + +###### 3a. Selecting the server for retry + +For sharded clusters, the server address on which the operation failed MUST be provided to the server selection +mechanism as a member of the deprioritized server address list. + +For all other topologies, the server address on which the operation failed MUST be provided to the server selection +mechanism as a member of the deprioritized server address list only if the error is labelled with +`SystemOverloadedError`. All other retryable errors MUST NOT cause the server address to be added to the deprioritized +server address list. This requirement preserves the existing behavior of retryable reads for non-overload errors and +avoids unintended consequences for operations utilizing primaryPreferred and secondaryPreferred read preferences. + +If the driver cannot select a server for a retry attempt or the newly selected server does not support retryable reads, +retrying is not possible and drivers MUST raise the previous retryable error. In both cases, the caller is able to infer +that an attempt was made. + +###### 3b. Sending an equivalent command for a retry attempt + +After server selection, a driver MUST send a valid command to the newly selected server that is equivalent[^1] to the +initial command sent to the first server. If the driver determines that the newly selected server may not be able to +support a command equivalent to the initial command, drivers MUST NOT retry and MUST raise the previous retryable error + +The above requirement can be fulfilled in one of two ways: + +1. During a retry attempt, the driver SHOULD recreate the command while adhering to that operation's specification's + server/wire version requirements. If an error occurs while recreating the command, then the driver MUST raise the + original retryable error. + + For example, if the wire version dips from *W*0 to *W*1 after server selection, and the spec + for operation *O* notes that for wire version *W*1, that field *F* should be omitted, then field *F* + should be omitted. If the spec for operation *O* requires the driver to error out if field *F* is defined when + talking to a server with wire version *W*1, then the driver must error out and raise the original + retryable error. + +2. Alternatively, if a driver chooses not to recreate the command as described above, then a driver MUST NOT retry if + the server/wire version dips after server selection and MUST raise the original retryable error. + + For example, if the wire version dips after server selection, the driver can choose to not retry and simply raise the + original retryable error because there is no guarantee that the lower versioned server can support the original + command. + +###### 3c. If a retry attempt fails + +If a retry attempt also fails and +[Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) (CSOT) is enabled +and the timeout has not yet expired, then the Driver MUST jump back to step 2b above in order to allow multiple retry +attempts. + +Otherwise, drivers MUST update their topology according to the SDAM spec (see +[SDAM: Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)). If an +error would not allow the caller to infer that an attempt was made (e.g. connection pool exception originating from the +driver), the previous error should be raised. If a retry failed due to another retryable error or some other error +originating from the server, that error should be raised instead as the caller can infer that an attempt was made and +the second error is likely more relevant (with respect to the current topology state). + +If a driver associates server information (e.g. the server address or description) with an error, the driver MUST ensure +that the reported server information corresponds to the server that originated the error. + +##### 4. Implementation constraints + +When retrying a read command, drivers MUST NOT resend the original wire protocol message (see: +[Can drivers resend the same wire protocol message on retry attempts?](#can-drivers-resend-the-same-wire-protocol-message-on-retry-attempts)). + +#### Pseudocode + +The following pseudocode for executing retryable read commands has been adapted from +[the pseudocode for executing retryable write commands](../retryable-writes/retryable-writes.md#executing-retryable-write-commands) +and reflects the flow described above. + +> [!NOTE] +> The rules above and the pseudocode below only demonstrate the rules for retryable reads as outlined in this +> specification. For simplicity, and to make the retryable reads rules easier to follow, the pseudocode was +> intentionally unmodified. For a pseudocode block that contains both retryable reads logic as defined in this +> specification and backoff retryabilitity as defined in the client backpressure specification, see the pseudocode in +> the [Backpressure Specification](../client-backpressure/client-backpressure.md). + +```typescript +/** + * Checks if a connection supports retryable reads. + */ +function isRetryableReadsSupported(connection) { + return connection.MaxWireVersion >= RETRYABLE_READS_MIN_WIRE_VERSION); +} + +/** + * Executes a read command in the context of a MongoClient where a retryable + * read have been enabled. The session parameter may be an implicit or + * explicit client session (depending on how the CRUD method was invoked). + */ +function executeRetryableRead(command, session) { + Exception previousError = null; + retrying = false; + Server previousServer = null; + deprioritizedServers = []; + while true { + if (previousError != null) { + retrying = true; + } + try { + if (previousServer == null) { + server = selectServer(); + } else { + // If a previous attempt was made, deprioritize the previous server address + // where the command failed. + // Sharded clusters deprioritize on all retryable errors. + // Other topologies only deprioritize on overload errors. + if previousServer.isSharded || previousError.hasLabel("SystemOverloadedError") { + deprioritizedServers.push(previousServer.address); + } + server = selectServer(deprioritizedServers); + } + } catch (ServerSelectionException exception) { + if (previousError == null) { + // If this is the first attempt, propagate the exception. + throw exception; + } + // For retries, propagate the previous error. + throw previousError; + } + + try { + connection = server.getConnection(); + } catch (PoolClearedException poolClearedError) { + /* PoolClearedException indicates the operation did not even attempt to + * create a connection, let alone execute the operation. This means we + * are always safe to attempt a retry. We do not need to update SDAM, + * since whatever error caused the pool to be cleared will do so itself. */ + if (previousError == null) { + previousError = poolClearedError; + } + /* CSOT is enabled and the operation has timed out. */ + if (timeoutMS != null && isExpired(timeoutMS) { + throw previousError; + } + /* CSOT is not enabled, and we have already tried once */ + if (timeoutMS == null && retrying) { + throw previousError; + } + continue; + } + + if ( !isRetryableReadsSupported(connection) || session.inTransaction()) { + /* If this is the first loop iteration and we determine that retryable + * reads are not supported, execute the command once and allow any + * errors to propagate */ + + if (previousError == null) { + return executeCommand(connection, command); + } + + /* If the server selected for retrying is too old, throw the previous error. + * The caller can then infer that an attempt was made and failed. This case + * is very rare, and likely means that the cluster is in the midst of a + * downgrade. */ + throw previousError; + } + + /* NetworkException and NotWritablePrimaryException are both retryable errors. If + * caught, remember the exception, update SDAM accordingly, and proceed with + * retrying the operation. + * + * Exceptions that originate from the driver (e.g. no socket available + * from the connection pool) are treated as fatal. Any such exception + * that occurs on the previous attempt is propagated as-is. On retries, + * the error from the previous attempt is raised as it will be more + * relevant for the user. */ + try { + return executeCommand(connection, retryableCommand); + } catch (NetworkException networkError) { + updateTopologyDescriptionForNetworkError(server, networkError); + previousError = networkError; + previousServer = server; + } catch (NotWritablePrimaryException notPrimaryError) { + updateTopologyDescriptionForNotWritablePrimaryError(server, notPrimaryError); + previousError = notPrimaryError; + previousServer = server; + } catch (Exception error when error.code in RETRYABLE_ERROR_CODES) { + /* Catches remaining server errors with retryable error codes as defined + * in the Retryable Error section. */ + updateTopologyDescriptionForError(server, error); + previousError = error; + previousServer = server; + } catch (DriverException error) { + if ( previousError != null ) { + throw previousError; + } + throw error; + } + + if (timeoutMS == null) { + /* If CSOT is not enabled, allow any retryable error from the second + * attempt to propagate to our caller, as it will be just as relevant + * (if not more relevant) than the original error. */ + if (retrying) { + throw previousError; + } + } else if (isExpired(timeoutMS)) { + /* CSOT is enabled and the operation has timed out. */ + throw previousError; + } + } +} +``` + +### Logging Retry Attempts + +[As with retryable writes](../retryable-writes/retryable-writes.md#logging-retry-attempts), drivers MAY choose to log +retry attempts for read operations. This specification does not define a format for such log messages. + +### Command Monitoring + +[As with retryable writes](../retryable-writes/retryable-writes.md#command-monitoring), in accordance with the +[Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) specification, +drivers MUST guarantee that each `CommandStartedEvent` has either a correlating `CommandSucceededEvent` or +`CommandFailedEvent` and that every "command started" log message has either a correlating "command succeeded" log +message or "command failed" log message. If the first attempt of a retryable read operation encounters a retryable +error, drivers MUST fire a `CommandFailedEvent` and emit a "command failed" log message for the retryable error and fire +a separate `CommandStartedEvent` and emit a separate "command started" log message when executing the subsequent retry +attempt. Note that the second `CommandStartedEvent` and "command started" log message may have a different +`connectionId`, since a server is reselected for a retry attempt. + +### Documentation + +1. Drivers MUST document all read operations that support retryable behavior. +2. Drivers MUST document that the operations in [Unsupported Read Operations](#unsupported-read-operations) do not + support retryable behavior. +3. Driver release notes MUST make it clear to users that they may need to adjust custom retry logic to prevent an + application from inadvertently retrying for too long (see [Backwards Compatibility](#backwards-compatibility) for + details). +4. Drivers implementing retryability for their generic command runner for read commands MUST document that `mapReduce` + will be retried if it is passed as a command to the command runner. These drivers also MUST document the potential + for degraded performance given that "Early Failure on Socket Disconnect" feature does not support `mapReduce`. + +## Test Plan + +See the [README](./tests/README.md) for tests. + +At a high level, the test plan will cover executing supported read operations within a MongoClient where retryable reads +have been enabled, ensuring that reads are retried. + +## Motivation for Change + +Drivers currently have an API for the retryability of write operations but not for read operations. The driver API needs +to be extended to include support for retryable behavior for read operations. + +## Design Rationale + +The design of this specification is based off the +[Retryable Writes specification](../retryable-writes/retryable-writes.md#design-rationale). It modifies the driver API +as little as possible to introduce the concept retryable behavior for read operations. + +Alternative retry strategies (e.g. exponential back-off, incremental intervals, regular intervals, immediate retry, +randomization) were considered, but the behavior of a single, immediate retry attempt was chosen in the interests of +simplicity as well as consistency with the design for retryable writes. + +See the [future work](#future-work) section for potential upcoming changes to retry mechanics. + +## Backwards Compatibility + +The API changes to support retryable reads extend the existing API but do not introduce any backward breaking changes. +Existing programs that do not make use of retryable reads will continue to compile and run correctly. + +N.B.: Applications with custom retry logic that choose to enable retryable reads may need to redo their custom retry +logic to ensure that the reads are retried as desired. e.g. if an application has custom logic that retries reads n +times and enables retryable reads, then the application could end up retrying reads up to 2n times. + +The note above will also apply if an application upgrades to a version of the driver where that defaults to enabling +retryable reads. + +### Rejected Designs + +1. To improve performance on servers without "Early Failure on Socket Disconnect", we considered using `killSessions` to + automatically kill the previous attempt before running a retry. We decided against this because after killing the + session, parts of it still may be running if there are any errors. Additionally, killing sessions takes time + because a kill has to talk to every non-config `mongod` in the cluster (i.e. all the primaries and secondaries of + each shard). In addition, in order to protect the system against getting overloaded with these requests, every + server allows no more than one killsession operation at a time. Operations that attempt to `killsessions` while a + killsession is running are batched together and run simultaneously after the current one finishes. + +## Reference Implementation + +The C# and Python drivers will provide the reference implementations. See +[CSHARP-2429](https://jira.mongodb.org/browse/CSHARP-2429) and +[PYTHON-1674](https://jira.mongodb.org/browse/PYTHON-1674). + +## Security Implications + +None. + +## Future work + +1. A later specification may allow operations (including read) to be retried any number of times during a singular + timeout period. +2. Any future changes to the the applicable parts of + [retryable writes specification](../retryable-writes/retryable-writes.md) may also need to be reflected in the + retryable reads specification, and vice versa. +3. We may revisit the decision not retry `Cursor.getMore()` (see [Q&A](#qa)). +4. Once [DRIVERS-560](https://jira.mongodb.org/browse/DRIVERS-560) is resolved, tests will be added to allow testing + Retryable Reads on MongoDB 3.6. See the [test plan](./tests/README.md) for additional information. + +## Q&A + +### Why is retrying `Cursor.getMore()` not supported? + +`Cursor.getMore()` cannot be retried because of the inability for the client to discern if the cursor was advanced. In +other words, since the driver does not know if the original `getMore()` succeeded or not, the driver cannot reliably +know if results might be inadvertently skipped. + +For example, if a transient network error occurs as a driver requests the second batch of results via a getMore() and +the driver were to silently retry the `getMore()`, it is possible that the server had actually received the initial +`getMore()`. In such a case, the server will advance the cursor once more and return the third batch instead of the +desired second batch. + +Furthermore, even if the driver could detect such a scenario, it is impossible to return previously iterated data from a +cursor because the server currently only allows forward iteration. + +It is worth noting that the "Cursors survive primary stepdown" feature avoids this issue in certain common +circumstances, so that we may revisit this decision to disallow trying `getMore()` in the future. + +### Why are read operations only retried once by default? + +[Read operations are only retried once for the same reasons that writes are also only retried once.](../retryable-writes/retryable-writes.md#why-are-write-operations-only-retried-once-by-default) +For convenience's sake, that reasoning has been adapted for reads and reproduced below: + +The spec concerns itself with retrying read operations that encounter a retryable error (i.e. no response due to network +error or a response indicating that the node is no longer a primary). A retryable error may be classified as either a +transient error (e.g. dropped connection, replica set failover) or persistent outage. If a transient error results in +the server being marked as "unknown", a subsequent retry attempt will allow the driver to rediscover the primary within +the designated server selection timeout period (30 seconds by default). If server selection times out during this retry +attempt, we can reasonably assume that there is a persistent outage. In the case of a persistent outage, multiple retry +attempts are fruitless and would waste time. See +[How To Write Resilient MongoDB Applications](https://emptysqua.re/blog/how-to-write-resilient-mongodb-applications/) +for additional discussion on this strategy. + +However when [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) is +enabled, the driver will retry multiple times until the operation succeeds, a non-retryable error is encountered, or the +timeout expires. Retrying multiple times provides greater resilience to cascading failures such as rolling server +restarts during planned maintenance events. + +### Can drivers resend the same wire protocol message on retry attempts? + +No. +[This is in contrast to the answer supplied in in the retryable writes specification.](../retryable-writes/retryable-writes.md#can-drivers-resend-the-same-wire-protocol-message-on-retry-attempts) +However, when retryable writes were implemented, no driver actually chose to resend the same wire protocol message. +Today, if a driver attempted to resend the same wire protocol message, this could violate +[the rules for gossiping $clusterTime](../sessions/driver-sessions.md#gossipping-the-cluster-time): specifically +[the rule that a driver must send the highest seen $clusterTime](../sessions/driver-sessions.md#sending-the-highest-seen-cluster-time). + +Additionally, there would be a behavioral difference between a driver resending the same wire protocol message and one +that does not. For example, a driver that creates a new wire protocol message could exhibit the following +characteristics: + +1. The second attempt to send the read command could have a higher `$clusterTime`. +2. If the initial attempt failed with a server error, then the session's `operationTime` would be advanced and the next + read would include a larger `readConcern.afterClusterTime`. + +A driver that resends the same wire protocol message would not exhibit the above characteristics. Thus, in order to +avoid this behavioral difference and not violate the rules about gossiping `$clusterTime`, drivers MUST not resend the +same wire protocol message. + +### Why isn't MongoDB 4.2 required? + +MongoDB 4.2 was initially considered as a requirement for retryable reads because MongoDB 4.2 implements support for +"Early Failure on Socket Disconnect," changing the the semantics of socket disconnect to prevent ops from doing work +that no client is interested in. This prevents applications from seeing degraded performance when an expensive read is +retried. Upon further discussion, we decided that "Early Failure on Socket Disconnect" should not be required to retry +reads because the resilience benefit of retryable reads outweighs the minor risk of degraded performance. Additionally, +any customers experiencing degraded performance can simply disable `retryableReads`. + +## Changelog + +- 2026-02-19: Clarified that server deprioritization on replica sets only occurs for `SystemOverloadedError` errors. + +- 2026-02-11: Clarified that the retry logic and pseudocode does not include the modifications required by client + backpressure. + +- 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses. + +- 2024-04-30: Migrated from reStructuredText to Markdown. + +- 2023-12-05: Add that any server information associated with retryable exceptions MUST reflect the originating server, + even in the presence of retries. + +- 2023-11-30: Add ReadConcernMajorityNotAvailableYet to the list of error codes that should be retried. + +- 2023-11-28: Add ExceededTimeLimit to the list of error codes that should be retried. + +- 2023-08-26: Require that in a sharded cluster the server on which the operation failed MUST be provided to the server + selection mechanism as a deprioritized server. + +- 2023-08-21: Update Q&A that contradicts SDAM transient error logic + +- 2022-11-09: CLAM must apply both events and log messages. + +- 2022-10-18: When CSOT is enabled multiple retry attempts may occur. + +- 2022-10-05: Remove spec front matter, move footnote, and reformat changelog. + +- 2022-01-25: Note that drivers should retry handshake network failures. + +- 2021-04-26: Replaced deprecated terminology; removed requirement to parse error message text as MongoDB 3.6+ servers + will always return an error code + +- 2021-03-23: Require that PoolClearedErrors are retried + +- 2019-06-07: Mention $merge stage for aggregate alongside $out + +- 2019-05-29: Renamed InterruptedDueToStepDown to InterruptedDueToReplStateChange + +[^1]: The first and second commands will be identical unless variations in parameters exist between wire/server versions. diff --git a/source/retryable-reads/retryable-reads.rst b/source/retryable-reads/retryable-reads.rst new file mode 100644 index 0000000000..5cbbaca95e --- /dev/null +++ b/source/retryable-reads/retryable-reads.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `retryable-reads.md `_. diff --git a/source/retryable-reads/tests/README.md b/source/retryable-reads/tests/README.md new file mode 100644 index 0000000000..2296bab37f --- /dev/null +++ b/source/retryable-reads/tests/README.md @@ -0,0 +1,356 @@ +# Retryable Reads Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +retryable reads. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +## Prose Tests + +### 1. PoolClearedError Retryability Test + +This test will be used to ensure drivers properly retry after encountering PoolClearedErrors. It MUST be implemented by +any driver that implements the CMAP specification. This test requires MongoDB 4.2.9+ for `blockConnection` support in +the failpoint. + +1. Create a client with maxPoolSize=1 and retryReads=true. If testing against a sharded deployment, be sure to connect + to only a single mongos. + +2. Enable the following failpoint: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorCode: 91, + blockConnection: true, + blockTimeMS: 1000 + } + } + ``` + +3. Start two threads and attempt to perform a `findOne` simultaneously on both. + +4. Verify that both `findOne` attempts succeed. + +5. Via CMAP monitoring, assert that the first check out succeeds. + +6. Via CMAP monitoring, assert that a PoolClearedEvent is then emitted. + +7. Via CMAP monitoring, assert that the second check out then fails due to a connection error. + +8. Via Command Monitoring, assert that exactly three `find` CommandStartedEvents were observed in total. + +9. Disable the failpoint. + +### 2. Retrying Reads in a Sharded Cluster + +These tests will be used to ensure drivers properly retry reads on a different mongos. + +Note: this test cannot reliably distinguish "retry on a different mongos due to server deprioritization" (the behavior +intended to be tested) from "retry on a different mongos due to normal SDAM behavior of randomized suitable server +selection". Verify relevant code paths are correctly executed by the tests using external means such as a logging, +debugger, code coverage tool, etc. + +#### 2.1 Retryable Reads Are Retried on a Different mongos When One is Available + +This test MUST be executed against a sharded cluster that has at least two mongos instances, supports `retryReads=true`, +and has enabled the `configureFailPoint` command (MongoDB 4.2+). + +1. Create two clients `s0` and `s1` that each connect to a single mongos from the sharded cluster. They must not connect + to the same mongos. + +2. Configure the following fail point for both `s0` and `s1`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorCode: 6 + } + } + ``` + +3. Create a client `client` with `retryReads=true` that connects to the cluster using the same two mongoses as `s0` and + `s1`. + +4. Enable failed command event monitoring for `client`. + +5. Execute a `find` command with `client`. Assert that the command failed. + +6. Assert that two failed command events occurred. Assert that both events occurred on different mongoses. + +7. Disable the fail point on both `s0` and `s1`. + +#### 2.2 Retryable Reads Are Retried on the Same mongos When No Others are Available + +This test MUST be executed against a sharded cluster that supports `retryReads=true` and has enabled the +`configureFailPoint` command (MongoDB 4.2+). + +1. Create a client `s0` that connects to a single mongos from the cluster. + +2. Configure the following fail point for `s0`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorCode: 6 + } + } + ``` + +3. Create a client `client` with `directConnection=false` (when not set by default) and `retryReads=true` that connects + to the cluster using the same single mongos as `s0`. + +4. Enable succeeded and failed command event monitoring for `client`. + +5. Execute a `find` command with `client`. Assert that the command succeeded. + +6. Assert that exactly one failed command event and one succeeded command event occurred. Assert that both events + occurred on the same mongos. + +7. Disable the fail point on `s0`. + +### 3. Retrying Reads in a Replica Set + +These tests will be used to ensure drivers properly retry reads against a replica set. + +#### 3.1 Retryable Reads Caused by Overload Errors Are Retried on a Different Replicaset Server When One is Available and enableOverloadRetargeting is enabled + +This test MUST be executed against a MongoDB 4.4+ replica set that has at least one secondary, supports +`retryReads=true`, and has enabled the `configureFailPoint` command with the `errorLabels` option. + +1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, `enableOverloadRetargeting=True`, + and command event monitoring enabled. + +2. Configure the following fail point for `client`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError", "SystemOverloadedError"] + errorCode: 6 + } + } + ``` + +3. Reset the command event monitor to clear the failpoint command from its stored events. + +4. Execute a `find` command with `client`. + +5. Assert that one failed command event and one successful command event occurred. + +6. Assert that both events occurred on different servers. + +#### 3.2 Retryable Reads Caused by Non-Overload Errors Are Retried on the Same Replicaset Server + +This test MUST be executed against a MongoDB 4.4+ replica set that has at least one secondary, supports +`retryReads=true`, and has enabled the `configureFailPoint` command with the `errorLabels` option. + +1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring + enabled. + +2. Configure the following fail point for `client`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError"] + errorCode: 6 + } + } + ``` + +3. Reset the command event monitor to clear the failpoint command from its stored events. + +4. Execute a `find` command with `client`. + +5. Assert that one failed command event and one successful command event occurred. + +6. Assert that both events occurred on the same server. + +#### 3.3 Retryable Reads Caused by Overload Errors Are Retried on Same Replicaset Server When enableOverloadRetargeting is disabled + +This test MUST be executed against a MongoDB 4.4+ replica set that has at least one secondary, supports +`retryReads=true`, and has enabled the `configureFailPoint` command with the `errorLabels` option. + +1. Create a client `client` with `retryReads=true`, `readPreference=primaryPreferred`, and command event monitoring + enabled. + +2. Configure the following fail point for `client`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError", "SystemOverloadedError"] + errorCode: 6 + } + } + ``` + +3. Reset the command event monitor to clear the failpoint command from its stored events. + +4. Execute a `find` command with `client`. + +5. Assert that one failed command event and one successful command event occurred. + +6. Assert that both events occurred on the same server. + +### 4: Test that drivers set the maximum number of retries for all retryable read errors when an overload error is encountered + +This test MUST be executed against a MongoDB 4.4+ server that supports `retryReads=true` and has enabled the +`configureFailPoint` command with the `errorLabels` option. + +1. Create a client. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `91` (ShutdownInProgress) and + the `RetryableError` label: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["find"], + errorLabels: ["RetryableError"], + errorCode: 91 + } + } + ``` + + Configure the second fail point command only if the failed event is for the first error configured in step 2. + +4. Attempt a `findOne` operation on any record for any database and collection. Expect the `findOne` to fail with a + server error. Assert that `MAX_RETRIES + 1` attempts were made. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +### 5: Test that drivers do not apply backoff to non-overload errors + +This test MUST be executed against a MongoDB 4.4+ server that supports `retryReads=true` and has enabled the +`configureFailPoint` command with the `errorLabels` option. + +1. Create a client. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["find"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `91` (ShutdownInProgress) and + the `RetryableError` label: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["find"], + errorLabels: ["RetryableError"], + errorCode: 91 + } + } + ``` + + Configure the second fail point command only if the failed event is for the first error configured in step 2. + +4. Attempt a `findOne` operation on any record for any database and collection. Expect the `findOne` to fail with a + server error. Assert that backoff was applied only once for the initial overload error and not for the subsequent + non-overload retryable errors. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +## Changelog + +- 2026-04-14: Add prose tests for retry behavior when a mix of overload and non-overload errors are encountered. + +- 2026-03-31: Add additional prose test for overload retargeting. + +- 2026-02-19: Add prose tests for retrying against a replica set. + +- 2024-04-30: Migrated from reStructuredText to Markdown. + +- 2024-03-06: Convert legacy retryable reads tests to unified format. + +- 2024-02-21: Update mongos redirection prose tests to workaround SDAM behavior preventing execution of deprioritization + code paths. + +- 2023-08-26: Add prose tests for retrying in a sharded cluster. + +- 2022-04-22: Clarifications to `serverless` and `useMultipleMongoses`. + +- 2022-01-10: Create legacy and unified subdirectories for new unified tests + +- 2021-08-27: Clarify behavior of `useMultipleMongoses` for `LoadBalanced` topologies. + +- 2019-03-19: Add top-level `runOn` field to denote server version and/or topology requirements requirements for the + test file. Removes the `minServerVersion` and `topology` top-level fields, which are now expressed within `runOn` + elements. + + Add test-level `useMultipleMongoses` field. + +- 2020-09-16: Suggest lowering heartbeatFrequencyMS in addition to minHeartbeatFrequencyMS. + +- 2021-03-23: Add prose test for retrying PoolClearedErrors + +- 2021-04-29: Add `load-balanced` to test topology requirements. diff --git a/source/retryable-reads/tests/etc/templates/handshakeError.yml.template b/source/retryable-reads/tests/etc/templates/handshakeError.yml.template new file mode 100644 index 0000000000..f759e42df8 --- /dev/null +++ b/source/retryable-reads/tests/etc/templates/handshakeError.yml.template @@ -0,0 +1,159 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable reads handshake failures" + +# 1.4 is required for "serverless: forbid". +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-reads-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. +{% for operation in operations %} + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake network error" + {%- if ((operation.operation_name == 'createChangeStream') or + (operation.operation_name == 'aggregate' and operation.object == 'database')) %} + runOnRequirements: + - serverless: forbid + {%- endif %} + 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: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + {%- if operation.operation_name == "createChangeStream" %} + saveResultAsEntity: changeStream + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} + + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake server error (ShutdownInProgress)" + {%- if ((operation.operation_name == 'createChangeStream') or + (operation.operation_name == 'aggregate' and operation.object == 'database')) %} + runOnRequirements: + - serverless: forbid + {%- endif %} + 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: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + {%- if operation.operation_name == "createChangeStream" %} + saveResultAsEntity: changeStream + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} +{% endfor -%} diff --git a/source/retryable-reads/tests/unified/aggregate-merge.json b/source/retryable-reads/tests/unified/aggregate-merge.json new file mode 100644 index 0000000000..96bbd0fc38 --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate-merge.json @@ -0,0 +1,143 @@ +{ + "description": "aggregate-merge", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate with $merge does not retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$merge": { + "into": "output-collection" + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/aggregate-merge.yml b/source/retryable-reads/tests/unified/aggregate-merge.yml new file mode 100644 index 0000000000..0add82b667 --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate-merge.yml @@ -0,0 +1,86 @@ +description: aggregate-merge + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: 4.1.11 + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + +tests: + - + description: 'Aggregate with $merge does not retry' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - + object: *collection0 + name: aggregate + arguments: + pipeline: &pipeline + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + - + $merge: + into: output-collection + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: *pipeline + commandName: aggregate + databaseName: *database_name diff --git a/source/retryable-reads/tests/unified/aggregate-serverErrors.json b/source/retryable-reads/tests/unified/aggregate-serverErrors.json new file mode 100644 index 0000000000..d39835a5d3 --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate-serverErrors.json @@ -0,0 +1,1430 @@ +{ + "description": "aggregate-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/aggregate-serverErrors.yml b/source/retryable-reads/tests/unified/aggregate-serverErrors.yml new file mode 100644 index 0000000000..66a3f2cbbb --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate-serverErrors.yml @@ -0,0 +1,411 @@ +description: aggregate-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + +tests: + - + description: 'Aggregate succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: aggregate + arguments: + pipeline: + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + expectResult: + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - + $match: + _id: { $gt: 1 } + - + $sort: + x: 1 + databaseName: *database_name + - *retryable_command_started_event + - + description: 'Aggregate succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: aggregate + arguments: + pipeline: + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/aggregate.json b/source/retryable-reads/tests/unified/aggregate.json new file mode 100644 index 0000000000..2b504c8d49 --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate.json @@ -0,0 +1,527 @@ +{ + "description": "aggregate", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Aggregate with $out does not retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "output-collection" + } + ] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + }, + { + "$out": "output-collection" + } + ] + }, + "commandName": "aggregate", + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/aggregate.yml b/source/retryable-reads/tests/unified/aggregate.yml new file mode 100644 index 0000000000..fa25d1f992 --- /dev/null +++ b/source/retryable-reads/tests/unified/aggregate.yml @@ -0,0 +1,227 @@ +description: aggregate + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + +tests: + - + description: 'Aggregate succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: aggregate + arguments: + pipeline: + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + expectResult: + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - + $match: + _id: { $gt: 1 } + - + $sort: + x: 1 + databaseName: *database_name + - + description: 'Aggregate succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: aggregate + arguments: + pipeline: + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'Aggregate fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Aggregate with $out does not retry' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: *failCommand_failPoint + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - + $match: + _id: + $gt: 1 + - + $sort: + x: 1 + - + $out: output-collection + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - + $match: + _id: { $gt: 1 } + - + $sort: + x: 1 + - + $out: output-collection + commandName: aggregate + databaseName: *database_name diff --git a/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.json b/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.json new file mode 100644 index 0000000000..47375974d2 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.json @@ -0,0 +1,959 @@ +{ + "description": "changeStreams-client.watch-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + } + ], + "tests": [ + { + "description": "client.watch succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.yml b/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.yml new file mode 100644 index 0000000000..7b6b8bbdf9 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-client.watch-serverErrors.yml @@ -0,0 +1,359 @@ +description: changeStreams-client.watch-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + +tests: + - + description: 'client.watch succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11600 + - &retryable_operation + object: *client0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: 1 + cursor: { } + pipeline: + - + $changeStream: + allChangesForCluster: true + databaseName: admin + - *retryable_command_started_event + - + description: 'client.watch succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + errorCode: 10107 + - &retryable_operation_fails + object: *client0 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - <<: *retryable_operation_fails + object: *client1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/changeStreams-client.watch.json b/source/retryable-reads/tests/unified/changeStreams-client.watch.json new file mode 100644 index 0000000000..95ddaf921d --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-client.watch.json @@ -0,0 +1,294 @@ +{ + "description": "changeStreams-client.watch", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + } + ], + "tests": [ + { + "description": "client.watch succeeds on first attempt", + "operations": [ + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "client.watch fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true + } + } + ] + }, + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-client.watch.yml b/source/retryable-reads/tests/unified/changeStreams-client.watch.yml new file mode 100644 index 0000000000..2fa3a300bc --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-client.watch.yml @@ -0,0 +1,131 @@ +description: changeStreams-client.watch + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + +tests: + - + description: 'client.watch succeeds on first attempt' + operations: + - &retryable_operation + object: *client0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: 1 + cursor: { } + pipeline: + - + $changeStream: + allChangesForCluster: true + databaseName: admin + - + description: 'client.watch succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'client.watch fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *client1 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'client.watch fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + closeConnection: true + - <<: *retryable_operation_fails + object: *client0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.json b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.json new file mode 100644 index 0000000000..589d0a3c37 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.json @@ -0,0 +1,944 @@ +{ + "description": "changeStreams-db.coll.watch-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "db.coll.watch succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.yml b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.yml new file mode 100644 index 0000000000..8dc05560bc --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch-serverErrors.yml @@ -0,0 +1,382 @@ +description: changeStreams-db.coll.watch-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'db.coll.watch succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11600 + - &retryable_operation + object: *collection0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + cursor: { } + pipeline: + - + $changeStream: { } + databaseName: *database_name + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.json b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.json new file mode 100644 index 0000000000..bbea2ffe4f --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.json @@ -0,0 +1,314 @@ +{ + "description": "changeStreams-db.coll.watch", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "db.coll.watch succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.coll.watch fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.yml b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.yml new file mode 100644 index 0000000000..8106382283 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.coll.watch.yml @@ -0,0 +1,152 @@ +description: changeStreams-db.coll.watch + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'db.coll.watch succeeds on first attempt' + operations: + - &retryable_operation + object: *collection0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + cursor: { } + pipeline: + - + $changeStream: { } + databaseName: *database_name + - + description: 'db.coll.watch succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.coll.watch fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'db.coll.watch fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.json b/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.json new file mode 100644 index 0000000000..6c12d7ddd8 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.json @@ -0,0 +1,930 @@ +{ + "description": "changeStreams-db.watch-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "db.watch succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.yml b/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.yml new file mode 100644 index 0000000000..a90a802d91 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.watch-serverErrors.yml @@ -0,0 +1,373 @@ +description: changeStreams-db.watch-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'db.watch succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11600 + - &retryable_operation + object: *database0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: 1 + cursor: { } + pipeline: + - + $changeStream: { } + databaseName: *database_name + - *retryable_command_started_event + - + description: 'db.watch succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + errorCode: 10107 + - &retryable_operation_fails + object: *database0 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - <<: *retryable_operation_fails + object: *database1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/changeStreams-db.watch.json b/source/retryable-reads/tests/unified/changeStreams-db.watch.json new file mode 100644 index 0000000000..1b6d911c76 --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.watch.json @@ -0,0 +1,303 @@ +{ + "description": "changeStreams-db.watch", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "db.watch succeeds on first attempt", + "operations": [ + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "killCursors" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database1", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "db.watch fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "createChangeStream", + "arguments": { + "pipeline": [] + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": {} + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/changeStreams-db.watch.yml b/source/retryable-reads/tests/unified/changeStreams-db.watch.yml new file mode 100644 index 0000000000..47cbc0457d --- /dev/null +++ b/source/retryable-reads/tests/unified/changeStreams-db.watch.yml @@ -0,0 +1,145 @@ +description: changeStreams-db.watch + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.7 + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'db.watch succeeds on first attempt' + operations: + - &retryable_operation + object: *database0 + name: createChangeStream + arguments: + pipeline: [] + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: 1 + cursor: { } + pipeline: + - + $changeStream: { } + databaseName: *database_name + - + description: 'db.watch succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'db.watch fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + ignoreCommandMonitoringEvents: + - killCursors + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *database1 + name: createChangeStream + arguments: + pipeline: [] + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'db.watch fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + closeConnection: true + - <<: *retryable_operation_fails + object: *database0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/count-serverErrors.json b/source/retryable-reads/tests/unified/count-serverErrors.json new file mode 100644 index 0000000000..c52edfdb98 --- /dev/null +++ b/source/retryable-reads/tests/unified/count-serverErrors.json @@ -0,0 +1,808 @@ +{ + "description": "count-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Count succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "count", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/count-serverErrors.yml b/source/retryable-reads/tests/unified/count-serverErrors.yml new file mode 100644 index 0000000000..a433aaa87d --- /dev/null +++ b/source/retryable-reads/tests/unified/count-serverErrors.yml @@ -0,0 +1,381 @@ +description: count-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + +tests: + - + description: 'Count succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: count + arguments: + filter: { } + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + count: *collection_name + databaseName: *database_name + - *retryable_command_started_event + - + description: 'Count succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - count + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: count + arguments: + filter: { } + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/count.json b/source/retryable-reads/tests/unified/count.json new file mode 100644 index 0000000000..d5c9a343a9 --- /dev/null +++ b/source/retryable-reads/tests/unified/count.json @@ -0,0 +1,286 @@ +{ + "description": "count", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "Count succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "count", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Count fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/count.yml b/source/retryable-reads/tests/unified/count.yml new file mode 100644 index 0000000000..912f7578b4 --- /dev/null +++ b/source/retryable-reads/tests/unified/count.yml @@ -0,0 +1,153 @@ +description: count + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + +tests: + - + description: 'Count succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: count + arguments: + filter: { } + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + count: *collection_name + databaseName: *database_name + - + description: 'Count succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Count fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: count + arguments: + filter: { } + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'Count fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - count + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/countDocuments-serverErrors.json b/source/retryable-reads/tests/unified/countDocuments-serverErrors.json new file mode 100644 index 0000000000..fd028b114c --- /dev/null +++ b/source/retryable-reads/tests/unified/countDocuments-serverErrors.json @@ -0,0 +1,1133 @@ +{ + "description": "countDocuments-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "CountDocuments succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/countDocuments-serverErrors.yml b/source/retryable-reads/tests/unified/countDocuments-serverErrors.yml new file mode 100644 index 0000000000..2bcca604f2 --- /dev/null +++ b/source/retryable-reads/tests/unified/countDocuments-serverErrors.yml @@ -0,0 +1,386 @@ +description: countDocuments-serverErrors +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'CountDocuments succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: countDocuments + arguments: + filter: { } + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - + $match: { } + - + $group: + _id: 1 + 'n': { $sum: 1 } + databaseName: *database_name + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: countDocuments + arguments: + filter: { } + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/countDocuments.json b/source/retryable-reads/tests/unified/countDocuments.json new file mode 100644 index 0000000000..e06e89c1ad --- /dev/null +++ b/source/retryable-reads/tests/unified/countDocuments.json @@ -0,0 +1,364 @@ +{ + "description": "countDocuments", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "CountDocuments succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "CountDocuments fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": {} + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/countDocuments.yml b/source/retryable-reads/tests/unified/countDocuments.yml new file mode 100644 index 0000000000..725137deef --- /dev/null +++ b/source/retryable-reads/tests/unified/countDocuments.yml @@ -0,0 +1,158 @@ +description: countDocuments +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 +tests: + - + description: 'CountDocuments succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: countDocuments + arguments: + filter: { } + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - + $match: { } + - + $group: + _id: 1 + 'n': { $sum: 1 } + databaseName: *database_name + - + description: 'CountDocuments succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - aggregate + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'CountDocuments fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: countDocuments + arguments: + filter: { } + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'CountDocuments fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - aggregate + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/distinct-serverErrors.json b/source/retryable-reads/tests/unified/distinct-serverErrors.json new file mode 100644 index 0000000000..79d2d5fc31 --- /dev/null +++ b/source/retryable-reads/tests/unified/distinct-serverErrors.json @@ -0,0 +1,1060 @@ +{ + "description": "distinct-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Distinct succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/distinct-serverErrors.yml b/source/retryable-reads/tests/unified/distinct-serverErrors.yml new file mode 100644 index 0000000000..999e0b3fa0 --- /dev/null +++ b/source/retryable-reads/tests/unified/distinct-serverErrors.yml @@ -0,0 +1,396 @@ +description: distinct-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + +tests: + - + description: 'Distinct succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: distinct + arguments: + fieldName: x + filter: + _id: + $gt: 1 + expectResult: + - 22 + - 33 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + distinct: *collection_name + key: x + query: + _id: + $gt: 1 + databaseName: *database_name + - *retryable_command_started_event + - + description: 'Distinct succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - distinct + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: distinct + arguments: + fieldName: x + filter: + _id: + $gt: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/distinct.json b/source/retryable-reads/tests/unified/distinct.json new file mode 100644 index 0000000000..81f1f66e91 --- /dev/null +++ b/source/retryable-reads/tests/unified/distinct.json @@ -0,0 +1,352 @@ +{ + "description": "distinct", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Distinct succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectResult": [ + 22, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Distinct fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "x", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "coll", + "key": "x", + "query": { + "_id": { + "$gt": 1 + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/distinct.yml b/source/retryable-reads/tests/unified/distinct.yml new file mode 100644 index 0000000000..9b6f1648d7 --- /dev/null +++ b/source/retryable-reads/tests/unified/distinct.yml @@ -0,0 +1,168 @@ +description: distinct + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + +tests: + - + description: 'Distinct succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: distinct + arguments: + fieldName: x + filter: + _id: + $gt: 1 + expectResult: + - 22 + - 33 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + distinct: *collection_name + key: x + query: + _id: + $gt: 1 + databaseName: *database_name + - + description: 'Distinct succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - distinct + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Distinct fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: distinct + arguments: + fieldName: x + filter: + _id: + $gt: 1 + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'Distinct fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - distinct + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.json b/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.json new file mode 100644 index 0000000000..ba983c6cdf --- /dev/null +++ b/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.json @@ -0,0 +1,768 @@ +{ + "description": "estimatedDocumentCount-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "EstimatedDocumentCount succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "estimatedDocumentCount", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.yml b/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.yml new file mode 100644 index 0000000000..98fa5ec9f7 --- /dev/null +++ b/source/retryable-reads/tests/unified/estimatedDocumentCount-serverErrors.yml @@ -0,0 +1,376 @@ +description: estimatedDocumentCount-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + +tests: + - + description: 'EstimatedDocumentCount succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: estimatedDocumentCount + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + count: *collection_name + databaseName: *database_name + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - count + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: estimatedDocumentCount + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/estimatedDocumentCount.json b/source/retryable-reads/tests/unified/estimatedDocumentCount.json new file mode 100644 index 0000000000..2ee29f6799 --- /dev/null +++ b/source/retryable-reads/tests/unified/estimatedDocumentCount.json @@ -0,0 +1,273 @@ +{ + "description": "estimatedDocumentCount", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "EstimatedDocumentCount succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectResult": 2 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "estimatedDocumentCount", + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "EstimatedDocumentCount fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "count" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "estimatedDocumentCount", + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "count": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/estimatedDocumentCount.yml b/source/retryable-reads/tests/unified/estimatedDocumentCount.yml new file mode 100644 index 0000000000..13ad4061e8 --- /dev/null +++ b/source/retryable-reads/tests/unified/estimatedDocumentCount.yml @@ -0,0 +1,148 @@ +description: estimatedDocumentCount + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + +tests: + - + description: 'EstimatedDocumentCount succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: estimatedDocumentCount + expectResult: 2 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + count: *collection_name + databaseName: *database_name + - + description: 'EstimatedDocumentCount succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - count + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: estimatedDocumentCount + expectError: + isClientError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'EstimatedDocumentCount fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - count + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/exceededTimeLimit.json b/source/retryable-reads/tests/unified/exceededTimeLimit.json new file mode 100644 index 0000000000..8d090bbe3f --- /dev/null +++ b/source/retryable-reads/tests/unified/exceededTimeLimit.json @@ -0,0 +1,147 @@ +{ + "description": "ExceededTimeLimit is a retryable read", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "exceededtimelimit-test" + } + } + ], + "initialData": [ + { + "collectionName": "exceededtimelimit-test", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Find succeeds on second attempt after ExceededTimeLimit", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 262 + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "exceededtimelimit-test", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "commandName": "find", + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "exceededtimelimit-test", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "commandName": "find", + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/exceededTimeLimit.yml b/source/retryable-reads/tests/unified/exceededTimeLimit.yml new file mode 100644 index 0000000000..1912fa7bbd --- /dev/null +++ b/source/retryable-reads/tests/unified/exceededTimeLimit.yml @@ -0,0 +1,68 @@ +description: "ExceededTimeLimit is a retryable read" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [single, replicaset] + - minServerVersion: "4.1.7" + topologies: [sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + # Ensure the `configureFailpoint` and `find` commands are run on the same mongos + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name "retryable-reads-tests" + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name "exceededtimelimit-test" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "Find succeeds on second attempt after ExceededTimeLimit" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "find" ] + errorCode: 262 # ExceededTimeLimit + - name: find + arguments: + filter: { _id: { $gt: 1 } } + object: *collection0 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + commandName: find + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + commandName: find + databaseName: *database0Name diff --git a/source/retryable-reads/tests/unified/find-serverErrors.json b/source/retryable-reads/tests/unified/find-serverErrors.json new file mode 100644 index 0000000000..ab3dbe45f4 --- /dev/null +++ b/source/retryable-reads/tests/unified/find-serverErrors.json @@ -0,0 +1,1184 @@ +{ + "description": "find-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "Find succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/find-serverErrors.yml b/source/retryable-reads/tests/unified/find-serverErrors.yml new file mode 100644 index 0000000000..039cf25d95 --- /dev/null +++ b/source/retryable-reads/tests/unified/find-serverErrors.yml @@ -0,0 +1,412 @@ +description: find-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + _id: 5 + x: 55 + +tests: + - + description: 'Find succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: find + arguments: + filter: { } + sort: + _id: 1 + limit: 4 + expectResult: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *collection_name + filter: { } + sort: + _id: 1 + limit: 4 + databaseName: *database_name + - *retryable_command_started_event + - + description: 'Find succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: find + arguments: + filter: { } + sort: + _id: 1 + limit: 4 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/find.json b/source/retryable-reads/tests/unified/find.json new file mode 100644 index 0000000000..30c4c5e478 --- /dev/null +++ b/source/retryable-reads/tests/unified/find.json @@ -0,0 +1,498 @@ +{ + "description": "find", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "Find succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds on second attempt with explicit clientOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": true + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 4 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/find.yml b/source/retryable-reads/tests/unified/find.yml new file mode 100644 index 0000000000..bcd98684cc --- /dev/null +++ b/source/retryable-reads/tests/unified/find.yml @@ -0,0 +1,221 @@ +description: find + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + _id: 5 + x: 55 + +tests: + - + description: 'Find succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: find + arguments: + filter: { } + sort: + _id: 1 + limit: 4 + expectResult: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *collection_name + filter: { } + sort: + _id: 1 + limit: 4 + databaseName: *database_name + - + description: 'Find succeeds on second attempt with explicit clientOptions' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: true + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + - <<: *retryable_operation_succeeds + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: *failCommand_failPoint + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Find fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: find + arguments: + filter: { } + sort: + _id: 1 + limit: 4 + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'Find fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/findOne-serverErrors.json b/source/retryable-reads/tests/unified/findOne-serverErrors.json new file mode 100644 index 0000000000..7adda1e32b --- /dev/null +++ b/source/retryable-reads/tests/unified/findOne-serverErrors.json @@ -0,0 +1,954 @@ +{ + "description": "findOne-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "FindOne succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/findOne-serverErrors.yml b/source/retryable-reads/tests/unified/findOne-serverErrors.yml new file mode 100644 index 0000000000..48e461f11b --- /dev/null +++ b/source/retryable-reads/tests/unified/findOne-serverErrors.yml @@ -0,0 +1,396 @@ +description: findOne-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + _id: 5 + x: 55 + +tests: + - + description: 'FindOne succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11600 + - &retryable_operation_succeeds + object: *collection0 + name: findOne + arguments: + filter: + _id: 1 + expectResult: + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *collection_name + filter: + _id: 1 + databaseName: *database_name + - *retryable_command_started_event + - + description: 'FindOne succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11602 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13435 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13436 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 189 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 91 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 7 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 6 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 89 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 9001 + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: findOne + arguments: + filter: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/findOne.json b/source/retryable-reads/tests/unified/findOne.json new file mode 100644 index 0000000000..4314a19e46 --- /dev/null +++ b/source/retryable-reads/tests/unified/findOne.json @@ -0,0 +1,330 @@ +{ + "description": "findOne", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "FindOne succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "FindOne fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": { + "_id": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/findOne.yml b/source/retryable-reads/tests/unified/findOne.yml new file mode 100644 index 0000000000..e4de9d77f0 --- /dev/null +++ b/source/retryable-reads/tests/unified/findOne.yml @@ -0,0 +1,168 @@ +description: findOne + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 11 + - + _id: 2 + x: 22 + - + _id: 3 + x: 33 + - + _id: 4 + x: 44 + - + _id: 5 + x: 55 + +tests: + - + description: 'FindOne succeeds on first attempt' + operations: + - &retryable_operation_succeeds + object: *collection0 + name: findOne + arguments: + filter: + _id: 1 + expectResult: + _id: 1 + x: 11 + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *collection_name + filter: + _id: 1 + databaseName: *database_name + - + description: 'FindOne succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + - *retryable_operation_succeeds + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'FindOne fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: findOne + arguments: + filter: + _id: 1 + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'FindOne fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/gridfs-download-serverErrors.json b/source/retryable-reads/tests/unified/gridfs-download-serverErrors.json new file mode 100644 index 0000000000..5bb7eee0b2 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-download-serverErrors.json @@ -0,0 +1,1092 @@ +{ + "description": "gridfs-download-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "Download succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket1", + "database": "database1" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket1", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/gridfs-download-serverErrors.yml b/source/retryable-reads/tests/unified/gridfs-download-serverErrors.yml new file mode 100644 index 0000000000..b7942f6f51 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-download-serverErrors.yml @@ -0,0 +1,421 @@ +description: gridfs-download-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + bucket: + id: &bucket0 bucket0 + database: *database0 + +initialData: + - + collectionName: &files_collection_name fs.files + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000001' + length: 1 + chunkSize: 4 + uploadDate: + $date: '1970-01-01T00:00:00.000Z' + filename: abc + metadata: { } + - + collectionName: &chunks_collection_name fs.chunks + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000002' + files_id: + $oid: '000000000000000000000001' + 'n': 0 + data: + $binary: + base64: EQ== # hex: 11 + subType: '00' + +tests: + - + description: 'Download succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11600 + - &retryable_operation + object: *bucket0 + name: download + arguments: + id: + $oid: '000000000000000000000001' + expectResult: + $$matchesHexBytes: "11" + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *files_collection_name + filter: + _id: + $oid: '000000000000000000000001' + databaseName: *database_name + - *retryable_command_started_event + - &find_chunks_command_started_event + commandStartedEvent: + command: + find: *chunks_collection_name + # Avoid checking additional fields since the exact query may + # differ among drivers. expectResult is sufficient to assert + # correctness. + databaseName: *database_name + - + description: 'Download succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + errorCode: 10107 + - &retryable_operation_fails + object: *bucket0 + name: download + arguments: + id: + $oid: '000000000000000000000001' + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'Download fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + bucket: + id: &bucket1 bucket1 + database: *database1 + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - <<: *retryable_operation_fails + object: *bucket1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/gridfs-download.json b/source/retryable-reads/tests/unified/gridfs-download.json new file mode 100644 index 0000000000..69fe8ff7c8 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-download.json @@ -0,0 +1,367 @@ +{ + "description": "gridfs-download", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "Download succeeds on first attempt", + "operations": [ + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket1", + "database": "database1" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket1", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Download fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket0", + "name": "download", + "arguments": { + "id": { + "$oid": "000000000000000000000001" + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "_id": { + "$oid": "000000000000000000000001" + } + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/gridfs-download.yml b/source/retryable-reads/tests/unified/gridfs-download.yml new file mode 100644 index 0000000000..8164e7e826 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-download.yml @@ -0,0 +1,184 @@ +description: gridfs-download + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + bucket: + id: &bucket0 bucket0 + database: *database0 + +initialData: + - + collectionName: &files_collection_name fs.files + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000001' + length: 1 + chunkSize: 4 + uploadDate: + $date: '1970-01-01T00:00:00.000Z' + filename: abc + metadata: { } + - + collectionName: &chunks_collection_name fs.chunks + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000002' + files_id: + $oid: '000000000000000000000001' + 'n': 0 + data: + $binary: + base64: EQ== # hex: 11 + subType: '00' + +tests: + - + description: 'Download succeeds on first attempt' + operations: + - &retryable_operation + object: *bucket0 + name: download + arguments: + id: + $oid: '000000000000000000000001' + expectResult: + $$matchesHexBytes: "11" + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *files_collection_name + filter: + _id: + $oid: '000000000000000000000001' + databaseName: *database_name + - &find_chunks_command_started_event + commandStartedEvent: + command: + find: *chunks_collection_name + # Avoid checking additional fields since the exact query may + # differ among drivers. expectResult is sufficient to assert + # correctness. + databaseName: *database_name + - + description: 'Download succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'Download fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + bucket: + id: &bucket1 bucket1 + database: *database1 + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *bucket1 + name: download + arguments: + id: + $oid: '000000000000000000000001' + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'Download fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + closeConnection: true + - <<: *retryable_operation_fails + object: *bucket0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.json b/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.json new file mode 100644 index 0000000000..35f7e1e563 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.json @@ -0,0 +1,1016 @@ +{ + "description": "gridfs-downloadByName-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "DownloadByName succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket1", + "database": "database1" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "bucket1", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.yml b/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.yml new file mode 100644 index 0000000000..04d1c809d9 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-downloadByName-serverErrors.yml @@ -0,0 +1,418 @@ +description: gridfs-downloadByName-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + bucket: + id: &bucket0 bucket0 + database: *database0 + +initialData: + - + collectionName: &files_collection_name fs.files + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000001' + length: 1 + chunkSize: 4 + uploadDate: + $date: '1970-01-01T00:00:00.000Z' + filename: abc + metadata: { } + - + collectionName: &chunks_collection_name fs.chunks + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000002' + files_id: + $oid: '000000000000000000000001' + 'n': 0 + data: + $binary: + base64: EQ== # hex: 11 + subType: '00' + +tests: + - + description: 'DownloadByName succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11600 + - &retryable_operation + object: *bucket0 + name: downloadByName + arguments: + filename: abc + expectResult: + $$matchesHexBytes: "11" + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *files_collection_name + filter: + filename: abc + databaseName: *database_name + - *retryable_command_started_event + - &find_chunks_command_started_event + commandStartedEvent: + command: + find: *chunks_collection_name + # Avoid checking additional fields since the exact query may + # differ among drivers. expectResult is sufficient to assert + # correctness. + databaseName: *database_name + - + description: 'DownloadByName succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + errorCode: 10107 + - &retryable_operation_fails + object: *bucket0 + name: downloadByName + arguments: + filename: abc + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'DownloadByName fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + bucket: + id: &bucket1 bucket1 + database: *database1 + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorCode: 10107 + - <<: *retryable_operation_fails + object: *bucket1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/gridfs-downloadByName.json b/source/retryable-reads/tests/unified/gridfs-downloadByName.json new file mode 100644 index 0000000000..c3fa873396 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-downloadByName.json @@ -0,0 +1,347 @@ +{ + "description": "gridfs-downloadByName", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 1, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "abc", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000001" + }, + "n": 0, + "data": { + "$binary": { + "base64": "EQ==", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "DownloadByName succeeds on first attempt", + "operations": [ + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectResult": { + "$$matchesHexBytes": "11" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.chunks" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "bucket": { + "id": "bucket1", + "database": "database1" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket1", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "DownloadByName fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "object": "bucket0", + "name": "downloadByName", + "arguments": { + "filename": "abc" + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "fs.files", + "filter": { + "filename": "abc" + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/gridfs-downloadByName.yml b/source/retryable-reads/tests/unified/gridfs-downloadByName.yml new file mode 100644 index 0000000000..403c4c29c7 --- /dev/null +++ b/source/retryable-reads/tests/unified/gridfs-downloadByName.yml @@ -0,0 +1,180 @@ +description: gridfs-downloadByName + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + bucket: + id: &bucket0 bucket0 + database: *database0 + +initialData: + - + collectionName: &files_collection_name fs.files + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000001' + length: 1 + chunkSize: 4 + uploadDate: + $date: '1970-01-01T00:00:00.000Z' + filename: abc + metadata: { } + - + collectionName: &chunks_collection_name fs.chunks + databaseName: *database_name + documents: + - + _id: + $oid: '000000000000000000000002' + files_id: + $oid: '000000000000000000000001' + 'n': 0 + data: + $binary: + base64: EQ== # hex: 11 + subType: '00' +tests: + - + description: 'DownloadByName succeeds on first attempt' + operations: + - &retryable_operation + object: *bucket0 + name: downloadByName + arguments: + filename: abc + expectResult: + $$matchesHexBytes: "11" + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + find: *files_collection_name + filter: + filename: abc + databaseName: *database_name + - &find_chunks_command_started_event + commandStartedEvent: + command: + find: *chunks_collection_name + # Avoid checking additional fields since the exact query may + # differ among drivers. expectResult is sufficient to assert + # correctness. + databaseName: *database_name + - + description: 'DownloadByName succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - *find_chunks_command_started_event + - + description: 'DownloadByName fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + bucket: + id: &bucket1 bucket1 + database: *database1 + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *bucket1 + name: downloadByName + arguments: + filename: abc + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'DownloadByName fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + closeConnection: true + - <<: *retryable_operation_fails + object: *bucket0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/handshakeError.json b/source/retryable-reads/tests/unified/handshakeError.json new file mode 100644 index 0000000000..2921d8a954 --- /dev/null +++ b/source/retryable-reads/tests/unified/handshakeError.json @@ -0,0 +1,3079 @@ +{ + "description": "retryable reads handshake failures", + "schemaVersion": "1.4", + "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-reads-handshake-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-handshake-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "client.listDatabases 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": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabases 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": "listDatabases", + "object": "client", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames 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": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.listDatabaseNames 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": "listDatabaseNames", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listDatabases" + } + }, + { + "commandSucceededEvent": { + "commandName": "listDatabases" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "client.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "client", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.aggregate succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.aggregate succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "aggregate", + "object": "database", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.listCollections 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": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollections 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": "listCollections", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames 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": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.listCollectionNames 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": "listCollectionNames", + "object": "database", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listCollections" + } + }, + { + "commandSucceededEvent": { + "commandName": "listCollections" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "database.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "database", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate 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": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.aggregate 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": "aggregate", + "object": "collection", + "arguments": { + "pipeline": [] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments 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": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.countDocuments 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": "countDocuments", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount 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": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandSucceededEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.estimatedDocumentCount 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": "estimatedDocumentCount", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "count" + } + }, + { + "commandSucceededEvent": { + "commandName": "count" + } + } + ] + } + ] + }, + { + "description": "collection.distinct 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": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandSucceededEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.distinct 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": "distinct", + "object": "collection", + "arguments": { + "fieldName": "x", + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "distinct" + } + }, + { + "commandSucceededEvent": { + "commandName": "distinct" + } + } + ] + } + ] + }, + { + "description": "collection.find 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": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.find 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": "find", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne 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": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.findOne 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": "findOne", + "object": "collection", + "arguments": { + "filter": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes 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": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexes 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": "listIndexes", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames 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": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.listIndexNames 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": "listIndexNames", + "object": "collection" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "listIndexes" + } + }, + { + "commandSucceededEvent": { + "commandName": "listIndexes" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + }, + { + "description": "collection.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "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": "createChangeStream", + "object": "collection", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + } + ] + }, + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "ping": 1 + }, + "databaseName": "retryable-reads-handshake-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "commandName": "aggregate" + } + }, + { + "commandSucceededEvent": { + "commandName": "aggregate" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/handshakeError.yml b/source/retryable-reads/tests/unified/handshakeError.yml new file mode 100644 index 0000000000..f2b1ec982c --- /dev/null +++ b/source/retryable-reads/tests/unified/handshakeError.yml @@ -0,0 +1,1342 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable reads handshake failures" + +# 1.4 is required for "serverless: forbid". +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-reads-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. + + - description: "client.listDatabases 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: listDatabases + object: *client + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabases 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: listDatabases + object: *client + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabaseNames 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: listDatabaseNames + object: *client + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.listDatabaseNames 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: listDatabaseNames + object: *client + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listDatabases + - commandSucceededEvent: + commandName: listDatabases + + - description: "client.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *client + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "client.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *client + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.aggregate succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + 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: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.aggregate succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + 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: aggregate + object: *database + arguments: + pipeline: [ { $listLocalSessions: {} }, { $limit: 1 } ] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.listCollections 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: listCollections + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollections 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: listCollections + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollectionNames 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: listCollectionNames + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.listCollectionNames 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: listCollectionNames + object: *database + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listCollections + - commandSucceededEvent: + commandName: listCollections + + - description: "database.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *database + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "database.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *database + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.aggregate 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: aggregate + object: *collection + arguments: + pipeline: [] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.aggregate 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: aggregate + object: *collection + arguments: + pipeline: [] + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.countDocuments 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: countDocuments + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.countDocuments 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: countDocuments + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.estimatedDocumentCount 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: estimatedDocumentCount + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: count + - commandSucceededEvent: + commandName: count + + - description: "collection.estimatedDocumentCount 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: estimatedDocumentCount + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: count + - commandSucceededEvent: + commandName: count + + - description: "collection.distinct 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: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: distinct + - commandSucceededEvent: + commandName: distinct + + - description: "collection.distinct 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: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: distinct + - commandSucceededEvent: + commandName: distinct + + - description: "collection.find 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: find + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.find 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: find + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.findOne 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: findOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.findOne 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: findOne + object: *collection + arguments: + filter: {} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + + - description: "collection.listIndexes 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: listIndexes + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexes 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: listIndexes + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexNames 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: listIndexNames + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.listIndexNames 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: listIndexNames + object: *collection + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: listIndexes + - commandSucceededEvent: + commandName: listIndexes + + - description: "collection.createChangeStream succeeds after retryable handshake network error" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *collection + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate + + - description: "collection.createChangeStream succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - serverless: forbid + 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: createChangeStream + object: *collection + arguments: + pipeline: [] + saveResultAsEntity: changeStream + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: aggregate + - commandSucceededEvent: + commandName: aggregate diff --git a/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.json b/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.json new file mode 100644 index 0000000000..162dd4cee0 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.json @@ -0,0 +1,710 @@ +{ + "description": "listCollectionNames-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollectionNames succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database1", + "name": "listCollectionNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.yml b/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.yml new file mode 100644 index 0000000000..bba0407d99 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionNames-serverErrors.yml @@ -0,0 +1,360 @@ +description: listCollectionNames-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollectionNames succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11600 + - &retryable_operation + object: *database0 + name: listCollectionNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + errorCode: 10107 + - &retryable_operation_fails + object: *database0 + name: listCollectionNames + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - <<: *retryable_operation_fails + object: *database1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listCollectionNames.json b/source/retryable-reads/tests/unified/listCollectionNames.json new file mode 100644 index 0000000000..0fe575f7a6 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionNames.json @@ -0,0 +1,243 @@ +{ + "description": "listCollectionNames", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollectionNames succeeds on first attempt", + "operations": [ + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database1", + "name": "listCollectionNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionNames fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollectionNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollectionNames.yml b/source/retryable-reads/tests/unified/listCollectionNames.yml new file mode 100644 index 0000000000..7522f1d093 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionNames.yml @@ -0,0 +1,132 @@ +description: listCollectionNames + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollectionNames succeeds on first attempt' + operations: + - &retryable_operation + object: *database0 + name: listCollectionNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - + description: 'ListCollectionNames succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionNames fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *database1 + name: listCollectionNames + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListCollectionNames fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + closeConnection: true + - <<: *retryable_operation_fails + object: *database0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json b/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json new file mode 100644 index 0000000000..8b9d582c10 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json @@ -0,0 +1,710 @@ +{ + "description": "listCollectionObjects-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollectionObjects succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database1", + "name": "listCollectionObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.yml b/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.yml new file mode 100644 index 0000000000..0489c890fd --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionObjects-serverErrors.yml @@ -0,0 +1,364 @@ +# listCollectionObjects returns an array of MongoCollection objects. +# Not all drivers support this functionality. For more details, see: +# https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.md#returning-a-list-of-collection-objects + +description: listCollectionObjects-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollectionObjects succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11600 + - &retryable_operation + object: *database0 + name: listCollectionObjects + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + errorCode: 10107 + - &retryable_operation_fails + object: *database0 + name: listCollectionObjects + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - <<: *retryable_operation_fails + object: *database1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listCollectionObjects.json b/source/retryable-reads/tests/unified/listCollectionObjects.json new file mode 100644 index 0000000000..9cdbb69276 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionObjects.json @@ -0,0 +1,243 @@ +{ + "description": "listCollectionObjects", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollectionObjects succeeds on first attempt", + "operations": [ + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database1", + "name": "listCollectionObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollectionObjects fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollectionObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollectionObjects.yml b/source/retryable-reads/tests/unified/listCollectionObjects.yml new file mode 100644 index 0000000000..739ad65a42 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollectionObjects.yml @@ -0,0 +1,136 @@ +# listCollectionObjects returns an array of MongoCollection objects. +# Not all drivers support this functionality. For more details, see: +# https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.md#returning-a-list-of-collection-objects + +description: listCollectionObjects + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollectionObjects succeeds on first attempt' + operations: + - &retryable_operation + object: *database0 + name: listCollectionObjects + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - + description: 'ListCollectionObjects succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollectionObjects fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *database1 + name: listCollectionObjects + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListCollectionObjects fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + closeConnection: true + - <<: *retryable_operation_fails + object: *database0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event \ No newline at end of file diff --git a/source/retryable-reads/tests/unified/listCollections-serverErrors.json b/source/retryable-reads/tests/unified/listCollections-serverErrors.json new file mode 100644 index 0000000000..171fe7457f --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollections-serverErrors.json @@ -0,0 +1,710 @@ +{ + "description": "listCollections-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollections succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database0", + "name": "listCollections", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "database1", + "name": "listCollections", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollections-serverErrors.yml b/source/retryable-reads/tests/unified/listCollections-serverErrors.yml new file mode 100644 index 0000000000..12fb42f02a --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollections-serverErrors.yml @@ -0,0 +1,360 @@ +description: listCollections-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollections succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11600 + - &retryable_operation + object: *database0 + name: listCollections + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - *retryable_command_started_event + - + description: 'ListCollections succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + errorCode: 10107 + - &retryable_operation_fails + object: *database0 + name: listCollections + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + errorCode: 10107 + - <<: *retryable_operation_fails + object: *database1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listCollections.json b/source/retryable-reads/tests/unified/listCollections.json new file mode 100644 index 0000000000..b6152f9ce5 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollections.json @@ -0,0 +1,243 @@ +{ + "description": "listCollections", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListCollections succeeds on first attempt", + "operations": [ + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollections" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database1", + "name": "listCollections", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListCollections fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listCollections" + ], + "closeConnection": true + } + } + } + }, + { + "object": "database0", + "name": "listCollections", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listCollections": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listCollections.yml b/source/retryable-reads/tests/unified/listCollections.yml new file mode 100644 index 0000000000..1dcfe1ef48 --- /dev/null +++ b/source/retryable-reads/tests/unified/listCollections.yml @@ -0,0 +1,132 @@ +description: listCollections + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + +initialData: + - + collectionName: &collection_name coll + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListCollections succeeds on first attempt' + operations: + - &retryable_operation + object: *database0 + name: listCollections + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listCollections: 1 + - + description: 'ListCollections succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listCollections + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListCollections fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *database1 + name: listCollections + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListCollections fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listCollections + closeConnection: true + - <<: *retryable_operation_fails + object: *database0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.json b/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.json new file mode 100644 index 0000000000..489ff0ad51 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.json @@ -0,0 +1,696 @@ +{ + "description": "listDatabaseNames-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabaseNames succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client1", + "name": "listDatabaseNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.yml b/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.yml new file mode 100644 index 0000000000..36db2a1907 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseNames-serverErrors.yml @@ -0,0 +1,351 @@ +description: listDatabaseNames-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabaseNames succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11600 + - &retryable_operation + object: *client0 + name: listDatabaseNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + errorCode: 10107 + - &retryable_operation_fails + object: *client0 + name: listDatabaseNames + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - <<: *retryable_operation_fails + object: *client1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listDatabaseNames.json b/source/retryable-reads/tests/unified/listDatabaseNames.json new file mode 100644 index 0000000000..5590f39a51 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseNames.json @@ -0,0 +1,229 @@ +{ + "description": "listDatabaseNames", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabaseNames succeeds on first attempt", + "operations": [ + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client1", + "name": "listDatabaseNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseNames fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabaseNames.yml b/source/retryable-reads/tests/unified/listDatabaseNames.yml new file mode 100644 index 0000000000..d80e7d6ebf --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseNames.yml @@ -0,0 +1,123 @@ +description: listDatabaseNames + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabaseNames succeeds on first attempt' + operations: + - &retryable_operation + object: *client0 + name: listDatabaseNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - + description: 'ListDatabaseNames succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseNames fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *client1 + name: listDatabaseNames + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListDatabaseNames fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + closeConnection: true + - <<: *retryable_operation_fails + object: *client0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json b/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json new file mode 100644 index 0000000000..56f9f36236 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json @@ -0,0 +1,696 @@ +{ + "description": "listDatabaseObjects-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabaseObjects succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client1", + "name": "listDatabaseObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.yml b/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.yml new file mode 100644 index 0000000000..6704febfad --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.yml @@ -0,0 +1,355 @@ +# listDatabaseObjects returns an array of MongoDatabase objects. +# Not all drivers support this functionality. For more details, see: +# https://github.com/mongodb/specifications/blob/master/source/enumerate-databases.md#enumerating-mongodatabase-objects + +description: listDatabaseObjects-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabaseObjects succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11600 + - &retryable_operation + object: *client0 + name: listDatabaseObjects + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + errorCode: 10107 + - &retryable_operation_fails + object: *client0 + name: listDatabaseObjects + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - <<: *retryable_operation_fails + object: *client1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listDatabaseObjects.json b/source/retryable-reads/tests/unified/listDatabaseObjects.json new file mode 100644 index 0000000000..46b1511d46 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseObjects.json @@ -0,0 +1,229 @@ +{ + "description": "listDatabaseObjects", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabaseObjects succeeds on first attempt", + "operations": [ + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client1", + "name": "listDatabaseObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabaseObjects fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabaseObjects", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabaseObjects.yml b/source/retryable-reads/tests/unified/listDatabaseObjects.yml new file mode 100644 index 0000000000..a7d1e7eb50 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabaseObjects.yml @@ -0,0 +1,127 @@ +# listDatabaseObjects returns an array of MongoDatabase objects. +# Not all drivers support this functionality. For more details, see: +# https://github.com/mongodb/specifications/blob/master/source/enumerate-databases.md#enumerating-mongodatabase-objects + +description: listDatabaseObjects + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabaseObjects succeeds on first attempt' + operations: + - &retryable_operation + object: *client0 + name: listDatabaseObjects + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - + description: 'ListDatabaseObjects succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabaseObjects fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *client1 + name: listDatabaseObjects + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListDatabaseObjects fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + closeConnection: true + - <<: *retryable_operation_fails + object: *client0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event \ No newline at end of file diff --git a/source/retryable-reads/tests/unified/listDatabases-serverErrors.json b/source/retryable-reads/tests/unified/listDatabases-serverErrors.json new file mode 100644 index 0000000000..09b935a59f --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabases-serverErrors.json @@ -0,0 +1,696 @@ +{ + "description": "listDatabases-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabases succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client0", + "name": "listDatabases", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "client1", + "name": "listDatabases", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabases-serverErrors.yml b/source/retryable-reads/tests/unified/listDatabases-serverErrors.yml new file mode 100644 index 0000000000..1a236bde39 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabases-serverErrors.yml @@ -0,0 +1,351 @@ +description: listDatabases-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabases succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11600 + - &retryable_operation + object: *client0 + name: listDatabases + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + errorCode: 10107 + - &retryable_operation_fails + object: *client0 + name: listDatabases + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + errorCode: 10107 + - <<: *retryable_operation_fails + object: *client1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listDatabases.json b/source/retryable-reads/tests/unified/listDatabases.json new file mode 100644 index 0000000000..4cf5eccc7b --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabases.json @@ -0,0 +1,229 @@ +{ + "description": "listDatabases", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListDatabases succeeds on first attempt", + "operations": [ + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabases" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client1", + "name": "listDatabases", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + }, + { + "description": "ListDatabases fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "listDatabases", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listDatabases.yml b/source/retryable-reads/tests/unified/listDatabases.yml new file mode 100644 index 0000000000..743e4a4830 --- /dev/null +++ b/source/retryable-reads/tests/unified/listDatabases.yml @@ -0,0 +1,123 @@ +description: listDatabases + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + +initialData: + - + collectionName: &collection_name coll + databaseName: &database_name retryable-reads-tests + documents: [] + +tests: + - + description: 'ListDatabases succeeds on first attempt' + operations: + - &retryable_operation + object: *client0 + name: listDatabases + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listDatabases: 1 + - + description: 'ListDatabases succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listDatabases + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListDatabases fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *client1 + name: listDatabases + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListDatabases fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listDatabases + closeConnection: true + - <<: *retryable_operation_fails + object: *client0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event \ No newline at end of file diff --git a/source/retryable-reads/tests/unified/listIndexNames-serverErrors.json b/source/retryable-reads/tests/unified/listIndexNames-serverErrors.json new file mode 100644 index 0000000000..7b98111480 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexNames-serverErrors.json @@ -0,0 +1,749 @@ +{ + "description": "listIndexNames-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListIndexNames succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "listIndexNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listIndexNames-serverErrors.yml b/source/retryable-reads/tests/unified/listIndexNames-serverErrors.yml new file mode 100644 index 0000000000..aa217bbdcc --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexNames-serverErrors.yml @@ -0,0 +1,370 @@ +description: listIndexNames-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListIndexNames succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 11600 + - &retryable_operation + object: *collection0 + name: listIndexNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listIndexes: *collection_name + databaseName: *database_name + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listIndexes + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: listIndexNames + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listIndexNames.json b/source/retryable-reads/tests/unified/listIndexNames.json new file mode 100644 index 0000000000..c5fe967ff5 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexNames.json @@ -0,0 +1,263 @@ +{ + "description": "listIndexNames", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListIndexNames succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "listIndexNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexNames fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "listIndexNames", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listIndexNames.yml b/source/retryable-reads/tests/unified/listIndexNames.yml new file mode 100644 index 0000000000..f18c223c49 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexNames.yml @@ -0,0 +1,142 @@ +description: listIndexNames + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListIndexNames succeeds on first attempt' + operations: + - &retryable_operation + object: *collection0 + name: listIndexNames + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listIndexes: *collection_name + databaseName: *database_name + - + description: 'ListIndexNames succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexNames fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: listIndexNames + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListIndexNames fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listIndexes + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listIndexes-serverErrors.json b/source/retryable-reads/tests/unified/listIndexes-serverErrors.json new file mode 100644 index 0000000000..0110a0acd0 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexes-serverErrors.json @@ -0,0 +1,749 @@ +{ + "description": "listIndexes-serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListIndexes succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11600 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 11602 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13435 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 13436 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 189 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 91 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 7 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 6 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 89 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 9001 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes fails after two NotWritablePrimary errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes fails after NotWritablePrimary when retryReads is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "collection1", + "name": "listIndexes", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listIndexes-serverErrors.yml b/source/retryable-reads/tests/unified/listIndexes-serverErrors.yml new file mode 100644 index 0000000000..964def3d10 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexes-serverErrors.yml @@ -0,0 +1,370 @@ +description: listIndexes-serverErrors + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListIndexes succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 11600 + - &retryable_operation + object: *collection0 + name: listIndexes + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listIndexes: *collection_name + databaseName: *database_name + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 11602 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 10107 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 13435 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 13436 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 189 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 91 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 7 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 6 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 89 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 9001 + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes fails after two NotWritablePrimary errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listIndexes + errorCode: 10107 + - &retryable_operation_fails + object: *collection0 + name: listIndexes + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes fails after NotWritablePrimary when retryReads is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + errorCode: 10107 + - <<: *retryable_operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event diff --git a/source/retryable-reads/tests/unified/listIndexes.json b/source/retryable-reads/tests/unified/listIndexes.json new file mode 100644 index 0000000000..2560e4961c --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexes.json @@ -0,0 +1,263 @@ +{ + "description": "listIndexes", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "ListIndexes succeeds on first attempt", + "operations": [ + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes succeeds on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes fails on first attempt", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "listIndexes", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListIndexes fails on second attempt", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "listIndexes" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "listIndexes", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "listIndexes": "coll" + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/listIndexes.yml b/source/retryable-reads/tests/unified/listIndexes.yml new file mode 100644 index 0000000000..9f3932a938 --- /dev/null +++ b/source/retryable-reads/tests/unified/listIndexes.yml @@ -0,0 +1,142 @@ +description: listIndexes + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'ListIndexes succeeds on first attempt' + operations: + - &retryable_operation + object: *collection0 + name: listIndexes + expectEvents: + - + client: *client0 + events: + - &retryable_command_started_event + commandStartedEvent: + command: + listIndexes: *collection_name + databaseName: *database_name + - + description: 'ListIndexes succeeds on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - listIndexes + closeConnection: true + - *retryable_operation + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event + - + description: 'ListIndexes fails on first attempt' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - &retryable_operation_fails + object: *collection1 + name: listIndexes + expectError: + isError: true + expectEvents: + - + client: *client1 + events: + - *retryable_command_started_event + - + description: 'ListIndexes fails on second attempt' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - listIndexes + closeConnection: true + - <<: *retryable_operation_fails + object: *collection0 + expectEvents: + - + client: *client0 + events: + - *retryable_command_started_event + - *retryable_command_started_event \ No newline at end of file diff --git a/source/retryable-reads/tests/unified/mapReduce.json b/source/retryable-reads/tests/unified/mapReduce.json new file mode 100644 index 0000000000..745c0ef001 --- /dev/null +++ b/source/retryable-reads/tests/unified/mapReduce.json @@ -0,0 +1,284 @@ +{ + "description": "mapReduce", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 0 + }, + { + "_id": 2, + "x": 1 + }, + { + "_id": 3, + "x": 2 + } + ] + } + ], + "tests": [ + { + "description": "MapReduce succeeds with retry on", + "operations": [ + { + "object": "collection0", + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "expectResult": [ + { + "_id": 0, + "value": 6 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "MapReduce fails with retry on", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "mapReduce" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "MapReduce fails with retry off", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryReads": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "mapReduce" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "mapReduce", + "arguments": { + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "mapReduce": "coll", + "map": { + "$code": "function inc() { return emit(0, this.x + 1) }" + }, + "reduce": { + "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" + }, + "out": { + "inline": 1 + } + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/mapReduce.yml b/source/retryable-reads/tests/unified/mapReduce.yml new file mode 100644 index 0000000000..10e88e9168 --- /dev/null +++ b/source/retryable-reads/tests/unified/mapReduce.yml @@ -0,0 +1,153 @@ +description: mapReduce + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - single + - replicaset + - + minServerVersion: 4.1.7 + # serverless proxy does not support mapReduce operation + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-reads-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - + _id: 1 + x: 0 + - + _id: 2 + x: 1 + - + _id: 3 + x: 2 + +tests: + - + description: 'MapReduce succeeds with retry on' + operations: + - &operation_succeeds + object: *collection0 + name: mapReduce + arguments: + map: + $code: 'function inc() { return emit(0, this.x + 1) }' + reduce: + $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' + out: + inline: 1 + expectResult: + - + _id: 0 + value: 6 + expectEvents: + - + client: *client0 + events: + - &command_started_event + commandStartedEvent: + command: + mapReduce: *collection_name + map: + $code: 'function inc() { return emit(0, this.x + 1) }' + reduce: + $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' + out: + inline: 1 + databaseName: *database_name + - + description: 'MapReduce fails with retry on' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: &failCommand_failPoint + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - mapReduce + closeConnection: true + - &operation_fails + object: *collection0 + name: mapReduce + arguments: + map: + $code: 'function inc() { return emit(0, this.x + 1) }' + reduce: + $code: 'function sum(key, values) { return values.reduce((acc, x) => acc + x); }' + out: + inline: 1 + expectError: + isError: true + expectEvents: + - + client: *client0 + events: + - *command_started_event + - + description: 'MapReduce fails with retry off' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryReads: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: *failCommand_failPoint + - <<: *operation_fails + object: *collection1 + expectEvents: + - + client: *client1 + events: + - *command_started_event diff --git a/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.json b/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.json new file mode 100644 index 0000000000..8aa6a6b5e5 --- /dev/null +++ b/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.json @@ -0,0 +1,147 @@ +{ + "description": "ReadConcernMajorityNotAvailableYet is a retryable read", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "readconcernmajoritynotavailableyet_test" + } + } + ], + "initialData": [ + { + "collectionName": "readconcernmajoritynotavailableyet_test", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Find succeeds on second attempt after ReadConcernMajorityNotAvailableYet", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 134 + } + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "readconcernmajoritynotavailableyet_test", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "commandName": "find", + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "readconcernmajoritynotavailableyet_test", + "filter": { + "_id": { + "$gt": 1 + } + } + }, + "commandName": "find", + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.yml b/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.yml new file mode 100644 index 0000000000..707a62acd7 --- /dev/null +++ b/source/retryable-reads/tests/unified/readConcernMajorityNotAvailableYet.yml @@ -0,0 +1,68 @@ +description: "ReadConcernMajorityNotAvailableYet is a retryable read" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [single, replicaset] + - minServerVersion: "4.1.7" + topologies: [sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + # Ensure the `configureFailpoint` and `find` commands are run on the same mongos + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name "retryable-reads-tests" + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name "readconcernmajoritynotavailableyet_test" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: "Find succeeds on second attempt after ReadConcernMajorityNotAvailableYet" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "find" ] + errorCode: 134 # ReadConcernMajorityNotAvailableYet + - name: find + arguments: + filter: { _id: { $gt: 1 } } + object: *collection0 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + commandName: find + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + commandName: find + databaseName: *database0Name diff --git a/source/retryable-writes/retryable-writes.md b/source/retryable-writes/retryable-writes.md new file mode 100644 index 0000000000..85b2ec4cdb --- /dev/null +++ b/source/retryable-writes/retryable-writes.md @@ -0,0 +1,811 @@ +# Retryable Writes + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +MongoDB 3.6 will implement support for server sessions, which are shared resources within a cluster identified by a +session ID. Drivers compatible with MongoDB 3.6 will also implement support for client sessions, which are always +associated with a server session and will allow for certain commands to be executed within the context of a server +session. + +Additionally, MongoDB 3.6 will utilize server sessions to allow some write commands to specify a transaction ID to +enforce at-most-once semantics for the write operation(s) and allow for retrying the operation if the driver fails to +obtain a write result (e.g. network error or "not writable primary" error after a replica set failover). This +specification will outline how an API for retryable write operations will be implemented in drivers. The specification +will define an option to enable retryable writes for an application and describe how a transaction ID will be provided +to write commands executed therein. + +The changes in this specification are related to but distinct from the retryability behaviors defined in the +[Client Backpressure Specification](../client-backpressure/client-backpressure.md), which defines a retryability +mechanism for all commands under certain server conditions. Unless otherwise noted, the changes in this specification +refer only to the retryability behaviors summarized above. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +**Transaction ID** + +The transaction ID identifies the transaction as part of which the command is running. In a write command where the +client has requested retryable behavior, it is expressed by the top-level `lsid` and `txnNumber` fields. The `lsid` +component is the corresponding server session ID. which is a BSON value defined in the +[Driver Session](../sessions/driver-sessions.md) specification. The `txnNumber` component is a monotonically increasing +(per server session), positive 64-bit integer. + +**ClientSession** + +Driver object representing a client session, which is defined in the [Driver Session](../sessions/driver-sessions.md) +specification. This object is always associated with a server session; however, drivers will pool server sessions so +that creating a ClientSession will not always entail creation of a new server session. The name of this object MAY vary +across drivers. + +**Retryable Write Error** + +An error is considered retryable if it has a RetryableWriteError label in its top-level "errorLabels" field. See +[Determining Retryable Write Errors](#determining-retryable-write-errors) for more information. + +Additional terms may be defined in the [Driver Session](../sessions/driver-sessions.md) specification. + +### Naming Deviations + +This specification defines the name for a new MongoClient option, `retryWrites`. Drivers MUST use the defined name for +the connection string parameter to ensure portability of connection strings across applications and drivers. + +If drivers solicit MongoClient options through another mechanism (e.g. options dictionary provided to the MongoClient +constructor), drivers SHOULD use the defined name but MAY deviate to comply with their existing conventions. For +example, a driver may use `retry_writes` instead of `retryWrites`. + +For any other names in the spec, drivers SHOULD use the defined name but MAY deviate to comply with their existing +conventions. + +### MongoClient Configuration + +This specification introduces the following client-level configuration option. + +#### retryWrites + +This boolean option determines whether retryable behavior will be applied to all supported write operations executed +within the MongoClient. This option MUST default to true. + +This option MUST NOT be configurable at the level of a database object, collection object, or at the level of an +individual write operation. + +### Requirements for Retryable Writes + +#### Supported Server Versions + +Like sessions, retryable writes require a MongoDB 3.6 replica set or shard cluster operating with feature compatibility +version 3.6 (i.e. the `{setFeatureCompatibilityVersion: 3.6}` administrative command has been run on the cluster). +Drivers MUST verify server eligibility by ensuring that `maxWireVersion` is at least six, the +`logicalSessionTimeoutMinutes` field is present in the server's `hello` or legacy hello response, and the server type is +not standalone. + +Retryable writes are only supported by storage engines that support document-level locking. Notably, that excludes the +MMAPv1 storage engine which is available in both MongoDB 3.6 and 4.0. Since `retryWrites` defaults to `true`, Drivers +MUST raise an actionable error message when the server returns code 20 with errmsg starting with "Transaction numbers". +The replacement error message MUST be: + +```text +This MongoDB deployment does not support retryable writes. Please add +retryWrites=false to your connection string. +``` + +If the server selected for the first attempt of a retryable write operation does not support retryable writes, drivers +MUST execute the write as if retryable writes were not enabled. Drivers MUST NOT include a transaction ID in the write +command and MUST not retry the command under any circumstances. + +In a sharded cluster, it is possible that mongos may appear to support retryable writes but one or more shards in the +cluster do not (e.g. replica set shard is configured with feature compatibility version 3.4, a standalone is added as a +new shard). In these rare cases, a write command that fans out to a shard that does not support retryable writes may +partially fail and an error may be reported in the write result from mongos (e.g. `writeErrors` array in the bulk write +result). This does not constitute a retryable write error. Drivers MUST relay such errors to the user. + +#### Supported Write Operations + +MongoDB 3.6 will support retryability for some, but not all, write operations. + +Supported single-statement write operations include `insertOne()`, `updateOne()`, `replaceOne()`, `deleteOne()`, +`findOneAndDelete()`, `findOneAndReplace()`, and `findOneAndUpdate()`. + +Supported multi-statement write operations include `insertMany()` and `bulkWrite()`. The ordered option may be `true` or +`false`. For both the collection-level and client-level `bulkWrite()` methods, a bulk write batch is only retryable if +it does not contain any `multi: true` writes (i.e. `UpdateMany` and `DeleteMany`). Drivers MUST evaluate eligibility for +each write command sent as part of the `bulkWrite()` (after order and batch splitting) individually. Drivers MUST NOT +alter existing logic for order and batch splitting in an attempt to maximize retryability for operations within a bulk +write. + +These methods above are defined in the [CRUD](../crud/crud.md) specification. + +Later versions of MongoDB may add support for additional write operations. + +Drivers MUST document operations that support retryable behavior and the conditions for which retryability is determined +(see: [How will users know which operations are supported?](#how-will-users-know-which-operations-are-supported)). +Drivers are not required to exhaustively document all operations that do not support retryable behavior. + +#### Unsupported Write Operations + +Write commands specifying an unacknowledged write concern (e.g. `{w: 0})`) do not support retryable behavior. Drivers +MUST NOT add a transaction ID to any write command with an unacknowledged write concern executed within a MongoClient +where retryable writes have been enabled. Drivers MUST NOT retry these commands. + +Write commands where a single statement might affect multiple documents will not be initially supported by MongoDB 3.6, +although this may change in the future. This includes an +[update](https://www.mongodb.com/docs/manual/reference/command/update/) command where any statement in the updates +sequence specifies a `multi` option of `true` or a +[delete](https://www.mongodb.com/docs/manual/reference/command/delete/) command where any statement in the `deletes` +sequence specifies a `limit` option of `0`. In the context of the [CRUD](../crud/crud.md) specification, this includes +the `updateMany()` and `deleteMany()` methods and, in some cases, `bulkWrite()`. Drivers MUST NOT add a transaction ID +to any single- or multi-statement write commands that include one or more multi-document write operations. Drivers MUST +NOT retry these commands if they fail to return a response. With regard to `bulkWrite()`, drivers MUST evaluate +eligibility for each write command sent as part of the `bulkWrite()` (after order and batch splitting) individually. + +Write commands other than [insert](https://www.mongodb.com/docs/manual/reference/command/insert/), +[update](https://www.mongodb.com/docs/manual/reference/command/update/), +[delete](https://www.mongodb.com/docs/manual/reference/command/delete/), or +[findAndModify](https://www.mongodb.com/docs/manual/reference/command/findAndModify/) will not be initially supported by +MongoDB 3.6, although this may change in the future. This includes, but is not limited to, an +[aggregate](https://www.mongodb.com/docs/manual/reference/command/aggregate/) command using a write stage (e.g. `$out`, +`$merge`). Drivers MUST NOT add a transaction ID to these commands and MUST NOT retry these commands if they fail to +return a response. + +#### Retryable Writes Within Transactions + +In MongoDB 4.0 the only supported retryable write commands within a transaction are `commitTransaction` and +`abortTransaction`. Therefore drivers MUST NOT retry write commands within transactions even when `retryWrites` has been +set to true on the `MongoClient`. In addition, drivers MUST NOT add the `RetryableWriteError` label to any error that +occurs during a write command within a transaction (excepting `commitTransation` and `abortTransaction`), even when +`retryWrites` has been set to true on the `MongoClient`. + +### Implementing Retryable Writes + +#### Determining Retryable Write Errors + +When connected to a MongoDB instance that supports retryable writes (versions 3.6+), the driver MUST treat all errors +with the RetryableWriteError label as retryable. This error label can be found in the top-level "errorLabels" field of +the error. + +##### RetryableWriteError Labels + +The RetryableWriteError label might be added to an error in a variety of ways: + +- When the driver encounters a network error establishing an initial connection to a server, it MUST add a + RetryableWriteError label to that error if the MongoClient performing the operation has the retryWrites + configuration option set to true. + +- When the driver encounters a network error communicating with any server version that supports retryable writes, it + MUST add a RetryableWriteError label to that error if the MongoClient performing the operation has the retryWrites + configuration option set to true. + +- When a CMAP-compliant driver encounters a + [PoolClearedError](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-errors) + during connection check out, it MUST add a RetryableWriteError label to that error if the MongoClient performing the + operation has the retryWrites configuration option set to true. + +- For server versions 4.4 and newer, the server will add a RetryableWriteError label to errors or server responses that + it considers retryable before returning them to the driver. As new server versions are released, the errors that are + labeled with the RetryableWriteError label may change. Drivers MUST NOT add a RetryableWriteError label to any error + derived from a 4.4+ server response (i.e. any error that is not a network error). + +- When receiving a command result with an error from a pre-4.4 server that supports retryable writes, the driver MUST + add a RetryableWriteError label to errors that meet the following criteria if the retryWrites option is set to true + on the client performing the relevant operation: + + - a mongod or mongos response with any the following error codes in the top-level `code` field: + + | Error Name | Error Code | + | ------------------------------- | ---------- | + | InterruptedAtShutdown | 11600 | + | InterruptedDueToReplStateChange | 11602 | + | NotWritablePrimary | 10107 | + | NotPrimaryNoSecondaryOk | 13435 | + | NotPrimaryOrSecondary | 13436 | + | PrimarySteppedDown | 189 | + | ShutdownInProgress | 91 | + | HostNotFound | 7 | + | HostUnreachable | 6 | + | NetworkTimeout | 89 | + | SocketException | 9001 | + | ExceededTimeLimit | 262 | + + - a mongod response with any of the previously listed codes in the `writeConcernError.code` field. + + Drivers MUST NOT add a RetryableWriteError label based on the following: + + - any `writeErrors[].code` fields in a mongod or mongos response + - the `writeConcernError.code` field in a mongos response + + The criteria for retryable errors is similar to the discussion in the SDAM spec's section on + [Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling), but includes + additional error codes. See [What do the additional error codes mean?](#what-do-the-additional-error-codes-mean) for + the reasoning behind these additional errors. + +To understand why the driver should only add the RetryableWriteError label to an error when the retryWrites option is +true on the MongoClient performing the operation, see +[Why does the driver only add the RetryableWriteError label to errors that occur on a MongoClient with retryWrites set to true?](#why-does-the-driver-only-add-the-retryablewriteerror-label-to-errors-that-occur-on-a-mongoclient-with-retrywrites-set-to-true) + +Note: During a retryable write operation on a sharded cluster, mongos may retry the operation internally, in which case +it will not add a RetryableWriteError label to any error that occurs after those internal retries to prevent excessive +retrying. + +For more information about error labels, see the +[Transactions specification](../transactions/transactions.md#error-labels). + +#### Generating Transaction IDs + +The server requires each retryable write operation to provide a unique transaction ID in its command document. The +transaction ID consists of a server session ID and a monotonically increasing transaction number. The session ID is +obtained from the ClientSession object, which will have either been passed to the write operation from the application +or constructed internally for the operation. Drivers will be responsible for maintaining a monotonically increasing +transaction number for each server session used by a ClientSession object. Drivers that pool server sessions MUST +preserve the transaction number when reusing a server session from the pool with a new ClientSession (this can be +tracked as another property on the driver's object for the server session). + +Drivers MUST ensure that each retryable write command specifies a transaction number larger than any previously used +transaction number for its session ID. + +Since ClientSession objects are not thread safe and may only be used by one thread at a time, drivers should not need to +worry about race conditions when incrementing the transaction number. + +#### Behavioral Changes for Write Commands + +Drivers MUST automatically add a transaction ID to all supported write commands executed via a specific +[CRUD](../crud/crud.md) method (e.g. `updateOne()`) or write command method (e.g. `executeWriteCommand()`) within a +MongoClient where retryable writes have been enabled and when the selected server supports retryable writes. + +If your driver offers a generic command method on your database object (e.g. `runCommand()`), it MUST NOT check the +user's command document to determine if it is a supported write operation and MUST NOT automatically add a transaction +ID. The method should send the user's command document to the server as-is. + +This specification does not affect write commands executed within a MongoClient where retryable writes have not been +enabled. + +#### Constructing Write Commands + +When constructing a supported write command that will be executed within a MongoClient where retryable writes have been +enabled, drivers MUST increment the transaction number for the corresponding server session and include the server +session ID and transaction number in top-level `lsid` and `txnNumber` fields, respectively. `lsid` is a BSON value +(discussed in the [Driver Session](../sessions/driver-sessions.md) specification). `txnNumber` MUST be a positive 64-bit +integer (BSON type 0x12). + +The following example illustrates a possible write command for an `updateOne()` operation: + +```typescript +{ + update: "coll", + lsid: { ... }, + txnNumber: 100, + updates: [ + { q: { x: 1 }, u: { $inc: { y: 1 } } }, + ], + ordered: true +} +``` + +When constructing multiple write commands for a multi-statement write operation (i.e. `insertMany()` and `bulkWrite()`), +drivers MUST increment the transaction number for each supported write command in the batch. + +#### Executing Retryable Write Commands + +When selecting a writable server for the first attempt of a retryable write command, drivers MUST allow a server +selection error to propagate. In this case, the caller is able to infer that no attempt was made. + +If retryable writes is not enabled or the selected server does not support retryable writes, drivers MUST NOT include a +transaction ID in the command and MUST attempt to execute the write command exactly once and allow any errors to +propagate. In this case, the caller is able to infer that an attempt was made. + +If retryable writes are enabled and the selected server supports retryable writes, drivers MUST add a transaction ID to +the command. Drivers MUST only attempt to retry a write command if the first attempt yields a retryable error. Drivers +MUST NOT attempt to retry a write command on any other error. + +If the first attempt of a write command including a transaction ID encounters a retryable error, the driver MUST update +its topology according to the SDAM spec (see: +[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)) and capture this +original retryable error. + +Drivers MUST then retry the operation as many times as necessary until any one of the following conditions is reached: + +- the operation succeeds. + +- the operation fails with a non-retryable error. + +- CSOT is enabled and the operation times out per + [Client Side Operations Timeout: Retryability](../client-side-operations-timeout/client-side-operations-timeout.md#retryability). + +- CSOT is not enabled and one retry was attempted. + +For each retry attempt, drivers MUST select a writable server. For sharded clusters, the server address on which the +operation failed MUST be provided to the server selection mechanism as a member of the deprioritized server address +list. For all other topologies, the server address on which the operation failed MUST be provided to the server +selection mechanism as a member of the deprioritized server address list only if the error is labelled with +`SystemOverloadedError`. This requirement preserves the existing behavior of retryable writes for non-overload errors. + +If the driver cannot select a server for a retry attempt or the selected server does not support retryable writes, +retrying is not possible and drivers MUST raise the retryable error from the previous attempt. In both cases, the caller +is able to infer that an attempt was made. + +If a retry attempt also fails, drivers MUST update their topology according to the SDAM spec (see: +[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)). + +If the driver is unable to retry an operation, an error MUST be returned to the user. Some errors that a driver +encounters indicate that no writes were attempted (i.e., the operation is a no-op). These errors include any client-side +error that occurs before a command is sent (e.g., a server selection or connection checkout error) or any server error +with the `NoWritesPerformed` error label. When the driver encounters multiple errors, the driver MUST ensure that if an +error has been encountered which indicates that a write was attempted, this error is returned. This behavior is +summarized below in the following rules: + +- If the driver has encountered only errors that indicate write attempts were made, the most recently encountered error + must be returned. +- If all errors indicate no attempt was made (e.g., all errors contain the `NoWritesPerformed` error label or are + client-side errors before a command is sent), the first error encountered must be returned. +- If the driver has encountered some errors which indicate a write attempt was made and some which indicate no write + attempt was made (e.g., a retryable server error followed by a checkout error), the most recently encountered error + which indicates a write attempt occurred must be returned. + +If a driver associates server information (e.g. the server address or description) with an error, the driver MUST ensure +that the reported server information corresponds to the server that originated the error. + +> [!NOTE] +> The rules above and the pseudocode below only demonstrate the rules for retryable writes as outlined in this +> specification. For simplicity, and to make the retryable writes rules easier to follow, the pseudocode was +> intentionally unmodified. For a pseudocode block that contains both retryable writes logic as defined in this +> specification and backoff retryabilitity as defined in the client backpressure specification, see the pseudocode in +> the [Backpressure Specification](../client-backpressure/client-backpressure.md). + +The above rules are implemented in the following pseudo-code: + +```typescript +/** + * Checks if a server supports retryable writes. + */ +function isRetryableWritesSupported(server) { + if (server.getMaxWireVersion() < RETRYABLE_WIRE_VERSION) { + return false; + } + + if ( ! server.hasLogicalSessionTimeoutMinutes()) { + return false; + } + + if (server.isStandalone()) { + return false; + } + + return true; +} + +/** + * Executes a write command in the context of a MongoClient where retryable + * writes have been enabled. The session parameter may be an implicit or + * explicit client session (depending on how the CRUD method was invoked). + */ +function executeRetryableWrite(command, session) { + /* Allow ServerSelectionException to propagate to our caller, which can then + * assume that no attempts were made. */ + server = selectServer("writable"); + + /* If the server does not support retryable writes, execute the write as if + * retryable writes are not enabled. */ + if ( ! isRetryableWritesSupported(server)) { + return executeCommand(server, command); + } + + /* Incorporate lsid and txnNumber fields into the command document. These + * values will be derived from the implicit or explicit session object. */ + retryableCommand = addTransactionIdToCommand(command, session); + + Exception previousError = null; + retrying = false; + deprioritizedServers = []; + while true { + try { + return executeCommand(server, retryableCommand); + } catch (Exception currentError) { + handleError(currentError); + + /* If the error has a RetryableWriteError label, remember the exception + * and proceed with retrying the operation. + * + * IllegalOperation (code 20) with errmsg starting with "Transaction + * numbers" MUST be re-raised with an actionable error message. + */ + if (!currentError.hasErrorLabel("RetryableWriteError")) { + if ( currentError.code == 20 && previousError.errmsg.startsWith("Transaction numbers") ) { + currentError.errmsg = "This MongoDB deployment does not support retryable..."; + } + throw currentError; + } + + /* + * If the "previousError" is "null", then the "currentError" is the + * first error encountered during the retry attempt cycle. We must + * persist the first error in the case where all succeeding errors are + * labeled "NoWritesPerformed", which would otherwise raise "null" as + * the error. + */ + if (previousError == null) { + previousError = currentError; + } + + /* + * For exceptions that originate from the driver (e.g. no socket available + * from the connection pool), we should raise the previous error if there + * was one. + */ + if (currentError is not DriverException && ! previousError.hasErrorLabel("NoWritesPerformed")) { + previousError = currentError; + } + } + + /* + * We try to select a server that has not already failed by adding the + * failed server to the list of deprioritized servers passed to selectServer. + * If we cannot select a writable server, do not proceed with retrying and + * throw the previous error. The caller can then infer that an attempt was + * made and failed. */ + // Sharded clusters deprioritize on all retryable errors. + // Other topologies only deprioritize on overload errors. + try { + if server.isSharded || previousError.hasLabel("SystemOverloadedError") { + deprioritizedServers.push(server.address); + } + server = selectServer("writable", deprioritizedServers); + } catch (Exception ignoredError) { + throw previousError; + } + + /* If the server selected for retrying is too old, throw the previous error. + * The caller can then infer that an attempt was made and failed. This case + * is very rare, and likely means that the cluster is in the midst of a + * downgrade. */ + if ( ! isRetryableWritesSupported(server)) { + throw previousError; + } + + if (timeoutMS == null) { + /* If CSOT is not enabled, allow any retryable error from the second + * attempt to propagate to our caller, as it will be just as relevant + * (if not more relevant) than the original error. */ + if (retrying) { + throw previousError; + } + } else if (isExpired(timeoutMS)) { + /* CSOT is enabled and the operation has timed out. */ + throw previousError; + } + retrying = true; + } +} +``` + +`handleError` in the above pseudocode refers to the function defined in the +[Error handling pseudocode](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling-pseudocode) +section of the SDAM specification. + +When retrying a write command, drivers MUST resend the command with the same transaction ID. Drivers MUST NOT resend the +original wire protocol message if doing so would violate rules for +[gossipping the cluster time](../sessions/driver-sessions.md#gossipping-the-cluster-time) (see: +[Can drivers resend the same wire protocol message on retry attempts?](#can-drivers-resend-the-same-wire-protocol-message-on-retry-attempts)). + +In the case of a multi-statement write operation split across multiple write commands, a failed retry attempt will also +interrupt execution of any additional write operations in the batch (regardless of the ordered option). This is no +different than if a retryable error had been encountered without retryable behavior enabled or supported by the driver. +Drivers are encouraged to provide access to an intermediary write result (e.g. BulkWriteResult, InsertManyResult) +through the BulkWriteException, in accordance with the [CRUD](../crud/crud.md) specification. + +## Logging Retry Attempts + +Drivers MAY choose to log retry attempts for write operations. This specification does not define a format for such log +messages. + +## Command Monitoring + +In accordance with the +[Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) specification, +drivers MUST guarantee that each `CommandStartedEvent` has either a correlating `CommandSucceededEvent` or +`CommandFailedEvent` and that every "command started" log message has either a correlating "command succeeded" log +message or "command failed" log message. If the first attempt of a retryable write operation encounters a retryable +error, drivers MUST fire a `CommandFailedEvent` and emit a "command failed" log message for the retryable error and fire +a separate `CommandStartedEvent` and "command succeeded" log message when executing the subsequent retry attempt. Note +that the second `CommandStartedEvent` and "command succeeded" log message may have a different `connectionId`, since a +writable server is reselected for the retry attempt. + +Each attempt of a retryable write operation SHOULD report a different `requestId` so that events for each attempt can be +properly correlated with one another. + +The [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) specification +states that the `operationId` field is a driver-generated, 64-bit integer and may be "used to link events together such +as bulk write operations." Each attempt of a retryable write operation SHOULD report the same `operationId`; however, +drivers SHOULD NOT use the `operationId` field to relay information about a transaction ID. A bulk write operation may +consist of multiple write commands, each of which may specify a unique transaction ID. + +## Test Plan + +See the [README](tests/README.md) for tests. + +At a high level, the test plan will cover the following scenarios for executing supported write operations within a +MongoClient where retryable writes have been enabled: + +- Executing the same write operation (and transaction ID) multiple times should yield an identical write result. +- Test at-most-once behavior by observing that subsequent executions of the same write operation do not incur further + modifications to the collection data. +- Exercise supported single-statement write operations (i.e. deleteOne, insertOne, replaceOne, updateOne, and + findAndModify). +- Exercise supported multi-statement insertMany and bulkWrite operations, which contain only supported single-statement + write operations. Both ordered and unordered execution should be tested. + +Additional prose tests for other scenarios are also included. + +## Motivation for Change + +Drivers currently have no API for specifying at-most-once semantics and retryable behavior for write operations. The +driver API needs to be extended to support this behavior. + +## Design Rationale + +The design of this specification piggy-backs that of the [Driver Session](../sessions/driver-sessions.md) specification +in that it modifies the driver API as little as possible to introduce the concept of at-most-once semantics and +retryable behavior for write operations. A transaction ID will be included in all supported write commands executed +within the scope of a MongoClient where retryable writes have been enabled. + +Drivers expect the server to yield an error if a transaction ID is included in an unsupported write command. This +requires drivers to maintain an allow list and track which write operations support retryable behavior for a given +server version (see: +[Why must drivers maintain an allow list of supported operations?](#why-must-drivers-maintain-an-allow-list-of-supported-operations)). + +While this approach will allow applications to take advantage of retryable write behavior with minimal code changes, it +also presents a documentation challenge. Users must understand exactly what can and will be retried (see: +[How will users know which operations are supported?](#how-will-users-know-which-operations-are-supported)). + +## Backwards Compatibility + +The API changes to support retryable writes extend the existing API but do not introduce any backward breaking changes. +Existing programs that do not make use of retryable writes will continue to compile and run correctly. + +## Reference Implementation + +The C# and C drivers will provide reference implementations. JIRA links will be added here at a later point. + +## Future Work + +Supporting at-most-once semantics and retryable behavior for updateMany and deleteMany operations may become possible +once the server implements support for multi-document transactions. + +A separate specification for retryable read operations could complement this specification. Retrying read operations +would not require client or server sessions and could be implemented independently of retryable writes. + +## Q & A + +### What do the additional error codes mean? + +The errors `HostNotFound`, `HostUnreachable`, `NetworkTimeout`, `SocketException` may be returned from mongos during +problems routing to a shard. These may be transient, or localized to that mongos. + +### Why are write operations only retried once by default? + +The spec concerns itself with retrying write operations that encounter a retryable error (i.e. no response due to +network error or a response indicating that the node is no longer a primary). A retryable error may be classified as +either a transient error (e.g. dropped connection, replica set failover) or persistent outage. In the case of a +transient error, the driver will mark the server as "unknown" per the +[SDAM](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec. A subsequent retry attempt will +allow the driver to rediscover the primary within the designated server selection timeout period (30 seconds by +default). If server selection times out during this retry attempt, we can reasonably assume that there is a persistent +outage. In the case of a persistent outage, multiple retry attempts are fruitless and would waste time. See +[How To Write Resilient MongoDB Applications](https://emptysqua.re/blog/how-to-write-resilient-mongodb-applications/) +for additional discussion on this strategy. + +However when [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md) is +enabled, the driver will retry multiple times until the operation succeeds, a non-retryable error is encountered, or the +timeout expires. Retrying multiple times provides greater resilience to cascading failures such as rolling server +restarts during planned maintenance events. + +### What if the transaction number overflows? + +Since server sessions are pooled and session lifetimes are configurable on the server, it is theoretically possible for +the transaction number to overflow if it reaches the limits of a signed 64-bit integer. The spec does not address this +scenario. Drivers may decide to handle this as they wish. For example, they may raise a client-side error if a +transaction number would overflow, eagerly remove sessions with sufficiently high transactions numbers from the pool in +an attempt to limit such occurrences, or simply rely on the server to raise an error when a transaction number is +reused. + +### Why are unacknowledged write concerns unsupported? + +The server does not consider the write concern when deciding if a write operation supports retryable behavior. +Technically, operations with an unacknowledged write concern can specify a transaction ID and be retried. However, the +spec elects not to support unacknowledged write concerns due to various ways that drivers may issue write operations +with unacknowledged write concerns. + +When using `OP_QUERY` to issue a write command to the server, a command response is always returned. A write command +with an unacknowledged write concern (i.e. `{w: 0}`) will return a response of `{ok: 1}`. If a retryable error is +encountered (either a network error or "not writeable primary" response), the driver could attempt to retry the +operation by executing it again with the same transaction ID. + +Some drivers fall back to legacy opcodes (e.g. `OP_INSERT`) to execute write operations with an unacknowledged write +concern. In the future, `OP_MSG` may allow the server to avoid returning any response for write operations sent with an +unacknowledged write concern. In both of these cases, there is no response for which the driver might encounter a +retryable error and decide to retry the operation. + +Rather than depend on an implementation detail to determine if retryable behavior might apply, the spec has chosen to +not support retryable behavior for unacknowledged write concerns and guarantee a consistent user experience across all +drivers. + +### Why must drivers maintain an allow list of supported operations? + +Requiring that drivers maintain an allow list of supported write operations is unfortunate. It both adds complexity to +the driver's implementation and limits the driver's ability to immediately take advantage of new server functionality +(i.e. the driver must be upgraded to support additional write operations). + +Several other alternatives were discussed: + +- The server could inform drivers which write operations support retryable behavior in its `hello` or legacy hello + response. This would be a form of feature discovery, for which there is no established protocol. It would also add + complexity to the connection handshake. +- The server could ignore a transaction ID on the first observed attempt of an unsupported write command and only yield + an error on subsequent attempts. This would require the server to create a transaction record for unsupported writes + to avoid the risk of applying a write twice and ensuring that retry attempts could be differentiated. It also poses + a significant problem for sharding if a multi-document write does not reach all shards, since those shards would not + know to create a transaction record. +- The driver could allow more fine-grained control retryable write behavior by supporting a `retryWrites` option on the + database and collection objects. This would allow users to enable `retryWrites` on a MongoClient and disable it as + needed to execute unsupported write operations, or vice versa. Since we expect the `retryWrites` option to become + less relevant once transactions are implemented, we would prefer not to add the option throughout the driver API. + +### How will users know which operations are supported? + +The initial list of supported operations is already quite permissive. Most [CRUD](../crud/crud.md) operations are +supported apart from `updateMany()`, `deleteMany()`, and `aggregate()` with a write stage (e.g. `$out`, `$merge`). Other +write operations (e.g. `renameCollection`) are rare. + +That said, drivers will need to clearly document exactly which operations support retryable behavior. In the case +`bulkWrite()`, which may or may not support retryability, drivers should discuss how eligibility is determined. + +### Can drivers resend the same wire protocol message on retry attempts? + +Since retry attempts entail sending the same command and transaction ID to the server, drivers might consider resending +the same wire protocol message in order to avoid constructing a new message and computing its checksum. The server will +not complain if it receives two messages with the same `requestId`, as the field is only used for logging and populating +the `responseTo` field in its replies to the client. That said, re-using a wire protocol message might violate rules for +[gossipping the cluster time](../sessions/driver-sessions.md#gossipping-the-cluster-time) and might also have +implications for [Command Monitoring](#command-monitoring), since the original write command and its retry attempt may +report the same `requestId`. + +### Why can't drivers split bulk write commands to maximize retryability? + +In [Supported Write Operations](#supported-write-operations), the spec prohibits drivers from altering existing logic +for splits `bulkWrite()`'s `requests` parameter into write commands in an attempt to segregate unsupported, +multi-document write operations and maximize retryability for other, supported write operations. The reasoning behind +this prohibition is that such behavior would conflict with a primary goal of the bulk API in reducing the number of +command round-trips to the server. + +### retryWrites originally defaulted to false, why does it now default to true? + +Since the initial release of retryable writes in MongoDB 3.6 testing showed that the overhead for supported operations +was sufficiently small that there was no risk in changing the default. Additionally, the fact that some operations +continue to be unsupported for retryable writes (updateMany and deleteMany) does not seem to pose a problem in practice. + +### Why do drivers have to parse errmsg to determine storage engine support? + +There is no reliable way to determine the storage engine in use for shards in a sharded cluster, and replica sets (and +shards) can have mixed deployments using different storage engines on different members. This is especially true when a +replica set or sharded cluster is being upgraded from one storage engine to another. This could be common when upgrading +to MongoDB 4.2, where MMAPv1 is no longer supported. + +The server returns error code 20 (IllegalOperation) when the storage engine doesn't support document-level locking and +txnNumbers. Error code 20 is used for a large number of different error cases in the server so we need some other way to +differentiate this error case from any other. The error code and errmsg are the same in MongoDB 3.6 and 4.0, and the +same from a replica set or sharded cluster (mongos just forwards the error from the shard's replica set). + +### Why does the driver only add the RetryableWriteError label to errors that occur on a MongoClient with retryWrites set to true? + +The driver does this to maintain consistency with the MongoDB server. Servers that support the RetryableWriteError label +(MongoDB version 4.4 and newer) only add the label to an error when the client has added a txnNumber to the command, +which only happens when the retryWrites option is true on the client. For the driver to add the label even if +retryWrites is not true would be inconsistent with the server and potentially confusing to developers. + +## Changelog + +- 2026-02-19: Clarified that server deprioritization on replica sets only occurs for `SystemOverloadedError` errors. + +- 2026-02-11: Clarified that the retry logic and pseudocode does not include the modifications required by client + backpressure. + +- 2026-01-14: Clarify which error to return when more than one error with the `NoWritesPerformed` label is encountered. + +- 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses. + +- 2024-05-08: Add guidance for client-level `bulkWrite()` retryability. + +- 2024-05-02: Migrated from reStructuredText to Markdown. + +- 2024-04-29: Fix the link to the Driver Sessions spec. + +- 2024-01-16: Do not use `writeConcernError.code` in pre-4.4 mongos response to determine retryability. Do not use + `writeErrors[].code` in pre-4.4 server responses to determine retryability. + +- 2023-12-06: Clarify that writes are not retried within transactions. + +- 2023-12-05: Add that any server information associated with retryable exceptions MUST reflect the originating server, + even in the presence of retries. + +- 2023-10-02: When CSOT is not enabled, one retry attempt occurs. + +- 2023-08-26: Require that in a sharded cluster the server on which the operation failed MUST be provided to the server + selection mechanism as a deprioritized server. + +- 2022-11-17: Add logic for persisting "currentError" as "previousError" on first retry attempt, avoiding raising "null" + errors. + +- 2022-11-09: CLAM must apply both events and log messages. + +- 2022-10-18: When CSOT is enabled multiple retry attempts may occur. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-25: Note that drivers should retry handshake network failures. + +- 2021-11-02: Clarify that error labels are only specified in a top-level field of an error. + +- 2021-04-26: Replaced deprecated terminology + +- 2021-03-24: Require that PoolClearedErrors be retried + +- 2020-09-01: State the the driver should only add the RetryableWriteError label to network errors when connected to a + 4.4+ server. + +- 2020-02-25: State that the driver should only add the RetryableWriteError label when retryWrites is on, and make it + clear that mongos will sometimes perform internal retries and not return the RetryableWriteError label. + +- 2020-02-10: Remove redundant content in Tests section. + +- 2020-01-14: Add ExceededTimeLimit to the list of error codes that should receive a RetryableWriteError label. + +- 2019-10-21: Change the definition of "retryable write" to be based on the RetryableWriteError label. Stop requiring + drivers to parse errmsg to categorize retryable errors for pre-4.4 servers. + +- 2019-07-30: Drivers must rewrite error messages for error code 20 when txnNumber is not supported by the storage + engine. + +- 2019-06-07: Mention `$merge` stage for aggregate alongside `$out` + +- 2019-05-29: Renamed InterruptedDueToStepDown to InterruptedDueToReplStateChange + +- 2019-03-06: retryWrites now defaults to true. + +- 2019-03-05: Prohibit resending wire protocol messages if doing so would violate rules for gossipping the cluster time. + +- 2018-06-07: WriteConcernFailed is not a retryable error code. + +- 2018-04-25: Evaluate retryable eligibility of bulkWrite() commands individually. + +- 2018-03-14: Clarify that retryable writes may fail with a FCV 3.4 shard. + +- 2017-11-02: Drivers should not raise errors if selected server does not support retryable writes and instead fall back + to non-retryable behavior. In addition to wire protocol version, drivers may check for + `logicalSessionTimeoutMinutes` to determine if a server supports sessions and retryable writes. + +- 2017-10-26: Errors when retrying may be raised instead of the original error provided they allow the user to infer + that an attempt was made. + +- 2017-10-23: Drivers must document operations that support retryability. + +- 2017-10-23: Raise the original retryable error if server selection or wire protocol checks fail during the retry + attempt. Encourage drivers to provide intermediary write results after an unrecoverable failure during a bulk write. + +- 2017-10-18: Standalone servers do not support retryable writes. + +- 2017-10-18: Also retry writes after a "not writable primary" error. + +- 2017-10-08: Renamed `txnNum` to `txnNumber` and noted that it must be a 64-bit integer (BSON type 0x12). + +- 2017-08-25: Drivers will maintain an allow list so that only supported write operations may be retried. Transaction + IDs will not be included in unsupported write commands, irrespective of the `retryWrites` option. + +- 2017-08-18: `retryWrites` is now a MongoClient option. diff --git a/source/retryable-writes/retryable-writes.rst b/source/retryable-writes/retryable-writes.rst index 7148c0d1a6..9d1c8d2ef5 100644 --- a/source/retryable-writes/retryable-writes.rst +++ b/source/retryable-writes/retryable-writes.rst @@ -1,761 +1,4 @@ -================ -Retryable Writes -================ -:Spec Title: Retryable Writes -:Spec Version: 1.0 -:Author: Jeremy Mikola -:Lead: \A. Jesse Jiryu Davis -:Advisors: Robert Stam, Esha Maharishi, Samantha Ritter, and Kaloian Manassiev -:Status: Accepted -:Type: Standards -:Minimum Server Version: 3.6 -:Last Modified: 2018-04-25 - -.. contents:: - --------- - -Abstract -======== - -MongoDB 3.6 will implement support for server sessions, which are shared -resources within a cluster identified by a session ID. Drivers compatible with -MongoDB 3.6 will also implement support for client sessions, which are always -associated with a server session and will allow for certain commands to be -executed within the context of a server session. - -Additionally, MongoDB 3.6 will utilize server sessions to allow some write -commands to specify a transaction ID to enforce at-most-once semantics for the -write operation(s) and allow for retrying the operation if the driver fails to -obtain a write result (e.g. network error or "not master" error after a replica -set failover). This specification will outline how an API for retryable write -operations will be implemented in drivers. The specification will define an -option to enable retryable writes for an application and describe how a -transaction ID will be provided to write commands executed therein. - -META -==== - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119 `_. - -Specification -============= - -Terms ------ - -Transaction ID - The transaction ID identifies the transaction as part of which the command is - running. In a write command where the client has requested retryable - behavior, it is expressed by the top-level ``lsid`` and ``txnNumber`` fields. - The ``lsid`` component is the corresponding server session ID. which is a - BSON value defined in the `Driver Session`_ specification. The ``txnNumber`` - component is a monotonically increasing (per server session), positive 64-bit - integer. - - .. _Driver Session: ../sessions/driver-sessions.rst - -ClientSession - Driver object representing a client session, which is defined in the - `Driver Session`_ specification. This object is always associated with a - server session; however, drivers will pool server sessions so that creating a - ClientSession will not always entail creation of a new server session. The - name of this object MAY vary across drivers. - -Retryable Error - An error is considered retryable if it meets any of the following criteria: - - - any network exception (e.g. socket timeout or error) - - - a server error response with any the following codes: - - .. list-table:: - :header-rows: 1 - - * - Error Name - - Error Code - * - InterruptedAtShutdown - - 11600 - * - InterruptedDueToReplStateChange - - 11602 - * - NotMaster - - 10107 - * - NotMasterNoSlaveOk - - 13435 - * - NotMasterOrSecondary - - 13436 - * - PrimarySteppedDown - - 189 - * - ShutdownInProgress - - 91 - * - HostNotFound - - 7 - * - HostUnreachable - - 6 - * - NetworkTimeout - - 89 - * - SocketException - - 9001 - - - a server error response without an error code or one different from those - listed above, but with an error message containing the substring "not - master" or "node is recovering" - - - a server response with a write concern error meeting the above criteria - - The criteria for retryable errors is similar to the discussion in the SDAM - spec's section on `Error Handling`_, but includes additional error codes. See - `What do the additional error codes mean?`_ for the reasoning behind these - additional errors. - - .. _Error Handling: ../server-discovery-and-monitoring/server-discovery-and-monitoring.rst#error-handling - -Additional terms may be defined in the `Driver Session`_ specification. - -Naming Deviations ------------------ - -This specification defines the name for a new MongoClient option, -``retryWrites``. Drivers MUST use the defined name for the connection string -parameter to ensure portability of connection strings across applications and -drivers. - -If drivers solicit MongoClient options through another mechanism (e.g. options -dictionary provided to the MongoClient constructor), drivers SHOULD use the -defined name but MAY deviate to comply with their existing conventions. For -example, a driver may use ``retry_writes`` instead of ``retryWrites``. - -For any other names in the spec, drivers SHOULD use the defined name but MAY -deviate to comply with their existing conventions. - -MongoClient Configuration -------------------------- - -This specification introduces the following client-level configuration option. - -retryWrites -~~~~~~~~~~~ - -This boolean option determines whether retryable behavior will be applied to all -supported write operations executed within the MongoClient. This option MUST -default to false, which implies no change in write behavior. - -This option MUST NOT be configurable at the level of a database object, -collection object, or at the level of an individual write operation. - -Requirements for Retryable Writes ---------------------------------- - -Supported Server Versions -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Like sessions, retryable writes require a MongoDB 3.6 replica set or shard -cluster operating with feature compatibility version 3.6 (i.e. the -``{setFeatureCompatibilityVersion: 3.6}`` administrative command has been run on -the cluster). Drivers SHOULD verify server eligibility by ensuring that -``maxWireVersion`` is at least six, the ``logicalSessionTimeoutMinutes`` -field is present in the server's ``isMaster`` response, and the server type is -not standalone. - -If the server selected for the first attempt of a retryable write operation does -not support retryable writes, drivers MUST execute the write as if retryable -writes were not enabled. Drivers MUST NOT include a transaction ID in the write -command and MUST not retry the command under any circumstances. - -In a sharded cluster, it is possible that mongos may appear to support retryable -writes but one or more shards in the cluster do not (e.g. replica set shard is -configured with feature compatibility version 3.4, a standalone is added as a -new shard). In these rare cases, a write command that fans out to a shard that -does not support retryable writes may partially fail and an error may be -reported in the write result from mongos (e.g. ``writeErrors`` array in the bulk -write result). This does not constitute a retryable error. Drivers MUST relay -such errors to the user. - -Supported Write Operations -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -MongoDB 3.6 will support retryability for some, but not all, write operations. - -Supported single-statement write operations include ``insertOne()``, -``updateOne()``, ``replaceOne()``, ``deleteOne()``, ``findOneAndDelete()``, -``findOneAndReplace()``, and ``findOneAndUpdate()``. - -Supported multi-statement write operations include ``insertMany()`` and -``bulkWrite()``. The ordered option may be ``true`` or ``false``. In the case of -``bulkWrite()``, ``UpdateMany`` or ``DeleteMany`` operations within the -``requests`` parameter may make some write commands ineligible for retryability. -Drivers MUST evaluate eligibility for each write command sent as part of the -``bulkWrite()`` (after order and batch splitting) individually. Drivers MUST NOT -alter existing logic for order and batch splitting in an attempt to maximize -retryability for operations within a bulk write. - -These methods above are defined in the `CRUD`_ specification. - -Later versions of MongoDB may add support for additional write operations. - -Drivers MUST document operations that support retryable behavior and the -conditions for which retryability is determined (see: -`How will users know which operations are supported?`_). Drivers are not -required to exhaustively document all operations that do not support retryable -behavior. - -Unsupported Write Operations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Write commands specifying an unacknowledged write concern (e.g. ``{w: 0})``) do -not support retryable behavior. Drivers MUST NOT add a transaction ID to any -write command with an unacknowledged write concern executed within a MongoClient -where retryable writes have been enabled. Drivers MUST NOT retry these commands. - -Write commands where a single statement might affect multiple documents will not -be initially supported by MongoDB 3.6, although this may change in the future. -This includes an `update`_ command where any statement in the updates sequence -specifies a ``multi`` option of ``true`` or a `delete`_ command where any -statement in the ``deletes`` sequence specifies a ``limit`` option of ``0``. In -the context of the `CRUD`_ specification, this includes the ``updateMany()`` and -``deleteMany()`` methods and, in some cases, ``bulkWrite()``. Drivers MUST NOT -add a transaction ID to any single- or multi-statement write commands that -include one or more multi-document write operations. Drivers MUST NOT retry -these commands if they fail to return a response. With regard to -``bulkWrite()``, drivers MUST evaluate eligibility for each write command sent -as part of the ``bulkWrite()`` (after order and batch splitting) individually. - -.. _update: https://docs.mongodb.com/manual/reference/command/update/ -.. _delete: https://docs.mongodb.com/manual/reference/command/delete/ - -Write commands other than `insert`_, `update`_, `delete`_, or `findAndModify`_ -will not be initially supported by MongoDB 3.6, although this may change in the -future. This includes, but is not limited to, an `aggregate`_ command using the -``$out`` pipeline operator. Drivers MUST NOT add a transaction ID to these -commands and MUST NOT retry these commands if they fail to return a response. - -.. _insert: https://docs.mongodb.com/manual/reference/command/insert/ -.. _findAndModify: https://docs.mongodb.com/manual/reference/command/findAndModify/ -.. _aggregate: https://docs.mongodb.com/manual/reference/command/aggregate/ - -Implementing Retryable Writes ------------------------------ - -Generating Transaction IDs -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The server requires each retryable write operation to provide a unique -transaction ID in its command document. The transaction ID consists of a server -session ID and a monotonically increasing transaction number. The session ID is -obtained from the ClientSession object, which will have either been passed to -the write operation from the application or constructed internally for the -operation. Drivers will be responsible for maintaining a monotonically -increasing transaction number for each server session used by a ClientSession -object. Drivers that pool server sessions MUST preserve the transaction number -when reusing a server session from the pool with a new ClientSession (this can -be tracked as another property on the driver's object for the server session). - -Drivers MUST ensure that each retryable write command specifies a transaction -number larger than any previously used transaction number for its session ID. - -Since ClientSession objects are not thread safe and may only be used by one -thread at a time, drivers should not need to worry about race conditions when -incrementing the transaction number. - -Behavioral Changes for Write Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MUST automatically add a transaction ID to all supported write commands -executed via a specific `CRUD`_ method (e.g. ``updateOne()``) or write command -method (e.g. ``executeWriteCommand()``) within a MongoClient where retryable -writes have been enabled and when the selected server supports retryable writes. - -.. _CRUD: ../crud/crud.rst - -If your driver offers a generic command method on your database object (e.g. -``runCommand()``), it MUST NOT check the user's command document to determine if -it is a supported write operation and MUST NOT automatically add a transaction -ID. The method should send the user's command document to the server as-is. - -This specification does not affect write commands executed within a MongoClient -where retryable writes have not been enabled. - -Constructing Write Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When constructing a supported write command that will be executed within a -MongoClient where retryable writes have been enabled, drivers MUST increment the -transaction number for the corresponding server session and include the server -session ID and transaction number in top-level ``lsid`` and ``txnNumber`` -fields, respectively. ``lsid`` is a BSON value (discussed in the -`Driver Session`_ specification). ``txnNumber`` MUST be a positive 64-bit -integer (BSON type 0x12). - -The following example illustrates a possible write command for an -``updateOne()`` operation: - -.. code:: typescript - - { - update: "coll", - lsid: { ... }, - txnNumber: 100, - updates: [ - { q: { x: 1 }, u: { $inc: { y: 1 } }, multi: false, upsert: false }, - ], - ordered: true - } - -When constructing multiple write commands for a multi-statement write operation -(i.e. ``insertMany()`` and ``bulkWrite()``), drivers MUST increment the -transaction number for each supported write command in the batch. - -Executing Retryable Write Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When selecting a writable server for the first attempt of a retryable write -command, drivers MUST allow a server selection error to propagate. In this case, -the caller is able to infer that no attempt was made. - -If retryable writes is not enabled or the selected server does not support -retryable writes, drivers MUST NOT include a transaction ID in the command and -MUST attempt to execute the write command exactly once and allow any errors to -propagate. In this case, the caller is able to infer that an attempt was made. - -If retryable writes are enabled and the selected server supports retryable -writes, drivers MUST add a transaction ID to the command. Drivers MUST only -attempt to retry a write command if the first attempt yields a retryable error. -Drivers MUST NOT attempt to retry a write command on any other error. - -If the first attempt of a write command including a transaction ID encounters a -retryable error, the driver MUST update its topology according to the SDAM spec -(see: `Error Handling`_) and capture this original retryable error. Drivers -should then proceed with selecting a writable server for the retry attempt. - -.. _Error Handling: ../server-discovery-and-monitoring/server-discovery-and-monitoring.rst#error-handling - -If the driver cannot select a server for the retry attempt or the selected -server does not support retryable writes, retrying is not possible and drivers -MUST raise the original retryable error. In both cases, the caller is able to -infer that an attempt was made. - -If the retry attempt also fails, drivers MUST update their topology according to -the SDAM spec (see: `Error Handling`_). If an error would not allow the caller -to infer that an attempt was made (e.g. connection pool exception originating -from the driver), the original error should be raised. If the retry failed due -to another retryable error or some other error originating from the server, that -error should be raised instead as the caller can infer that an attempt was made -and the second error is likely more relevant (with respect to the current -topology state). - -Drivers MUST NOT attempt to retry a write command with the same transaction ID -more than once. - -Consider the following pseudo-code: - -.. code:: typescript - - /** - * Checks if a server supports retryable writes. - */ - function isRetryableWritesSupported(server) { - if (server.getMaxWireVersion() < RETRYABLE_WIRE_VERSION) { - return false; - } - - if ( ! server.hasLogicalSessionTimeoutMinutes()) { - return false; - } - - if (server.isStandalone()) { - return false; - } - - return true; - } - - /** - * Executes a write command in the context of a MongoClient where retryable - * writes have been enabled. The session parameter may be an implicit or - * explicit client session (depending on how the CRUD method was invoked). - */ - function executeRetryableWrite(command, session) { - /* Allow ServerSelectionException to propagate to our caller, which can then - * assume that no attempts were made. */ - server = selectServer("writable"); - - /* If the server does not support retryable writes, execute the write as if - * retryable writes are not enabled. */ - if ( ! isRetryableWritesSupported()) { - return executeCommand(server, command); - } - - /* Incorporate lsid and txnNumber fields into the command document. These - * values will be derived from the implicit or explicit session object. */ - retryableCommand = addTransactionIdToCommand(command, session); - - /* NetworkException and NotMasterException are both retryable errors. If - * caught, remember the exception, update SDAM accordingly, and proceed with - * retrying the operation. */ - try { - return executeCommand(server, retryableCommand); - } catch (NetworkException originalError) { - updateTopologyDescriptionForNetworkError(server, originalError); - } catch (NotMasterException originalError) { - updateTopologyDescriptionForNotMasterError(server, originalError); - } - - /* If we cannot select a writable server, do not proceed with retrying and - * throw the original error. The caller can then infer that an attempt was - * made and failed. */ - try { - server = selectServer("writable"); - } catch (Exception ignoredError) { - throw originalError; - } - - /* If the server selected for retrying is too old, throw the original error. - * The caller can then infer that an attempt was made and failed. This case - * is very rare, and likely means that the cluster is in the midst of a - * downgrade. */ - if ( ! isRetryableWritesSupported()) { - throw originalError; - } - - /* Allow any retryable error from the second attempt to propagate to our - * caller, as it will be just as relevant (if not more relevant) than the - * original error. For exceptions that originate from the driver (e.g. no - * socket available from the connection pool), we should raise the original - * error. Other exceptions originating from the server should be allowed to - * propagate. */ - try { - return executeCommand(server, retryableCommand); - } catch (NetworkException secondError) { - updateTopologyDescriptionForNetworkError(server, secondError); - throw secondError; - } catch (NotMasterException secondError) { - updateTopologyDescriptionForNotMasterError(server, secondError); - throw secondError; - } catch (DriverException ignoredError) { - throw originalError; - } - } - -When retrying a write command, drivers MUST resend the command with the same -transaction ID. Drivers MAY resend the original wire protocol message (see: -`Can drivers resend the same wire protocol message on retry attempts?`_). - -In the case of a multi-statement write operation split across multiple write -commands, a failed retry attempt will also interrupt execution of any additional -write operations in the batch (regardless of the ordered option). This is no -different than if a retryable error had been encountered without retryable -behavior enabled or supported by the driver. Drivers are encouraged to provide -access to an intermediary write result (e.g. BulkWriteResult, InsertManyResult) -through the BulkWriteException, in accordance with the `CRUD`_ specification. - -Logging Retry Attempts -====================== - -Drivers MAY choose to log retry attempts for write operations. This -specification does not define a format for such log messages. - -Command Monitoring -================== - -In accordance with the `Command Monitoring`_ specification, drivers MUST -guarantee that each ``CommandStartedEvent`` has either a correlating -``CommandSucceededEvent`` or ``CommandFailedEvent``. If the first attempt of a -retryable write operation encounters a retryable error, drivers MUST fire a -``CommandFailedEvent`` for the retryable error and fire a separate -``CommandStartedEvent`` when executing the subsequent retry attempt. Note that -the second ``CommandStartedEvent`` may have a different ``connectionId``, since -a writable server is reselected for the retry attempt. - -.. _Command Monitoring: ../command-monitoring/command-monitoring.rst - -Each attempt of a retryable write operation SHOULD report a different -``requestId`` so that events for each attempt can be properly correlated with -one another. - -The `Command Monitoring`_ specification states that the ``operationId`` field is -a driver-generated, 64-bit integer and may be "used to link events together such -as bulk write operations." Each attempt of a retryable write operation SHOULD -report the same ``operationId``; however, drivers SHOULD NOT use the -``operationId`` field to relay information about a transaction ID. A bulk write -operation may consist of multiple write commands, each of which may specify a -unique transaction ID. - -Test Plan -========= - -See the `README `_ for tests. - -At a high level, the test plan will cover the following scenarios for executing -supported write operations within a MongoClient where retryable writes have been -enabled: - -* Executing the same write operation (and transaction ID) multiple times should - yield an identical write result. -* Test at-most-once behavior by observing that subsequent executions of the same - write operation do not incur further modifications to the collection data. -* Exercise supported single-statement write operations (i.e. deleteOne, - insertOne, replaceOne, updateOne, and findAndModify). -* Exercise supported multi-statement insertMany and bulkWrite operations, which - contain only supported single-statement write operations. Both ordered and - unordered execution should be tested. - -If possible, drivers should test that transaction IDs are never included in -commands for unsupported write operations: - -* Write commands with unacknowledged write concerns (e.g. ``{w: 0}``) - -* Unsupported single-statement write operations - - - ``updateMany()`` - - ``deleteMany()`` - -* Unsupported multi-statement write operations - - - commands that include ``UpdateMany`` or ``DeleteMany`` executed as part of a - ``bulkWrite()`` - -* Unsupported write commands - - - ``aggregate`` with ``$out`` pipeline operator - -Drivers may also be able to verify at-most-once semantics as described above by -testing their internal implementation (e.g. checking that transaction IDs are -added to outgoing commands). - -Motivation for Change -===================== - -Drivers currently have no API for specifying at-most-once semantics and -retryable behavior for write operations. The driver API needs to be extended to -support this behavior. - -Design Rationale -================ - -The design of this specification piggy-backs that of the `Driver Session`_ -specification in that it modifies the driver API as little as possible to -introduce the concept of at-most-once semantics and retryable behavior for write -operations. A transaction ID will be included in all supported write commands -executed within the scope of a MongoClient where retryable writes have been -enabled. - -Drivers expect the server to yield an error if a transaction ID is included in -an unsupported write command. This requires drivers to maintain a whitelist and -track which write operations support retryable behavior for a given server -version (see: `Why must drivers maintain a whitelist of supported -operations?`_). - -While this approach will allow applications to take advantage of retryable write -behavior with minimal code changes, it also presents a documentation challenge. -Users must understand exactly what can and will be retried (see: `How will users -know which operations are supported?`_). - -Backwards Compatibility -======================= - -The API changes to support retryable writes extend the existing API but do not -introduce any backward breaking changes. Existing programs that do not make use -of retryable writes will continue to compile and run correctly. - -Reference Implementation -======================== - -The C# and C drivers will provide reference implementations. JIRA links will be -added here at a later point. - -Future Work -=========== - -Supporting at-most-once semantics and retryable behavior for updateMany and -deleteMany operations may become possible once the server implements support for -multi-document transactions. - -A separate specification for retryable read operations could complement this -specification. Retrying read operations would not require client or server -sessions and could be implemented independently of retryable writes. - -Q & A -===== - -What do the additional error codes mean? ----------------------------------------- - -The errors `HostNotFound`, `HostUnreachable`, `NetworkTimeout`, -`SocketException` may be returned from mongos during problems routing to a -shard. These may be transient, or localized to that mongos. - -Why are write operations only retried once? -------------------------------------------- - -The spec concerns itself with retrying write operations that encounter a -retryable error (i.e. no response due to network error or a response indicating -that the node is no longer a primary). A retryable error may be classified as -either a transient error (e.g. dropped connection, replica set failover) or -persistent outage. In the case of a transient error, the driver will mark the -server as "unknown" per the `SDAM`_ spec. A subsequent retry attempt will allow -the driver to rediscover the primary within the designated server selection -timeout period (30 seconds by default). If server selection times out during -this retry attempt, we can reasonably assume that there is a persistent outage. -In the case of a persistent outage, multiple retry attempts are fruitless and -would waste time. See `How To Write Resilient MongoDB Applications`_ for -additional discussion on this strategy. - -.. _SDAM: ../server-discovery-and-monitoring/server-discovery-and-monitoring.rst -.. _How To Write Resilient MongoDB Applications: https://emptysqua.re/blog/how-to-write-resilient-mongodb-applications/ - -What if the transaction number overflows? ------------------------------------------ - -Since server sessions are pooled and session lifetimes are configurable on -the server, it is theoretically possible for the transaction number to overflow -if it reaches the limits of a signed 64-bit integer. The spec does not address -this scenario. Drivers may decide to handle this as they wish. For example, they -may raise a client-side error if a transaction number would overflow, eagerly -remove sessions with sufficiently high transactions numbers from the pool in an -attempt to limit such occurrences, or simply rely on the server to raise an -error when a transaction number is reused. - -Why are unacknowledged write concerns unsupported? --------------------------------------------------- - -The server does not consider the write concern when deciding if a write -operation supports retryable behavior. Technically, operations with an -unacknowledged write concern can specify a transaction ID and be retried. -However, the spec elects not to support unacknowledged write concerns due to -various ways that drivers may issue write operations with unacknowledged write -concerns. - -When using ``OP_QUERY`` to issue a write command to the server, a command -response is always returned. A write command with an unacknowledged write -concern (i.e. ``{w: 0}``) will return a response of ``{ok: 1}``. If a retryable -error is encountered (either a network error or "not master" response), the -driver could attempt to retry the operation by executing it again with the same -transaction ID. - -Some drivers fall back to legacy opcodes (e.g. ``OP_INSERT``) to execute write -operations with an unacknowledged write concern. In the future, ``OP_MSG`` may -allow the server to avoid returning any response for write operations sent with -an unacknowledged write concern. In both of these cases, there is no response -for which the driver might encounter a retryable error and decide to retry the -operation. - -Rather than depend on an implementation detail to determine if retryable -behavior might apply, the spec has chosen to not support retryable behavior -for unacknowledged write concerns and guarantee a consistent user experience -across all drivers. - -Why must drivers maintain a whitelist of supported operations? --------------------------------------------------------------- - -Requiring that drivers maintain a whitelist of supported write operations is -unfortunate. It both adds complexity to the driver's implementation and limits -the driver's ability to immediately take advantage of new server functionality -(i.e. the driver must be upgraded to support additional write operations). - -Several other alternatives were discussed: - -* The server could inform drivers which write operations support retryable - behavior in its ``isMaster`` response. This would be a form of feature - discovery, for which there is no established protocol. It would also add - complexity to the connection handshake. -* The server could ignore a transaction ID on the first observed attempt of an - unsupported write command and only yield an error on subsequent attempts. This - would require the server to create a transaction record for unsupported writes - to avoid the risk of applying a write twice and ensuring that retry attempts - could be differentiated. It also poses a significant problem for sharding if a - multi-document write does not reach all shards, since those shards would not - know to create a transaction record. -* The driver could allow more fine-grained control retryable write behavior by - supporting a ``retryWrites`` option on the database and collection objects. - This would allow users to enable ``retryWrites`` on a MongoClient and disable - it as needed to execute unsupported write operations, or vice versa. Since we - expect the ``retryWrites`` option to become less relevant once transactions - are implemented, we would prefer not to add the option throughout the driver - API. - -How will users know which operations are supported? ---------------------------------------------------- - -The initial list of supported operations is already quite permissive. Most -`CRUD`_ operations are supported apart from ``updateMany()``, ``deleteMany()``, -and ``aggregate()`` with ``$out``. Other write operations -(e.g. ``renameCollection``) are rare. - -That said, drivers will need to clearly document exactly which operations -support retryable behavior. In the case ``bulkWrite()``, which may or may not -support retryability, drivers should discuss how elegibility is determined. - -Why does the retryWrites MongoClient option default to false? -------------------------------------------------------------- - -Retryable write operations are a first step towards the server supporting -transactions and multi-document writes. MongoDB 3.6 lacks support for retrying -some `CRUD`_ operations, such as ``updateMany()`` and ``deleteMany()``. -Additionally, write commands other than ``insert``, ``update``, ``delete``, and -``findAndModify`` are not supported at all. - -Enabling retryability for write operations does incur some server-side overhead. -As such, it would be prudent not to enable this feature for all applications by -default and instead have applications opt in to the behavior. We may change this -default in the future if testing reveals the overhead to be sufficiently small. - -Can drivers resend the same wire protocol message on retry attempts? --------------------------------------------------------------------- - -Since retry attempts entail sending the same command and transaction ID to the -server, drivers may opt to resend the same wire protocol message in order to -avoid constructing a new message and computing its checksum. The server will not -complain if it receives two messages with the same ``requestId``, as the field -is only used for logging and populating the ``responseTo`` field in its replies -to the client. That said, this approach may have implications for -`Command Monitoring`_, since the original write command and its retry attempt -may report the same ``requestId``. - -Why can't drivers split bulk write commands to maximize retryability? ---------------------------------------------------------------------- - -In `Supported Write Operations`_, the spec prohibits drivers from altering -existing logic for splits ``bulkWrite()``'s ``requests`` parameter into write -commands in an attempt to segregate unsupported, multi-document write operations -and maximize retryability for other, supported write operations. The reasoning -behind this prohibition is that such behavior would conflict with a primary goal -of the bulk API in reducing the number of command round-trips to the server. - -Changes -======= - -2018-06-07: WriteConcernFailed is not a retryable error code. - -2018-04-25: Evaluate retryable eligibility of bulkWrite() commands individually. - -2018-03-14: Clarify that retryable writes may fail with a FCV 3.4 shard. - -2017-11-02: Drivers should not raise errors if selected server does not support -retryable writes and instead fall back to non-retryable behavior. In addition to -wire protocol version, drivers may check for ``logicalSessionTimeoutMinutes`` to -determine if a server supports sessions and retryable writes. - -2017-10-26: Errors when retrying may be raised instead of the original error -provided they allow the user to infer that an attempt was made. - -2017-10-23: Drivers must document operations that support retryability. - -2017-10-23: Raise the original retryable error if server selection or wire -protocol checks fail during the retry attempt. Encourage drivers to provide -intermediary write results after an unrecoverable failure during a bulk write. - -2017-10-18: Standalone servers do not support retryable writes. - -2017-10-18: Also retry writes after a "not master" error. - -2017-10-08: Renamed ``txnNum`` to ``txnNumber`` and noted that it must be a -64-bit integer (BSON type 0x12). - -2017-08-25: Drivers will maintain a whitelist so that only supported write -operations may be retried. Transaction IDs will not be included in unsupported -write commands, irrespective of the ``retryWrites`` option. - -2017-08-18: ``retryWrites`` is now a MongoClient option. +.. note:: + This specification has been converted to Markdown and renamed to + `retryable-writes.md `_. diff --git a/source/retryable-writes/tests/README.md b/source/retryable-writes/tests/README.md new file mode 100644 index 0000000000..c78539c5f0 --- /dev/null +++ b/source/retryable-writes/tests/README.md @@ -0,0 +1,566 @@ +# Retryable Write Tests + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +retryable writes. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +Tests will require a MongoClient created with options defined in the tests. Integration tests will require a running +MongoDB cluster with server versions 3.6.0 or later. The `{setFeatureCompatibilityVersion: 3.6}` admin command will also +need to have been executed to enable support for retryable writes on the cluster. Some tests may have more stringent +version requirements depending on the fail points used. + +## Use as Integration Tests + +Integration tests are expressed in YAML and can be run against a replica set or sharded cluster as denoted by the +top-level `runOn` field. Tests that rely on the `onPrimaryTransactionalWrite` fail point cannot be run against a sharded +cluster because the fail point is not supported by mongos. + +The tests exercise the following scenarios: + +- Single-statement write operations + - Each test expecting a write result will encounter at-most one network error for the write command. Retry attempts + should return without error and allow operation to succeed. Observation of the collection state will assert that + the write occurred at-most once. + - Each test expecting an error will encounter successive network errors for the write command. Observation of the + collection state will assert that the write was never committed on the server. +- Multi-statement write operations + - Each test expecting a write result will encounter at-most one network error for some write command(s) in the batch. + Retry attempts should return without error and allow the batch to ultimately succeed. Observation of the + collection state will assert that each write occurred at-most once. + - Each test expecting an error will encounter successive network errors for some write command in the batch. The batch + will ultimately fail with an error, but observation of the collection state will assert that the failing write was + never committed on the server. We may observe that earlier writes in the batch occurred at-most once. + +We cannot test a scenario where the first and second attempts both encounter network errors but the write does actually +commit during one of those attempts. This is because (1) the fail point only triggers when a write would be committed +and (2) the skip and times options are mutually exclusive. That said, such a test would mainly assert the server's +correctness for at-most once semantics and is not essential to assert driver correctness. + +## Split Batch Tests + +The YAML tests specify bulk write operations that are split by command type (e.g. sequence of insert, update, and delete +commands). Multi-statement write operations may also be split due to `maxWriteBatchSize`, `maxBsonObjectSize`, or +`maxMessageSizeBytes`. + +For instance, an insertMany operation with five 10 MiB documents executed using OP_MSG payload type 0 (i.e. entire +command in one document) would be split into five insert commands in order to respect the 16 MiB `maxBsonObjectSize` +limit. The same insertMany operation executed using OP_MSG payload type 1 (i.e. command arguments pulled out into a +separate payload vector) would be split into two insert commands in order to respect the 48 MB `maxMessageSizeBytes` +limit. + +Noting when a driver might split operations, the `onPrimaryTransactionalWrite` fail point's `skip` option may be used to +control when the fail point first triggers. Once triggered, the fail point will transition to the `alwaysOn` state until +disabled. Driver authors should also note that the server attempts to process all documents in a single insert command +within a single commit (i.e. one insert command with five documents may only trigger the fail point once). This behavior +is unique to insert commands (each statement in an update and delete command is processed independently). + +If testing an insert that is split into two commands, a `skip` of one will allow the fail point to trigger on the second +insert command (because all documents in the first command will be processed in the same commit). When testing an update +or delete that is split into two commands, the `skip` should be set to the number of statements in the first command to +allow the fail point to trigger on the second command. + +## ~~Command Construction Tests~~ + +The command construction prose tests have been removed in favor of command event assertions in the unified format tests. + +## Prose Tests + +The following tests ensure that retryable writes work properly with replica sets and sharded clusters. + +### 1. Test that retryable writes raise an exception when using the MMAPv1 storage engine. + +For this test, execute a write operation, such as `insertOne`, which should generate an exception. Assert that the error +message is the replacement error message: + +```text +This MongoDB deployment does not support retryable writes. Please add +retryWrites=false to your connection string. +``` + +and the error code is 20. + +> [!NOTE] +> Drivers that rely on `serverStatus` to determine the storage engine in use MAY skip this test for sharded clusters, +> since `mongos` does not report this information in its `serverStatus` response. + +### 2. Test that drivers properly retry after encountering PoolClearedErrors. + +This test MUST be implemented by any driver that implements the CMAP specification. + +This test requires MongoDB 4.3.4+ for both the `errorLabels` and `blockConnection` fail point options. + +1. Create a client with maxPoolSize=1 and retryWrites=true. If testing against a sharded deployment, be sure to connect + to only a single mongos. + +2. Enable the following failpoint: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 91, + blockConnection: true, + blockTimeMS: 1000, + errorLabels: ["RetryableWriteError"] + } + } + ``` + +3. Start two threads and attempt to perform an `insertOne` simultaneously on both. + +4. Verify that both `insertOne` attempts succeed. + +5. Via CMAP monitoring, assert that the first check out succeeds. + +6. Via CMAP monitoring, assert that a PoolClearedEvent is then emitted. + +7. Via CMAP monitoring, assert that the second check out then fails due to a connection error. + +8. Via Command Monitoring, assert that exactly three `insert` CommandStartedEvents were observed in total. + +9. Disable the failpoint. + +### 3. Test that drivers return the original error after encountering a WriteConcernError with a RetryableWriteError label. + +This test MUST: + +- be implemented by any driver that implements the Command Monitoring specification, +- only run against replica sets as mongos does not propagate the NoWritesPerformed label to the drivers. +- be run against server versions 6.0 and above. + +Additionally, this test requires drivers to set a fail point after an `insertOne` operation but before the subsequent +retry. Drivers that are unable to set a failCommand after the CommandSucceededEvent SHOULD use mocking or write a unit +test to cover the same sequence of events. + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress): + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableWriteError"], + writeConcernError: { code: 91 } + } + } + ``` + +3. Via the command monitoring CommandSucceededEvent, configure a fail point with error code `10107` (NotWritablePrimary) + and a NoWritesPerformed label: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorCode: 10107, + errorLabels: ["RetryableWriteError", "NoWritesPerformed"] + } + } + ``` + + Drivers SHOULD only configure the `10107` fail point command if the the succeeded event is for the `91` error + configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. For the resulting error, assert that + the associated error code is `91`. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +### 4. Test that in a sharded cluster writes are retried on a different mongos when one is available. + +This test MUST be executed against a sharded cluster that has at least two mongos instances, supports +`retryWrites=true`, has enabled the `configureFailPoint` command, and supports the `errorLabels` field (MongoDB 4.3.1+). + +> [!NOTE] +> This test cannot reliably distinguish "retry on a different mongos due to server deprioritization" (the behavior +> intended to be tested) from "retry on a different mongos due to normal SDAM randomized suitable server selection". +> Verify relevant code paths are correctly executed by the tests using external means such as a logging, debugger, code +> coverage tool, etc. + +1. Create two clients `s0` and `s1` that each connect to a single mongos from the sharded cluster. They must not connect + to the same mongos. + +2. Configure the following fail point for both `s0` and `s1`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 6, + errorLabels: ["RetryableWriteError"] + } + } + ``` + +3. Create a client `client` with `retryWrites=true` that connects to the cluster using the same two mongoses as `s0` and + `s1`. + +4. Enable failed command event monitoring for `client`. + +5. Execute an `insert` command with `client`. Assert that the command failed. + +6. Assert that two failed command events occurred. Assert that the failed command events occurred on different mongoses. + +7. Disable the fail points on both `s0` and `s1`. + +### 5. Test that in a sharded cluster writes are retried on the same mongos when no others are available. + +This test MUST be executed against a sharded cluster that supports `retryWrites=true`, has enabled the +`configureFailPoint` command, and supports the `errorLabels` field (MongoDB 4.3.1+). + +Note: this test cannot reliably distinguish "retry on a different mongos due to server deprioritization" (the behavior +intended to be tested) from "retry on a different mongos due to normal SDAM behavior of randomized suitable server +selection". Verify relevant code paths are correctly executed by the tests using external means such as a logging, +debugger, code coverage tool, etc. + +1. Create a client `s0` that connects to a single mongos from the cluster. + +2. Configure the following fail point for `s0`: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 1 }, + data: { + failCommands: ["insert"], + errorCode: 6, + errorLabels: ["RetryableWriteError"], + closeConnection: true + } + } + ``` + +3. Create a client `client` with `directConnection=false` (when not set by default) and `retryWrites=true` that connects + to the cluster using the same single mongos as `s0`. + +4. Enable succeeded and failed command event monitoring for `client`. + +5. Execute an `insert` command with `client`. Assert that the command succeeded. + +6. Assert that exactly one failed command event and one succeeded command event occurred. Assert that both events + occurred on the same mongos. + +7. Disable the fail point on `s0`. + +### 6. Test error propagation after encountering multiple errors. + +These tests MUST: + +- be implemented by any driver that implements the Command Monitoring specification. +- only run against replica sets as mongos does not propagate the NoWritesPerformed label to the drivers. +- be run against server versions 6.0 and above. +- be implemented by any driver that has implemented the Client Backpressure specification. + +Additionally, this test requires drivers to set a fail point after an `insertOne` operation but before the subsequent +retry. Drivers that are unable to set a failCommand after the CommandFailedEvent SHOULD use mocking or write a unit test +to cover the same sequence of events. + +#### Case 1: Test that drivers return the correct error when receiving only errors without `NoWritesPerformed` + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `10107` (NotWritablePrimary): + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["insert"], + errorCode: 10107, + errorLabels: ["RetryableError", "SystemOverloadedError"] + } + } + ``` + + Configure the `10107` fail point command only if the the failed event is for the `91` error configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a + server error. Assert that the error code of the server error is `10107`. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +#### Case 2: Test that drivers return the correct error when receiving only errors with `NoWritesPerformed` + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError`, `SystemOverloadedError`, + and `NoWritesPerformed` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `10107` (NotWritablePrimary) + and a NoWritesPerformed label: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["insert"], + errorCode: 10107, + errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"] + } + } + ``` + + Configure the `10107` fail point command only if the the failed event is for the `91` error configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a + server error. Assert that the error code of the server error is 91. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +#### Case 3: Test that drivers return the correct error when receiving some errors with `NoWritesPerformed` and some without `NoWritesPerformed` + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `91` (ShutdownInProgress) and + the `NoWritesPerformed`, `RetryableError` and `SystemOverloadedError` labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"], + errorCode: 91 + } + } + ``` + + Configure the second fail point command only if the failed event is for the first error configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a + server error. Assert that the error code of the server error is 91. Assert that the error does not contain the + error label `NoWritesPerformed`. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +#### Case 4: Test that drivers set the maximum number of retries for all retryable write errors when an overload error is encountered + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `91` (ShutdownInProgress) and + the `RetryableWriteError` and `RetryableError` labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "RetryableWriteError"], + errorCode: 91 + } + } + ``` + + Configure the second fail point command only if the failed event is for the first error configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a + server error. Assert that `MAX_RETRIES + 1` attempts were made. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +#### Case 5: Test that drivers do not apply backoff to non-overload errors + +1. Create a client with `retryWrites=true`. + +2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and + `SystemOverloadedError` error labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: {times: 1}, + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "SystemOverloadedError"], + errorCode: 91 + } + } + ``` + +3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `91` (ShutdownInProgress) and + the `RetryableWriteError` and `RetryableError` labels: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "alwaysOn", + data: { + failCommands: ["insert"], + errorLabels: ["RetryableError", "RetryableWriteError"], + errorCode: 91 + } + } + ``` + + Configure the second fail point command only if the failed event is for the first error configured in step 2. + +4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a + server error. Assert that backoff was applied only once for the initial overload error and not for the subsequent + non-overload retryable errors. + +5. Disable the fail point: + + ```javascript + { + configureFailPoint: "failCommand", + mode: "off" + } + ``` + +## Changelog + +- 2026-04-14: Add prose tests for retry behavior when a mix of overload and non-overload errors are encountered. + +- 2026-04-02: Fix test for error propagation behavior when multiple errors are encountered. + +- 2026-02-17: Fix test for error propagation behavior when multiple errors are encountered. + +- 2026-02-03: Add tests for error propagation behavior when multiple errors are encountered. + +- 2024-10-29: Convert command construction tests to unified format. + +- 2024-05-30: Migrated from reStructuredText to Markdown. + +- 2024-02-27: Convert legacy retryable writes tests to unified format. + +- 2024-02-21: Update prose tests 4 and 5 to workaround SDAM behavior preventing execution of deprioritization code + paths. + +- 2024-01-05: Fix typo in prose test title. + +- 2024-01-03: Note server version requirements for fail point options and revise tests to specify the `errorLabels` + option at the top-level instead of within `writeConcernError`. + +- 2023-08-26: Add prose tests for retrying in a sharded cluster. + +- 2022-08-30: Add prose test verifying correct error handling for errors with the NoWritesPerformed label, which is to + return the original error. + +- 2022-04-22: Clarifications to `serverless` and `useMultipleMongoses`. + +- 2021-08-27: Add `serverless` to `runOn`. Clarify behavior of `useMultipleMongoses` for `LoadBalanced` topologies. + +- 2021-04-23: Add `load-balanced` to test topology requirements. + +- 2021-03-24: Add prose test verifying `PoolClearedErrors` are retried. + +- 2019-10-21: Add `errorLabelsContain` and `errorLabelsContain` fields to `result` + +- 2019-08-07: Add Prose Tests section + +- 2019-06-07: Mention $merge stage for aggregate alongside $out + +- 2019-03-01: Add top-level `runOn` field to denote server version and/or topology requirements requirements for the + test file. Removes the `minServerVersion` and `maxServerVersion` top-level fields, which are now expressed within + `runOn` elements. + + Add test-level `useMultipleMongoses` field. diff --git a/source/retryable-writes/tests/README.rst b/source/retryable-writes/tests/README.rst deleted file mode 100644 index 606ba93b6a..0000000000 --- a/source/retryable-writes/tests/README.rst +++ /dev/null @@ -1,316 +0,0 @@ -===================== -Retryable Write Tests -===================== - -.. contents:: - ----- - -Introduction -============ - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the Retryable Writes 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. - -Tests will require a MongoClient created with options defined in the tests. -Integration tests will require a running MongoDB cluster with server versions -3.6.0 or later. The ``{setFeatureCompatibilityVersion: 3.6}`` admin command -will also need to have been executed to enable support for retryable writes on -the cluster. - -Server Fail Point -================= - -The tests depend on a server fail point, ``onPrimaryTransactionalWrite``, which -allows us to force a network error before the server would return a write result -to the client. The fail point also allows control whether the server will -successfully commit the write via its ``failBeforeCommitExceptionCode`` option. -Keep in mind that the fail point only triggers for transaction writes (i.e. write -commands including ``txnNumber`` and ``lsid`` fields). See `SERVER-29606`_ for -more information. - -.. _SERVER-29606: https://jira.mongodb.org/browse/SERVER-29606 - -The fail point may be configured like so:: - - db.runCommand({ - configureFailPoint: "onPrimaryTransactionalWrite", - mode: , - data: - }); - -``mode`` is a generic fail point option and may be assigned a string or document -value. The string values ``"alwaysOn"`` and ``"off"`` may be used to enable or -disable the fail point, respectively. A document may be used to specify either -``times`` or ``skip``, which are mutually exclusive: - -- ``{ times: }`` may be used to limit the number of times the fail - point may trigger before transitioning to ``"off"``. -- ``{ skip: }`` may be used to defer the first trigger of a fail - point, after which it will transition to ``"alwaysOn"``. - -The ``data`` option is a document that may be used to specify options that -control the fail point's behavior. As noted in `SERVER-29606`_, -``onPrimaryTransactionalWrite`` supports the following ``data`` options, which -may be combined if desired: - -- ``closeConnection``: Boolean option, which defaults to ``true``. If ``true``, - the connection on which the write is executed will be closed before a result - can be returned. -- ``failBeforeCommitExceptionCode``: Integer option, which is unset by default. - If set, the specified exception code will be thrown and the write will not be - committed. If unset, the write will be allowed to commit. - -Disabling Fail Point after Test Execution ------------------------------------------ - -After each test that configures a fail point, drivers should disable the -``onPrimaryTransactionalWrite`` fail point to avoid spurious failures in -subsequent tests. The fail point may be disabled like so:: - - db.runCommand({ - configureFailPoint: "onPrimaryTransactionalWrite", - mode: "off" - }); - -Network Error Tests -=================== - -Network error tests are expressed in YAML and should be run against a replica -set. These tests cannot be run against a shard cluster because mongos does not -support the necessary fail point. - -The tests exercise the following scenarios: - -- Single-statement write operations - - - Each test expecting a write result will encounter at-most one network error - for the write command. Retry attempts should return without error and allow - operation to succeed. Observation of the collection state will assert that - the write occurred at-most once. - - - Each test expecting an error will encounter successive network errors for - the write command. Observation of the collection state will assert that the - write was never committed on the server. - -- Multi-statement write operations - - - Each test expecting a write result will encounter at-most one network error - for some write command(s) in the batch. Retry attempts should return without - error and allow the batch to ultimately succeed. Observation of the - collection state will assert that each write occurred at-most once. - - - Each test expecting an error will encounter successive network errors for - some write command in the batch. The batch will ultimately fail with an - error, but observation of the collection state will assert that the failing - write was never committed on the server. We may observe that earlier writes - in the batch occurred at-most once. - -We cannot test a scenario where the first and second attempts both encounter -network errors but the write does actually commit during one of those attempts. -This is because (1) the fail point only triggers when a write would be committed -and (2) the skip and times options are mutually exclusive. That said, such a -test would mainly assert the server's correctness for at-most once semantics and -is not essential to assert driver correctness. - -Test Format ------------ - -Each YAML file has the following keys: - -- ``data``: The data that should exist in the collection under test before each - test run. - -- ``minServerVersion`` (optional): The minimum server version (inclusive) - required to successfully run the test. If this field is not present, it should - be assumed that there is no lower bound on the required server version. - -- ``maxServerVersion`` (optional): The maximum server version (exclusive) - against which this test can run successfully. If this field is not present, - it should be assumed that there is no upper bound on the required server - version. - -- ``tests``: An array of tests that are to be run independently of each other. - Each test will have some or all of the following fields: - - - ``description``: The name of the test. - - - ``clientOptions``: Parameters to pass to MongoClient(). - - - ``failPoint``: The ``configureFailPoint`` command document to run to - configure a fail point on the primary server. Drivers must ensure that - ``configureFailPoint`` is the first field in the command. - - - ``operation``: Document describing the operation to be executed. The - operation should be executed through a collection object derived from a - client that has been created with ``clientOptions``. The operation will have - some or all of the following fields: - - - ``name``: The name of the operation as defined in the CRUD specification. - - - ``arguments``: The names and values of arguments from the CRUD - specification. - - - ``outcome``: Document describing the return value and/or expected state of - the collection after the operation is executed. This will have some or all - of the following fields: - - - ``error``: If ``true``, the test should expect an error or exception. Note - that some drivers may report server-side errors as a write error within a - write result object. - - - ``result``: The return value from the operation. This will correspond to - an operation's result object as defined in the CRUD specification. This - field may be omitted if ``error`` is ``true``. If this field is present - and ``error`` is ``true`` (generally for multi-statement tests), the - result reports information about operations that succeeded before an - unrecoverable failure. In that case, drivers may choose to check the - result object if their BulkWriteException (or equivalent) provides access - to a write result object. - - - ``collection``: - - - ``name`` (optional): The name of the collection to verify. If this isn't - present then use the collection under test. - - - ``data``: The data that should exist in the collection after the - operation has been run. - -Split Batch Tests -================= - -The YAML tests specify bulk write operations that are split by command type -(e.g. sequence of insert, update, and delete commands). Multi-statement write -operations may also be split due to ``maxWriteBatchSize``, -``maxBsonObjectSize``, or ``maxMessageSizeBytes``. - -For instance, an insertMany operation with five 10 MB documents executed using -OP_MSG payload type 0 (i.e. entire command in one document) would be split into -five insert commands in order to respect the 16 MB ``maxBsonObjectSize`` limit. -The same insertMany operation executed using OP_MSG payload type 1 (i.e. command -arguments pulled out into a separate payload vector) would be split into two -insert commands in order to respect the 48 MB ``maxMessageSizeBytes`` limit. - -Noting when a driver might split operations, the ``onPrimaryTransactionalWrite`` -fail point's ``skip`` option may be used to control when the fail point first -triggers. Once triggered, the fail point will transition to the ``alwaysOn`` -state until disabled. Driver authors should also note that the server attempts -to process all documents in a single insert command within a single commit (i.e. -one insert command with five documents may only trigger the fail point once). -This behavior is unique to insert commands (each statement in an update and -delete command is processed independently). - -If testing an insert that is split into two commands, a ``skip`` of one will -allow the fail point to trigger on the second insert command (because all -documents in the first command will be processed in the same commit). When -testing an update or delete that is split into two commands, the ``skip`` should -be set to the number of statements in the first command to allow the fail point -to trigger on the second command. - -Replica Set Failover Test -========================= - -In addition to network errors, writes should also be retried in the event of a -primary failover, which results in a "not master" command error (or similar). -The ``stepdownHangBeforePerformingPostMemberStateUpdateActions`` fail point -implemented in `d4eb562`_ for `SERVER-31355`_ may be used for this test, as it -allows a primary to keep its client connections open after a step down. This -fail point operates by hanging the step down procedure (i.e. ``replSetStepDown`` -command) until the fail point is later deactivated. - -.. _d4eb562: https://github.com/mongodb/mongo/commit/d4eb562ac63717904f24de4a22e395070687bc62 -.. _SERVER-31355: https://jira.mongodb.org/browse/SERVER-31355 - -The following test requires three MongoClient instances and will generally -require two execution contexts (async drivers may get by with a single thread). - -- The client under test will connect to the replica set and be used to execute - write operations. -- The fail point client will connect directly to the initial primary and be used - to toggle the fail point. -- The step down client will connect to the replica set and be used to step down - the primary. This client will generally require its own execution context, - since the step down will hang. - -In order to guarantee that the client under test does not detect the stepped -down primary's state change via SDAM, it must be configured with a large -`heartbeatFrequencyMS`_ value (e.g. 60 seconds). Single-threaded drivers may -also need to set `serverSelectionTryOnce`_ to ``false`` to ensure that server -selection for the retry attempt waits until a new primary is elected. - -.. _heartbeatFrequencyMS: https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#heartbeatfrequencyms -.. _serverSelectionTryOnce: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#serverselectiontryonce - -The test proceeds as follows: - -- Using the client under test, insert a document and observe a successful write - result. This will ensure that initial discovery takes place. -- Using the fail point client, activate the fail point by setting ``mode`` - to ``"alwaysOn"``. -- Using the step down client, step down the primary by executing the command - ``{ replSetStepDown: 60, force: true}``. This operation will hang so long as - the fail point is activated. When the fail point is later deactivated, the - step down will complete and the primary's client connections will be dropped. - At that point, any ensuing network error should be ignored. -- Using the client under test, insert a document and observe a successful write - result. The test MUST assert that the insert command fails once against the - stepped down node and is successfully retried on the newly elected primary - (after SDAM discovers the topology change). The test MAY use APM or another - means to observe both attempts. -- Using the fail point client, deactivate the fail point by setting ``mode`` - to ``"off"``. - -Command Construction Tests -========================== - -Drivers should also assert that command documents are properly constructed with -or without a transaction ID, depending on whether the write operation is -supported. `Command Monitoring`_ may be used to check for the presence of a -``txnNumber`` field in the command document. Note that command documents may -always include an ``lsid`` field per the `Driver Session`_ specification. - -.. _Command Monitoring: ../../command-monitoring/command-monitoring.rst -.. _Driver Session: ../../sessions/driver-sessions.rst - -These tests may be run against both a replica set and shard cluster. - -Drivers should test that transaction IDs are never included in commands for -unsupported write operations: - -* Write commands with unacknowledged write concerns (e.g. ``{w: 0}``) - -* Unsupported single-statement write operations - - - ``updateMany()`` - - ``deleteMany()`` - -* Unsupported multi-statement write operations - - - ``bulkWrite()`` that includes ``UpdateMany`` or ``DeleteMany`` - -* Unsupported write commands - - - ``aggregate`` with ``$out`` pipeline operator - -Drivers should test that transactions IDs are always included in commands for -supported write operations: - -* Supported single-statement write operations - - - ``insertOne()`` - - ``updateOne()`` - - ``replaceOne()`` - - ``deleteOne()`` - - ``findOneAndDelete()`` - - ``findOneAndReplace()`` - - ``findOneAndUpdate()`` - -* Supported multi-statement write operations - - - ``insertMany()`` with ``ordered=true`` - - ``insertMany()`` with ``ordered=false`` - - ``bulkWrite()`` with ``ordered=true`` (no ``UpdateMany`` or ``DeleteMany``) - - ``bulkWrite()`` with ``ordered=false`` (no ``UpdateMany`` or ``DeleteMany``) diff --git a/source/retryable-writes/tests/bulkWrite-serverErrors.json b/source/retryable-writes/tests/bulkWrite-serverErrors.json deleted file mode 100644 index a208019f77..0000000000 --- a/source/retryable-writes/tests/bulkWrite-serverErrors.json +++ /dev/null @@ -1,184 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "BulkWrite succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 3 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 3 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/bulkWrite-serverErrors.yml b/source/retryable-writes/tests/bulkWrite-serverErrors.yml deleted file mode 100644 index 387f680b84..0000000000 --- a/source/retryable-writes/tests/bulkWrite-serverErrors.yml +++ /dev/null @@ -1,92 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "BulkWrite succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 1: 3 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 23 } - - { _id: 3, x: 33 } - - - description: "BulkWrite succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 1: 3 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 23 } - - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/bulkWrite.json b/source/retryable-writes/tests/bulkWrite.json deleted file mode 100644 index d94dc6152e..0000000000 --- a/source/retryable-writes/tests/bulkWrite.json +++ /dev/null @@ -1,829 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "First command is retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "All commands are retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 7 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4, - "x": 44 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 5, - "x": 55 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "_id": 3, - "x": 333 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 3, - "insertedIds": { - "0": 2, - "2": 3, - "4": 5 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 1, - "upsertedIds": { - "3": 4 - } - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 333 - }, - { - "_id": 4, - "x": 45 - }, - { - "_id": 5, - "x": 55 - } - ] - } - } - }, - { - "description": "Both commands are retried after their first statement fails", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "Second command is retried after its second statement fails", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "BulkWrite with unordered execution", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 2, - "insertedIds": { - "0": 2, - "1": 3 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "First insertOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "Second updateOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Third updateOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following deleteMany is retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": 11 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following updateMany is retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "x": 11 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/bulkWrite.yml b/source/retryable-writes/tests/bulkWrite.yml deleted file mode 100644 index 4d561159f8..0000000000 --- a/source/retryable-writes/tests/bulkWrite.yml +++ /dev/null @@ -1,413 +0,0 @@ -data: - - { _id: 1, x: 11 } - -minServerVersion: '3.6' - -tests: - - - description: "First command is retried" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 23 } - - - # Write operations in this ordered batch are intentionally sequenced so - # that each write command consists of a single statement, which will - # fail on the first attempt and succeed on the second, retry attempt. - description: "All commands are retried" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 7 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - - - name: "updateOne" - arguments: - filter: { _id: 4, x: 44 } - update: { $inc: { x : 1 }} - upsert: true - - - name: "insertOne" - arguments: - document: { _id: 5, x: 55 } - - - name: "replaceOne" - arguments: - filter: { _id: 3 } - replacement: { _id: 3, x: 333 } - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 3 - insertedIds: { 0: 2, 2: 3, 4: 5 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 1 - upsertedIds: { 3: 4 } - collection: - data: - - { _id: 2, x: 23 } - - { _id: 3, x: 333 } - - { _id: 4, x: 45 } - - { _id: 5, x: 55 } - - - description: "Both commands are retried after their first statement fails" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 23 } - - - description: "Second command is retried after its second statement fails" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 2 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 23 } - - - description: "BulkWrite with unordered execution" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - options: { ordered: false } - outcome: - result: - deletedCount: 0 - insertedCount: 2 - insertedIds: { 0: 2, 1: 3 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "First insertOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 0 - insertedIds: { } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - - description: "Second updateOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - - - name: "deleteOne" - arguments: - filter: { _id: 1 } - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 0: 2 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "Third updateOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { skip: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - - - name: "updateOne" - arguments: - filter: { _id: 2 } - update: { $inc: { x : 1 }} - options: { ordered: true } - outcome: - error: true - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - # The onPrimaryTransactionalWrite fail point only triggers for write - # operations that include a transaction ID. Therefore, it will not - # affect the initial deleteMany and will trigger once (and only once) - # for the first insertOne attempt. - description: "Single-document write following deleteMany is retried" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "deleteMany" - arguments: - filter: { x: 11 } - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - options: { ordered: true } - outcome: - result: - deletedCount: 1 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 2, x: 22 } - - - # The onPrimaryTransactionalWrite fail point only triggers for write - # operations that include a transaction ID. Therefore, it will not - # affect the initial updateMany and will trigger once (and only once) - # for the first insertOne attempt. - description: "Single-document write following updateMany is retried" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "bulkWrite" - arguments: - requests: - - - name: "updateMany" - arguments: - filter: { x: 11 } - update: { $inc: { x : 1 }} - - - name: "insertOne" - arguments: - document: { _id: 2, x: 22 } - options: { ordered: true } - outcome: - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: { 1: 2 } - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - upsertedIds: { } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/deleteOne-serverErrors.json b/source/retryable-writes/tests/deleteOne-serverErrors.json deleted file mode 100644 index 606fc66f5c..0000000000 --- a/source/retryable-writes/tests/deleteOne-serverErrors.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "DeleteOne succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/deleteOne-serverErrors.yml b/source/retryable-writes/tests/deleteOne-serverErrors.yml deleted file mode 100644 index 006f151383..0000000000 --- a/source/retryable-writes/tests/deleteOne-serverErrors.yml +++ /dev/null @@ -1,50 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "DeleteOne succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["delete"] - errorCode: 189 - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["delete"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/deleteOne.json b/source/retryable-writes/tests/deleteOne.json deleted file mode 100644 index 8e42e9c4f0..0000000000 --- a/source/retryable-writes/tests/deleteOne.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "DeleteOne is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/deleteOne.yml b/source/retryable-writes/tests/deleteOne.yml deleted file mode 100644 index d82bfc054f..0000000000 --- a/source/retryable-writes/tests/deleteOne.yml +++ /dev/null @@ -1,60 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "DeleteOne is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - result: - deletedCount: 1 - collection: - data: - - { _id: 2, x: 22 } - - - description: "DeleteOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "deleteOne" - arguments: - filter: { _id: 1 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/etc/templates/handshakeError.yml.template b/source/retryable-writes/tests/etc/templates/handshakeError.yml.template new file mode 100644 index 0000000000..03b0e19bcd --- /dev/null +++ b/source/retryable-writes/tests/etc/templates/handshakeError.yml.template @@ -0,0 +1,150 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable writes handshake failures" + +schemaVersion: "1.4" # For `serverless: forbid` + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-writes-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. +{% for operation in operations %} + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake network error" + {%- if (operation.operation_name == 'clientBulkWrite') %} + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + serverless: forbid + {%- endif %} + 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: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} + + - description: "{{operation.object}}.{{operation.operation_name}} succeeds after retryable handshake server error (ShutdownInProgress)" + {%- if (operation.operation_name == 'clientBulkWrite') %} + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + serverless: forbid + {%- endif %} + 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: {{operation.operation_name}} + object: *{{operation.object}} + {%- if operation.arguments|length > 0 %} + arguments: + {%- for arg in operation.arguments %} + {{arg}} + {%- endfor -%} + {%- endif %} + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: {{operation.command_name}} + - commandSucceededEvent: + commandName: {{operation.command_name}} +{% endfor -%} diff --git a/source/retryable-writes/tests/findOneAndDelete-serverErrors.json b/source/retryable-writes/tests/findOneAndDelete-serverErrors.json deleted file mode 100644 index 306b1423f4..0000000000 --- a/source/retryable-writes/tests/findOneAndDelete-serverErrors.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "FindOneAndDelete succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndDelete-serverErrors.yml b/source/retryable-writes/tests/findOneAndDelete-serverErrors.yml deleted file mode 100644 index 75071a36f2..0000000000 --- a/source/retryable-writes/tests/findOneAndDelete-serverErrors.yml +++ /dev/null @@ -1,50 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "FindOneAndDelete succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 2, x: 22 } - - - description: "FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/findOneAndDelete.json b/source/retryable-writes/tests/findOneAndDelete.json deleted file mode 100644 index 5963c4329d..0000000000 --- a/source/retryable-writes/tests/findOneAndDelete.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "FindOneAndDelete is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndDelete.yml b/source/retryable-writes/tests/findOneAndDelete.yml deleted file mode 100644 index 4f5d71caa3..0000000000 --- a/source/retryable-writes/tests/findOneAndDelete.yml +++ /dev/null @@ -1,61 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "FindOneAndDelete is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 2, x: 22 } - - - description: "FindOneAndDelete is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 2, x: 22 } - - - description: "FindOneAndDelete is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndDelete" - arguments: - filter: { x: { $gte: 11 }} - sort: { x: 1 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/findOneAndReplace-serverErrors.json b/source/retryable-writes/tests/findOneAndReplace-serverErrors.json deleted file mode 100644 index ab209b8d1c..0000000000 --- a/source/retryable-writes/tests/findOneAndReplace-serverErrors.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "FindOneAndReplace succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndReplace-serverErrors.yml b/source/retryable-writes/tests/findOneAndReplace-serverErrors.yml deleted file mode 100644 index 8b6a201293..0000000000 --- a/source/retryable-writes/tests/findOneAndReplace-serverErrors.yml +++ /dev/null @@ -1,54 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "FindOneAndReplace succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/findOneAndReplace.json b/source/retryable-writes/tests/findOneAndReplace.json deleted file mode 100644 index 20b9e0bc31..0000000000 --- a/source/retryable-writes/tests/findOneAndReplace.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "FindOneAndReplace is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndReplace.yml b/source/retryable-writes/tests/findOneAndReplace.yml deleted file mode 100644 index 6e26d7cfe0..0000000000 --- a/source/retryable-writes/tests/findOneAndReplace.yml +++ /dev/null @@ -1,66 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "FindOneAndReplace is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "FindOneAndReplace is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "FindOneAndReplace is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndReplace" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - returnDocument: "Before" - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/findOneAndUpdate-serverErrors.json b/source/retryable-writes/tests/findOneAndUpdate-serverErrors.json deleted file mode 100644 index 92f09ce9a1..0000000000 --- a/source/retryable-writes/tests/findOneAndUpdate-serverErrors.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "FindOneAndUpdate succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndUpdate-serverErrors.yml b/source/retryable-writes/tests/findOneAndUpdate-serverErrors.yml deleted file mode 100644 index 7eddcdc09b..0000000000 --- a/source/retryable-writes/tests/findOneAndUpdate-serverErrors.yml +++ /dev/null @@ -1,54 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "FindOneAndUpdate succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - errorCode: 189 - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["findAndModify"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/findOneAndUpdate.json b/source/retryable-writes/tests/findOneAndUpdate.json deleted file mode 100644 index 92d4f54bda..0000000000 --- a/source/retryable-writes/tests/findOneAndUpdate.json +++ /dev/null @@ -1,149 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "FindOneAndUpdate is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/findOneAndUpdate.yml b/source/retryable-writes/tests/findOneAndUpdate.yml deleted file mode 100644 index a82bd03445..0000000000 --- a/source/retryable-writes/tests/findOneAndUpdate.yml +++ /dev/null @@ -1,65 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "FindOneAndUpdate is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "FindOneAndUpdate is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - returnDocument: "Before" - outcome: - result: { _id: 1, x: 11 } - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "FindOneAndUpdate is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "findOneAndUpdate" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/insertMany-serverErrors.json b/source/retryable-writes/tests/insertMany-serverErrors.json deleted file mode 100644 index 17ba9b1408..0000000000 --- a/source/retryable-writes/tests/insertMany-serverErrors.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "InsertMany succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/insertMany-serverErrors.yml b/source/retryable-writes/tests/insertMany-serverErrors.yml deleted file mode 100644 index aafaee8197..0000000000 --- a/source/retryable-writes/tests/insertMany-serverErrors.yml +++ /dev/null @@ -1,59 +0,0 @@ -data: - - { _id: 1, x: 11 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "InsertMany succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 189 - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: true } - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertMany succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: true } - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/insertMany.json b/source/retryable-writes/tests/insertMany.json deleted file mode 100644 index 74dd4a7a69..0000000000 --- a/source/retryable-writes/tests/insertMany.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "InsertMany succeeds after one network error", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany with unordered execution", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany fails after multiple network errors", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": "alwaysOn", - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/insertMany.yml b/source/retryable-writes/tests/insertMany.yml deleted file mode 100644 index 7559e34014..0000000000 --- a/source/retryable-writes/tests/insertMany.yml +++ /dev/null @@ -1,77 +0,0 @@ -data: - - { _id: 1, x: 11 } - -minServerVersion: '3.6' - -tests: - - - description: "InsertMany succeeds after one network error" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: true } - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertMany with unordered execution" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - options: { ordered: false } - outcome: - result: - insertedIds: { 0: 2, 1: 3 } - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertMany fails after multiple network errors" - clientOptions: - retryWrites: true - failPoint: - # Normally, a mongod will insert the documents as a batch with a - # single commit. If this fails, mongod may try to insert each - # document one at a time depending on the failure. Therefore our - # single insert command may trigger the failpoint twice on each - # driver attempt. This test permanently enables the fail point to - # ensure the retry attempt always fails. - configureFailPoint: onPrimaryTransactionalWrite - mode: "alwaysOn" - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertMany" - arguments: - documents: - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - { _id: 4, x: 44 } - options: { ordered: true } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } diff --git a/source/retryable-writes/tests/insertOne-serverErrors.json b/source/retryable-writes/tests/insertOne-serverErrors.json deleted file mode 100644 index 9ef8437062..0000000000 --- a/source/retryable-writes/tests/insertOne-serverErrors.json +++ /dev/null @@ -1,1000 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "InsertOne succeeds after connection failure", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotMaster", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 10107, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotMasterOrSecondary", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13436, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotMasterNoSlaveOk", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13435, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedDueToReplStateChange", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11602, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedAtShutdown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostNotFound", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 7, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostUnreachable", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 6, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after SocketException", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 9001, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NetworkTimeout", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 89, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after Interrupted", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError InterruptedAtShutdown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after multiple retryable writeConcernErrors", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError Interrupted", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError WriteConcernFailed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": true - } - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/insertOne-serverErrors.yml b/source/retryable-writes/tests/insertOne-serverErrors.yml deleted file mode 100644 index 69173b9c18..0000000000 --- a/source/retryable-writes/tests/insertOne-serverErrors.yml +++ /dev/null @@ -1,471 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "InsertOne succeeds after connection failure" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - closeConnection: true - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NotMaster" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 10107 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NotMasterOrSecondary" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 13436 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NotMasterNoSlaveOk" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 13435 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after InterruptedDueToReplStateChange" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11602 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after InterruptedAtShutdown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11600 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 189 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 91 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after HostNotFound" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 7 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after HostUnreachable" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 6 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after SocketException" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 9001 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after NetworkTimeout" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 89 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne fails after Interrupted" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 11601 - closeConnection: false - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "InsertOne succeeds after WriteConcernError InterruptedAtShutdown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 11600 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 11602 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 189 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne fails after multiple retryable writeConcernErrors" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["insert"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } # The write was still applied. - - - description: "InsertOne fails after WriteConcernError Interrupted" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 11601 - errmsg: operation was interrupted - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } # The write was still applied. - - - description: "InsertOne fails after WriteConcernError WriteConcernFailed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - writeConcernError: - code: 64 - codeName: WriteConcernFailed - errmsg: waiting for replication timed out - errInfo: {wtimeout: True} - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } # The write was still applied. diff --git a/source/retryable-writes/tests/insertOne.json b/source/retryable-writes/tests/insertOne.json deleted file mode 100644 index 123d51edb6..0000000000 --- a/source/retryable-writes/tests/insertOne.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "InsertOne is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/insertOne.yml b/source/retryable-writes/tests/insertOne.yml deleted file mode 100644 index 0e535ddbda..0000000000 --- a/source/retryable-writes/tests/insertOne.yml +++ /dev/null @@ -1,64 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "InsertOne is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - result: - insertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 33 } - - - description: "InsertOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "insertOne" - arguments: - document: { _id: 3, x: 33 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/replaceOne-serverErrors.json b/source/retryable-writes/tests/replaceOne-serverErrors.json deleted file mode 100644 index b9c449a869..0000000000 --- a/source/retryable-writes/tests/replaceOne-serverErrors.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "ReplaceOne succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/replaceOne-serverErrors.yml b/source/retryable-writes/tests/replaceOne-serverErrors.yml deleted file mode 100644 index bd2bb0f30f..0000000000 --- a/source/retryable-writes/tests/replaceOne-serverErrors.yml +++ /dev/null @@ -1,58 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "ReplaceOne succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "ReplaceOne succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/replaceOne.json b/source/retryable-writes/tests/replaceOne.json deleted file mode 100644 index 6e9107b799..0000000000 --- a/source/retryable-writes/tests/replaceOne.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "ReplaceOne is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/replaceOne.yml b/source/retryable-writes/tests/replaceOne.yml deleted file mode 100644 index 1eed95edff..0000000000 --- a/source/retryable-writes/tests/replaceOne.yml +++ /dev/null @@ -1,69 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "ReplaceOne is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "ReplaceOne is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 111 } - - { _id: 2, x: 22 } - - - description: "ReplaceOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "replaceOne" - arguments: - filter: { _id: 1 } - replacement: { _id: 1, x: 111 } - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/aggregate-out-merge.json b/source/retryable-writes/tests/unified/aggregate-out-merge.json new file mode 100644 index 0000000000..fd25c345ac --- /dev/null +++ b/source/retryable-writes/tests/unified/aggregate-out-merge.json @@ -0,0 +1,149 @@ +{ + "description": "aggregate with $out/$merge does not set txnNumber", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "mergeCollection", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "aggregate with $out does not set txnNumber", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "outCollection" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate with $merge does not set txnNumber", + "runOnRequirements": [ + { + "minServerVersion": "4.1.11" + } + ], + "operations": [ + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$merge": { + "into": "mergeCollection" + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "aggregate", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/aggregate-out-merge.yml b/source/retryable-writes/tests/unified/aggregate-out-merge.yml new file mode 100644 index 0000000000..5114b31633 --- /dev/null +++ b/source/retryable-writes/tests/unified/aggregate-out-merge.yml @@ -0,0 +1,67 @@ +description: "aggregate with $out/$merge does not set txnNumber" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "3.6" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + # The output collection must already exist for $merge on a sharded cluster + - collectionName: &mergeCollection mergeCollection + databaseName: *database0Name + documents: [] + +tests: + - description: "aggregate with $out does not set txnNumber" + runOnRequirements: + - serverless: forbid # $out is not supported on serverless + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $out: outCollection } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: aggregate + command: + txnNumber: { $$exists: false } + - description: "aggregate with $merge does not set txnNumber" + runOnRequirements: + - minServerVersion: "4.1.11" + operations: + - object: *collection0 + name: aggregate + arguments: + pipeline: + - { $sort: { x: 1 } } + - { $match: { _id: { $gt: 1 } } } + - { $merge: { into: *mergeCollection } } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: aggregate + command: + txnNumber: { $$exists: false } diff --git a/source/retryable-writes/tests/unified/bulkWrite-errorLabels.json b/source/retryable-writes/tests/unified/bulkWrite-errorLabels.json new file mode 100644 index 0000000000..13ba9bae75 --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite-errorLabels.json @@ -0,0 +1,416 @@ +{ + "description": "bulkWrite-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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 with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "BulkWrite succeeds after WriteConcernError ShutdownInProgress", + "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" + } + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 3 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/bulkWrite-errorLabels.yml b/source/retryable-writes/tests/unified/bulkWrite-errorLabels.yml new file mode 100644 index 0000000000..9adec6de71 --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite-errorLabels.yml @@ -0,0 +1,222 @@ +description: bulkWrite-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'BulkWrite succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + # Driver retries operation and it succeeds + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } + - + description: 'BulkWrite succeeds after WriteConcernError ShutdownInProgress' + 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' + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 3 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/unified/bulkWrite-serverErrors.json b/source/retryable-writes/tests/unified/bulkWrite-serverErrors.json new file mode 100644 index 0000000000..0a063ab4d9 --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite-serverErrors.json @@ -0,0 +1,285 @@ +{ + "description": "retryable-writes bulkWrite serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "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.3.1" + } + ], + "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 + } + ] + } + ] + }, + { + "description": "BulkWrite fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/bulkWrite-serverErrors.yml b/source/retryable-writes/tests/unified/bulkWrite-serverErrors.yml new file mode 100644 index 0000000000..a88a206123 --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite-serverErrors.yml @@ -0,0 +1,136 @@ +description: "retryable-writes bulkWrite serverErrors" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.7" + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - description: "BulkWrite succeeds after retryable writeConcernError in first batch" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: [RetryableWriteError] # top-level error labels + writeConcernError: + code: 91 # ShutdownInProgress + 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: *collectionName + documents: [{ _id: 3, x: 33 }] + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: [{ _id: 3, x: 33 }] + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - + q: { _id: 2 } + limit: 1 + commandName: delete + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 3, x: 33 } # The write was still applied + - + description: 'BulkWrite fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteOne: + filter: { _id: 1 } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/unified/bulkWrite.json b/source/retryable-writes/tests/unified/bulkWrite.json new file mode 100644 index 0000000000..f2bd9e0eb8 --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite.json @@ -0,0 +1,1083 @@ +{ + "description": "bulkWrite", + "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 + } + ] + } + ], + "tests": [ + { + "description": "First command is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "All commands are retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 7 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 4, + "x": 44 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + } + }, + { + "insertOne": { + "document": { + "_id": 5, + "x": 55 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 3 + }, + "replacement": { + "_id": 3, + "x": 333 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 3, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "2": 3, + "4": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 1, + "upsertedIds": { + "3": 4 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 23 + }, + { + "_id": 3, + "x": 333 + }, + { + "_id": 4, + "x": 45 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ] + }, + { + "description": "Both commands are retried after their first statement fails", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "Second command is retried after its second statement fails", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 2 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ] + }, + { + "description": "BulkWrite with unordered execution", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + } + ], + "ordered": false + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 2, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "First insertOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 0, + "insertedIds": { + "$$unsetOrMatches": {} + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "Second updateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 2 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Third updateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "skip": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Single-document write following deleteMany is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": { + "x": 11 + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "Single-document write following updateMany is retried", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": { + "x": 11 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 2, + "x": 22 + } + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "1": 2 + } + }, + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "collection bulkWrite with updateMany does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateMany": { + "filter": {}, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "collection bulkWrite with deleteMany does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "deleteMany": { + "filter": {} + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/bulkWrite.yml b/source/retryable-writes/tests/unified/bulkWrite.yml new file mode 100644 index 0000000000..c92fd4b74a --- /dev/null +++ b/source/retryable-writes/tests/unified/bulkWrite.yml @@ -0,0 +1,559 @@ +description: bulkWrite + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'First command is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - + # Write operations in this ordered batch are intentionally sequenced so that + # each write command consists of a single statement, which will fail on the + # first attempt and succeed on the second, retry attempt. + description: 'All commands are retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 7 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 3, x: 33 } + - + updateOne: + filter: { _id: 4, x: 44 } + update: { $inc: { x: 1 } } + upsert: true + - + insertOne: + document: { _id: 5, x: 55 } + - + replaceOne: + filter: { _id: 3 } + replacement: { _id: 3, x: 333 } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 3 + insertedIds: + $$unsetOrMatches: + '0': 2 + '2': 3 + '4': 5 + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 1 + upsertedIds: { '3': 4 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 23 } + - { _id: 3, x: 333 } + - { _id: 4, x: 45 } + - { _id: 5, x: 55 } + - + description: 'Both commands are retried after their first statement fails' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + - + description: 'Second command is retried after its second statement fails' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 2 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + - + description: 'BulkWrite with unordered execution' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + insertOne: + document: { _id: 3, x: 33 } + ordered: false + expectResult: + deletedCount: 0 + insertedCount: 2 + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + description: 'First insertOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 0 + insertedIds: + $$unsetOrMatches: { } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'Second updateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - + deleteOne: + filter: { _id: 1 } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '0': 2 } } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'Third updateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { skip: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateOne: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 2, x: 22 } + - + updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + ordered: true + expectError: + isError: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + # The onPrimaryTransactionalWrite fail point only triggers for write + # operations that include a transaction ID. Therefore, it will not affect + # the initial deleteMany and will trigger once (and only once) for the first + # insertOne attempt. + description: 'Single-document write following deleteMany is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: { x: 11 } + - + insertOne: + document: { _id: 2, x: 22 } + ordered: true + expectResult: + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + # The onPrimaryTransactionalWrite fail point only triggers for write + # operations that include a transaction ID. Therefore, it will not affect + # the initial updateMany and will trigger once (and only once) for the first + # insertOne attempt. + description: 'Single-document write following updateMany is retried' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateMany: + filter: { x: 11 } + update: { $inc: { x: 1 } } + - + insertOne: + document: { _id: 2, x: 22 } + ordered: true + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { '1': 2 } } + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + upsertedIds: { } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - description: "collection bulkWrite with updateMany does not set txnNumber" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - + updateMany: + filter: {} + update: { $set: { x: 1 } } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: false } + - description: "collection bulkWrite with deleteMany does not set txnNumber" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: + - + deleteMany: + filter: {} + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: false } \ No newline at end of file diff --git a/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json b/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json new file mode 100644 index 0000000000..d16e0c9c8d --- /dev/null +++ b/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.json @@ -0,0 +1,351 @@ +{ + "description": "client bulkWrite retryable writes with client errors", + "schemaVersion": "1.21", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "retryable-writes-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with one network error succeeds after retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "client bulkWrite with two network errors fails after retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "closeConnection": true + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ], + "verboseResults": true + }, + "expectError": { + "isClientError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml b/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml new file mode 100644 index 0000000000..e5214b90f8 --- /dev/null +++ b/source/retryable-writes/tests/unified/client-bulkWrite-clientErrors.yml @@ -0,0 +1,173 @@ +description: "client bulkWrite retryable writes with client errors" +schemaVersion: "1.21" +runOnRequirements: + - minServerVersion: "8.0" + topologies: + - replicaset + - sharded + - load-balanced + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "retryable-writes-tests.coll0" + +tests: + - description: "client bulkWrite with one network error succeeds after retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + closeConnection: true + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 4 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - description: "client bulkWrite with two network errors fails after retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: [ bulkWrite ] + closeConnection: true + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + verboseResults: true + expectError: + isClientError: true + errorLabelsContain: ["RetryableWriteError"] # Error label added by driver. + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + # An implicit session is included with the transaction number: + lsid: { "$$exists": true } + txnNumber: { "$$exists": true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json b/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json new file mode 100644 index 0000000000..a1f7c8152a --- /dev/null +++ b/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.json @@ -0,0 +1,882 @@ +{ + "description": "client bulkWrite retryable writes", + "schemaVersion": "1.21", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "clientRetryWritesFalse", + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "retryable-writes-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite with no multi: true operations succeeds after retryable top-level error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "replaceOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 2 + }, + "replacement": { + "x": 222 + } + } + }, + { + "deleteOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 1, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "3": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 222 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "client bulkWrite with multi: true operations fails after retryable top-level error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ] + }, + "expectError": { + "errorCode": 189, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": true + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with no multi: true operations succeeds after retryable writeConcernError", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "updateOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "replaceOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 2 + }, + "replacement": { + "x": 222 + } + } + }, + { + "deleteOne": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 2, + "modifiedCount": 2, + "deletedCount": 1, + "insertResults": { + "0": { + "insertedId": 4 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + } + }, + "deleteResults": { + "3": { + "deletedCount": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "_id": 2 + }, + "updateMods": { + "x": 222 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": false + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "lsid": { + "$$exists": true + }, + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with multi: true operations fails after retryable writeConcernError", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "deleteMany": { + "namespace": "retryable-writes-tests.coll0", + "filter": { + "_id": 3 + } + } + } + ] + }, + "expectError": { + "writeConcernErrors": [ + { + "code": 91, + "message": "Replication is being shut down" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": true + }, + { + "delete": 0, + "filter": { + "_id": 3 + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "client bulkWrite with retryWrites: false does not retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "clientRetryWritesFalse", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "clientRetryWritesFalse", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-tests.coll0", + "document": { + "_id": 4, + "x": 44 + } + } + } + ] + }, + "expectError": { + "errorCode": 189, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "clientRetryWritesFalse", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 4, + "x": 44 + } + } + ], + "nsInfo": [ + { + "ns": "retryable-writes-tests.coll0" + } + ], + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml b/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml new file mode 100644 index 0000000000..9510682317 --- /dev/null +++ b/source/retryable-writes/tests/unified/client-bulkWrite-serverErrors.yml @@ -0,0 +1,416 @@ +description: "client bulkWrite retryable writes" +schemaVersion: "1.21" +runOnRequirements: + - minServerVersion: "8.0" + topologies: + - replicaset + - sharded + - load-balanced + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - client: + id: &clientRetryWritesFalse clientRetryWritesFalse + uriOptions: + retryWrites: false + observeEvents: [ commandStartedEvent ] + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +_yamlAnchors: + namespace: &namespace "retryable-writes-tests.coll0" + +tests: + - description: "client bulkWrite with no multi: true operations succeeds after retryable top-level error" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - replaceOne: + namespace: *namespace + filter: { _id: 2 } + replacement: { x: 222 } + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 1 + insertResults: + 0: + insertedId: 4 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 3: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 222 } + - { _id: 4, x: 44 } + - description: "client bulkWrite with multi: true operations fails after retryable top-level error" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateMany: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - deleteMany: + namespace: *namespace + filter: { _id: 3 } + expectError: + errorCode: 189 + errorLabelsContain: [ RetryableWriteError ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: true + - delete: 0 + filter: { _id: 3 } + multi: true + nsInfo: + - ns: *namespace + txnNumber: { $$exists: false } + - description: "client bulkWrite with no multi: true operations succeeds after retryable writeConcernError" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 + errmsg: "Replication is being shut down" + - object: *client0 + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - replaceOne: + namespace: *namespace + filter: { _id: 2 } + replacement: { x: 222 } + - deleteOne: + namespace: *namespace + filter: { _id: 3 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 2 + modifiedCount: 2 + deletedCount: 1 + insertResults: + 0: + insertedId: 4 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + deleteResults: + 3: + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: false + - update: 0 + filter: { _id: 2 } + updateMods: { x: 222 } + multi: false + - delete: 0 + filter: { _id: 3 } + multi: false + nsInfo: + - ns: *namespace + lsid: { $$exists: true } + txnNumber: { $$exists: true } + - description: "client bulkWrite with multi: true operations fails after retryable writeConcernError" + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 + errmsg: "Replication is being shut down" + - object: *client0 + name: clientBulkWrite + arguments: + models: + - updateMany: + namespace: *namespace + filter: { _id: 1 } + update: + $inc: { x: 1 } + - deleteMany: + namespace: *namespace + filter: { _id: 3 } + expectError: + writeConcernErrors: + - code: 91 + message: "Replication is being shut down" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - update: 0 + filter: { _id: 1 } + updateMods: + $inc: { x: 1 } + multi: true + - delete: 0 + filter: { _id: 3 } + multi: true + nsInfo: + - ns: *namespace + txnNumber: { $$exists: false } + - description: "client bulkWrite with retryWrites: false does not retry" + operations: + - object: testRunner + name: failPoint + arguments: + client: *clientRetryWritesFalse + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: [ bulkWrite ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - object: *clientRetryWritesFalse + name: clientBulkWrite + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 4, x: 44 } + expectError: + errorCode: 189 + errorLabelsContain: [ RetryableWriteError ] + expectEvents: + - client: *clientRetryWritesFalse + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - insert: 0 + document: { _id: 4, x: 44 } + nsInfo: + - ns: *namespace + txnNumber: { $$exists: false } diff --git a/source/retryable-writes/tests/unified/deleteMany.json b/source/retryable-writes/tests/unified/deleteMany.json new file mode 100644 index 0000000000..381f377954 --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteMany.json @@ -0,0 +1,96 @@ +{ + "description": "deleteMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "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": "DeleteMany ignores retryWrites", + "operations": [ + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "filter": {} + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/deleteMany.yml b/source/retryable-writes/tests/unified/deleteMany.yml new file mode 100644 index 0000000000..a05fe80769 --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteMany.yml @@ -0,0 +1,57 @@ +description: deleteMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteMany ignores retryWrites' + operations: + - + object: *collection0 + name: deleteMany + arguments: + filter: { } + expectResult: + deletedCount: 2 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: false } diff --git a/source/retryable-writes/tests/unified/deleteOne-errorLabels.json b/source/retryable-writes/tests/unified/deleteOne-errorLabels.json new file mode 100644 index 0000000000..88920862ec --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne-errorLabels.json @@ -0,0 +1,266 @@ +{ + "description": "deleteOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "DeleteOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/deleteOne-errorLabels.yml b/source/retryable-writes/tests/unified/deleteOne-errorLabels.yml new file mode 100644 index 0000000000..08e700a9bb --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne-errorLabels.yml @@ -0,0 +1,157 @@ +description: deleteOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + # Driver retries operation and it succeeds + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'DeleteOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ delete ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/deleteOne-serverErrors.json b/source/retryable-writes/tests/unified/deleteOne-serverErrors.json new file mode 100644 index 0000000000..0808b7921d --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne-serverErrors.json @@ -0,0 +1,114 @@ +{ + "description": "deleteOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "DeleteOne fails with RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "delete" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/deleteOne-serverErrors.yml b/source/retryable-writes/tests/unified/deleteOne-serverErrors.yml new file mode 100644 index 0000000000..2b63c43e37 --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne-serverErrors.yml @@ -0,0 +1,67 @@ +description: deleteOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne fails with RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ delete ] + closeConnection: true + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/deleteOne.json b/source/retryable-writes/tests/unified/deleteOne.json new file mode 100644 index 0000000000..9e37ff8bcf --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne.json @@ -0,0 +1,218 @@ +{ + "description": "deleteOne", + "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": "DeleteOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "delete", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "DeleteOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "DeleteOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/deleteOne.yml b/source/retryable-writes/tests/unified/deleteOne.yml new file mode 100644 index 0000000000..5a176f8293 --- /dev/null +++ b/source/retryable-writes/tests/unified/deleteOne.yml @@ -0,0 +1,123 @@ +description: deleteOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'DeleteOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: delete + command: + txnNumber: { $$exists: true } + - + description: 'DeleteOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectResult: + deletedCount: 1 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'DeleteOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: deleteOne + arguments: + filter: { _id: 1 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json b/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json new file mode 100644 index 0000000000..8639873fca --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.json @@ -0,0 +1,289 @@ +{ + "description": "findOneAndDelete-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndDelete succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml b/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml new file mode 100644 index 0000000000..cd712f2eb3 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete-errorLabels.yml @@ -0,0 +1,158 @@ +description: findOneAndDelete-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json b/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json new file mode 100644 index 0000000000..f6d8e9d69c --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "findOneAndDelete-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndDelete fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml b/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml new file mode 100644 index 0000000000..a9da496c08 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete-serverErrors.yml @@ -0,0 +1,68 @@ +description: findOneAndDelete-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndDelete.json b/source/retryable-writes/tests/unified/findOneAndDelete.json new file mode 100644 index 0000000000..ebfb8ce665 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete.json @@ -0,0 +1,235 @@ +{ + "description": "findOneAndDelete", + "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": "FindOneAndDelete is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "FindOneAndDelete is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndDelete is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "filter": { + "x": { + "$gte": 11 + } + }, + "sort": { + "x": 1 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndDelete.yml b/source/retryable-writes/tests/unified/findOneAndDelete.yml new file mode 100644 index 0000000000..3788733216 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndDelete.yml @@ -0,0 +1,124 @@ +description: findOneAndDelete + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndDelete is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + description: 'FindOneAndDelete is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2, x: 22 } + - + description: 'FindOneAndDelete is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndDelete + arguments: + filter: { x: { $gte: 11 } } + sort: { x: 1 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json b/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json new file mode 100644 index 0000000000..78db52e75d --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.json @@ -0,0 +1,301 @@ +{ + "description": "findOneAndReplace-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndReplace succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml b/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml new file mode 100644 index 0000000000..254b61980d --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace-errorLabels.yml @@ -0,0 +1,165 @@ +description: findOneAndReplace-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json b/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json new file mode 100644 index 0000000000..1c355c3ebf --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "findOneAndReplace-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndReplace fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml b/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml new file mode 100644 index 0000000000..090356badd --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace-serverErrors.yml @@ -0,0 +1,69 @@ +description: findOneAndReplace-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndReplace.json b/source/retryable-writes/tests/unified/findOneAndReplace.json new file mode 100644 index 0000000000..638d15a41d --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace.json @@ -0,0 +1,243 @@ +{ + "description": "findOneAndReplace", + "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": "FindOneAndReplace is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "FindOneAndReplace is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndReplace is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndReplace.yml b/source/retryable-writes/tests/unified/findOneAndReplace.yml new file mode 100644 index 0000000000..cc2468264c --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndReplace.yml @@ -0,0 +1,129 @@ +description: findOneAndReplace + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndReplace is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + description: 'FindOneAndReplace is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndReplace is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndReplace + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: Before + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json b/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json new file mode 100644 index 0000000000..38b3f7ba44 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.json @@ -0,0 +1,305 @@ +{ + "description": "findOneAndUpdate-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndUpdate succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml b/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml new file mode 100644 index 0000000000..034edbe77e --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate-errorLabels.yml @@ -0,0 +1,165 @@ +description: findOneAndUpdate-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + # Driver retries operation and it succeeds + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ findAndModify ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json b/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json new file mode 100644 index 0000000000..150012ac72 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.json @@ -0,0 +1,120 @@ +{ + "description": "findOneAndUpdate-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "FindOneAndUpdate fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml b/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml new file mode 100644 index 0000000000..8f9765fc19 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate-serverErrors.yml @@ -0,0 +1,69 @@ +description: findOneAndUpdate-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ findAndModify ] + closeConnection: true + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate.json b/source/retryable-writes/tests/unified/findOneAndUpdate.json new file mode 100644 index 0000000000..eefe98ae11 --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate.json @@ -0,0 +1,245 @@ +{ + "description": "findOneAndUpdate", + "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": "FindOneAndUpdate is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "findAndModify", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/findOneAndUpdate.yml b/source/retryable-writes/tests/unified/findOneAndUpdate.yml new file mode 100644 index 0000000000..51c77f108c --- /dev/null +++ b/source/retryable-writes/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,128 @@ +description: findOneAndUpdate + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'FindOneAndUpdate is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: findAndModify + command: + txnNumber: { $$exists: true } + - + description: 'FindOneAndUpdate is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'FindOneAndUpdate is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: findOneAndUpdate + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/handshakeError.json b/source/retryable-writes/tests/unified/handshakeError.json new file mode 100644 index 0000000000..93cb2e849e --- /dev/null +++ b/source/retryable-writes/tests/unified/handshakeError.json @@ -0,0 +1,2015 @@ +{ + "description": "retryable writes handshake failures", + "schemaVersion": "1.4", + "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": "client.clientBulkWrite succeeds after retryable handshake network error", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-handshake-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + } + } + ], + "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": "bulkWrite" + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "client.clientBulkWrite succeeds after retryable handshake server error (ShutdownInProgress)", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "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": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "retryable-writes-handshake-tests.coll", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + } + } + ], + "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": "bulkWrite" + } + }, + { + "commandSucceededEvent": { + "commandName": "bulkWrite" + } + } + ] + } + ] + }, + { + "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/source/retryable-writes/tests/unified/handshakeError.yml b/source/retryable-writes/tests/unified/handshakeError.yml new file mode 100644 index 0000000000..1743463370 --- /dev/null +++ b/source/retryable-writes/tests/unified/handshakeError.yml @@ -0,0 +1,877 @@ +# Tests in this file are generated from handshakeError.yml.template. + +description: "retryable writes handshake failures" + +schemaVersion: "1.4" # For `serverless: forbid` + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + auth: true + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName retryable-writes-handshake-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + +tests: + # Because setting a failPoint creates a connection in the connection pool, run + # a ping operation that fails immediately after the failPoint operation in + # order to discard the connection before running the actual operation to be + # tested. The saslContinue command is used to avoid SDAM errors. + # + # Description of events: + # - Failpoint operation. + # - Creates a connection in the connection pool that must be closed. + # - Ping operation. + # - Triggers failpoint (first time). + # - Closes the connection made by the fail point operation. + # - Test operation. + # - New connection is created. + # - Triggers failpoint (second time). + # - Tests whether operation successfully retries the handshake and succeeds. + + - description: "client.clientBulkWrite succeeds after retryable handshake network error" + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + serverless: forbid + 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: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: retryable-writes-handshake-tests.coll + document: { _id: 8, x: 88 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: bulkWrite + - commandSucceededEvent: + commandName: bulkWrite + + - description: "client.clientBulkWrite succeeds after retryable handshake server error (ShutdownInProgress)" + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + serverless: forbid + 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: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: retryable-writes-handshake-tests.coll + document: { _id: 8, x: 88 } + expectEvents: + - client: *client + eventType: cmap + events: + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - { connectionCheckOutStartedEvent: {} } + - client: *client + events: + - commandStartedEvent: + command: { ping: 1 } + databaseName: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: bulkWrite + - commandSucceededEvent: + commandName: bulkWrite + + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - 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: *databaseName + - commandFailedEvent: + commandName: ping + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert diff --git a/source/retryable-writes/tests/unified/insertMany-errorLabels.json b/source/retryable-writes/tests/unified/insertMany-errorLabels.json new file mode 100644 index 0000000000..5254ba7cb2 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany-errorLabels.json @@ -0,0 +1,335 @@ +{ + "description": "insertMany-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertMany succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertMany succeeds after WriteConcernError ShutdownInProgress", + "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" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertMany-errorLabels.yml b/source/retryable-writes/tests/unified/insertMany-errorLabels.yml new file mode 100644 index 0000000000..631b3a5762 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany-errorLabels.yml @@ -0,0 +1,185 @@ +description: insertMany-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + # Driver retries operation and it succeeds + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertMany succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertMany succeeds after WriteConcernError ShutdownInProgress' + 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' + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/source/retryable-writes/tests/unified/insertMany-serverErrors.json b/source/retryable-writes/tests/unified/insertMany-serverErrors.json new file mode 100644 index 0000000000..f5f513603c --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany-serverErrors.json @@ -0,0 +1,114 @@ +{ + "description": "insertMany-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertMany-serverErrors.yml b/source/retryable-writes/tests/unified/insertMany-serverErrors.yml new file mode 100644 index 0000000000..f229428bd7 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany-serverErrors.yml @@ -0,0 +1,68 @@ +description: insertMany-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } diff --git a/source/retryable-writes/tests/unified/insertMany.json b/source/retryable-writes/tests/unified/insertMany.json new file mode 100644 index 0000000000..35a18c46c6 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany.json @@ -0,0 +1,290 @@ +{ + "description": "insertMany", + "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 + } + ] + } + ], + "tests": [ + { + "description": "InsertMany succeeds after one network error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "InsertMany with unordered execution", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": false + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "InsertMany fails after multiple network errors", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": "alwaysOn", + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ], + "ordered": true + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertMany.yml b/source/retryable-writes/tests/unified/insertMany.yml new file mode 100644 index 0000000000..0ead50da7d --- /dev/null +++ b/source/retryable-writes/tests/unified/insertMany.yml @@ -0,0 +1,158 @@ +description: insertMany + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + +tests: + - + description: 'InsertMany succeeds after one network error' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: true + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + description: 'InsertMany with unordered execution' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: false + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + description: 'InsertMany fails after multiple network errors' + operations: + - + # Normally, a mongod will insert the documents as a batch with a single + # commit. If this fails, mongod may try to insert each document one at a + # time depending on the failure. Therefore our single insert command may + # trigger the failpoint twice on each driver attempt. This test + # permanently enables the fail point to ensure the retry attempt always + # fails. + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: alwaysOn + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + ordered: true + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } diff --git a/source/retryable-writes/tests/unified/insertOne-errorLabels.json b/source/retryable-writes/tests/unified/insertOne-errorLabels.json new file mode 100644 index 0000000000..39f31a8aa6 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne-errorLabels.json @@ -0,0 +1,1127 @@ +{ + "description": "insertOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "InsertOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [] + } + ] + }, + { + "description": "InsertOne succeeds after NotWritablePrimary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 10107, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 13436, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 13435, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11602, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after HostNotFound", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after HostUnreachable", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 6, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after SocketException", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 9001, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after NetworkTimeout", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after ExceededTimeLimit", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 262, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11600, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11602, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 189, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after WriteConcernError ShutdownInProgress", + "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" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "InsertOne fails after multiple retryable writeConcernErrors", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertOne-errorLabels.yml b/source/retryable-writes/tests/unified/insertOne-errorLabels.yml new file mode 100644 index 0000000000..ce52421772 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne-errorLabels.yml @@ -0,0 +1,610 @@ +description: insertOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'InsertOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + # Driver retries operation and it succeeds + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'InsertOne succeeds after NotWritablePrimary' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 10107 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NotPrimaryOrSecondary' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 13436 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NotPrimaryNoSecondaryOk' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 13435 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after InterruptedDueToReplStateChange' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11602 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after InterruptedAtShutdown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11600 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 189 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 91 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after HostNotFound' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 7 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after HostUnreachable' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 6 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after SocketException' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 9001 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after NetworkTimeout' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 89 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after ExceededTimeLimit' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 262 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError InterruptedAtShutdown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11600 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11602 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 189 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne succeeds after WriteConcernError ShutdownInProgress' + 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' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - + description: 'InsertOne fails after multiple retryable writeConcernErrors' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } # The write was still applied. diff --git a/source/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json b/source/retryable-writes/tests/unified/insertOne-noWritesPerformedError.json new file mode 100644 index 0000000000..3194e91c5c --- /dev/null +++ b/source/retryable-writes/tests/unified/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/source/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml b/source/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml new file mode 100644 index 0000000000..6d8e8e7d4b --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne-noWritesPerformedError.yml @@ -0,0 +1,51 @@ +description: "retryable-writes insertOne noWritesPerformedErrors" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "6.0" + topologies: [ replicaset ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandFailedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &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: *collectionName + databaseName: *databaseName + documents: [] diff --git a/source/retryable-writes/tests/unified/insertOne-serverErrors.json b/source/retryable-writes/tests/unified/insertOne-serverErrors.json new file mode 100644 index 0000000000..8edafb7029 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne-serverErrors.json @@ -0,0 +1,864 @@ +{ + "description": "retryable-writes insertOne serverErrors", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "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.3.1" + } + ], + "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 + } + ] + } + ] + }, + { + "description": "RetryableWriteError label is added based on top-level code in pre-4.4 server response", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "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 + } + ] + } + ] + }, + { + "description": "RetryableWriteError label is added based on writeConcernError in pre-4.4 mongod response", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "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 + } + ] + } + ] + }, + { + "description": "RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "maxServerVersion": "4.2.99", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "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 + } + ] + } + ] + }, + { + "description": "InsertOne succeeds after connection failure", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails after connection failure when retryWrites option is false", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + } + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertOne fails after Interrupted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601, + "closeConnection": false + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertOne fails after WriteConcernError Interrupted", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 11601, + "errmsg": "operation was interrupted" + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails after WriteConcernError WriteConcernTimeout", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 64, + "errmsg": "waiting for replication timed out", + "errInfo": { + "wtimeout": true + } + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertOne-serverErrors.yml b/source/retryable-writes/tests/unified/insertOne-serverErrors.yml new file mode 100644 index 0000000000..6fd43365d8 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne-serverErrors.yml @@ -0,0 +1,405 @@ +description: "retryable-writes insertOne serverErrors" + +schemaVersion: "1.9" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.7" + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - description: "InsertOne succeeds after retryable writeConcernError" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorLabels: [RetryableWriteError] # top-level error labels + writeConcernError: + code: 91 # ShutdownInProgress + 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: *collectionName + documents: [{ _id: 3, x: 33 }] + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: [{ _id: 3, x: 33 }] + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied + + - description: "RetryableWriteError label is added based on top-level code in pre-4.4 server response" + runOnRequirements: + - minServerVersion: "4.2" + maxServerVersion: "4.2.99" + topologies: [ replicaset, sharded ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + # Trigger the fail point twice to allow asserting the error label in + # the retry attempt's response. + mode: { times: 2 } + data: + failCommands: [ "insert" ] + errorCode: 189 # PrimarySteppedDown + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3, x: 33 } + expectError: + errorLabelsContain: [ "RetryableWriteError" ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &insertCommandStartedEvent + command: + insert: *collectionName + documents: [{ _id: 3, x: 33 }] + commandName: insert + databaseName: *databaseName + - commandStartedEvent: *insertCommandStartedEvent + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + + - description: "RetryableWriteError label is added based on writeConcernError in pre-4.4 mongod response" + runOnRequirements: + - minServerVersion: "4.2" + maxServerVersion: "4.2.99" + topologies: [ replicaset ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + # Trigger the fail point twice to allow asserting the error label in + # the retry attempt's response. + mode: { times: 2 } + data: + failCommands: [ "insert" ] + writeConcernError: + code: 91 # ShutdownInProgress + errmsg: "Replication is being shut down" + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3, x: 33 } + expectError: + errorLabelsContain: [ "RetryableWriteError" ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: *insertCommandStartedEvent + - commandStartedEvent: *insertCommandStartedEvent + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + # writeConcernError doesn't prevent the server from applying the write + - { _id: 3, x: 33 } + + - description: "RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response" + runOnRequirements: + - minServerVersion: "4.2" + maxServerVersion: "4.2.99" + topologies: [ sharded ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + # Trigger the fail point only once since a RetryableWriteError label + # will not be added and the write will not be retried. + mode: { times: 1 } + data: + failCommands: [ "insert" ] + writeConcernError: + code: 91 # ShutdownInProgress + errmsg: "Replication is being shut down" + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3, x: 33 } + expectError: + errorLabelsOmit: [ "RetryableWriteError" ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: *insertCommandStartedEvent + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + # writeConcernError doesn't prevent the server from applying the write + - { _id: 3, x: 33 } + - + description: 'InsertOne succeeds after connection failure' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertOne fails after connection failure when retryWrites option is false' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - + name: failPoint + object: testRunner + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection1 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + # If retryWrites is false, the driver should not add the + # RetryableWriteError label to the error. + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'InsertOne fails after Interrupted' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11601 + closeConnection: false + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'InsertOne fails after WriteConcernError Interrupted' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + writeConcernError: + code: 11601 + errmsg: 'operation was interrupted' + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied. + - + description: 'InsertOne fails after WriteConcernError WriteConcernTimeout' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + writeConcernError: + code: 64 + errmsg: 'waiting for replication timed out' + errInfo: + wtimeout: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied. + - + description: 'InsertOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + closeConnection: true + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/insertOne.json b/source/retryable-writes/tests/unified/insertOne.json new file mode 100644 index 0000000000..a6afdbf224 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne.json @@ -0,0 +1,245 @@ +{ + "description": "insertOne", + "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 is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "InsertOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "InsertOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/insertOne.yml b/source/retryable-writes/tests/unified/insertOne.yml new file mode 100644 index 0000000000..9b45634823 --- /dev/null +++ b/source/retryable-writes/tests/unified/insertOne.yml @@ -0,0 +1,127 @@ +description: insertOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'InsertOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: true } + - + description: 'InsertOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - + description: 'InsertOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3, x: 33 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/replaceOne-errorLabels.json b/source/retryable-writes/tests/unified/replaceOne-errorLabels.json new file mode 100644 index 0000000000..22c4561ae7 --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne-errorLabels.json @@ -0,0 +1,300 @@ +{ + "description": "replaceOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "ReplaceOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/replaceOne-errorLabels.yml b/source/retryable-writes/tests/unified/replaceOne-errorLabels.yml new file mode 100644 index 0000000000..38f271d563 --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne-errorLabels.yml @@ -0,0 +1,170 @@ +description: replaceOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + # Driver retries operation and it succeeds + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/replaceOne-serverErrors.json b/source/retryable-writes/tests/unified/replaceOne-serverErrors.json new file mode 100644 index 0000000000..c957db7244 --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne-serverErrors.json @@ -0,0 +1,118 @@ +{ + "description": "replaceOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "ReplaceOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/replaceOne-serverErrors.yml b/source/retryable-writes/tests/unified/replaceOne-serverErrors.yml new file mode 100644 index 0000000000..b6f2f65bdf --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne-serverErrors.yml @@ -0,0 +1,68 @@ +description: replaceOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/replaceOne.json b/source/retryable-writes/tests/unified/replaceOne.json new file mode 100644 index 0000000000..ee6e37d3bb --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne.json @@ -0,0 +1,242 @@ +{ + "description": "replaceOne", + "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": "ReplaceOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "ReplaceOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 111 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/replaceOne.yml b/source/retryable-writes/tests/unified/replaceOne.yml new file mode 100644 index 0000000000..90fc559037 --- /dev/null +++ b/source/retryable-writes/tests/unified/replaceOne.yml @@ -0,0 +1,132 @@ +description: replaceOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'ReplaceOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - + description: 'ReplaceOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 111 } + - { _id: 2, x: 22 } + - + description: 'ReplaceOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: replaceOne + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/unacknowledged-write-concern.json b/source/retryable-writes/tests/unified/unacknowledged-write-concern.json new file mode 100644 index 0000000000..eaa114acfd --- /dev/null +++ b/source/retryable-writes/tests/unified/unacknowledged-write-concern.json @@ -0,0 +1,77 @@ +{ + "description": "unacknowledged write does not set txnNumber", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + } + ], + "tests": [ + { + "description": "unacknowledged write does not set txnNumber", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "x": 11 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/unacknowledged-write-concern.yml b/source/retryable-writes/tests/unified/unacknowledged-write-concern.yml new file mode 100644 index 0000000000..3a0cce6ae8 --- /dev/null +++ b/source/retryable-writes/tests/unified/unacknowledged-write-concern.yml @@ -0,0 +1,40 @@ +description: "unacknowledged write does not set txnNumber" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "3.6" + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name retryable-writes-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + collectionOptions: + writeConcern: { w: 0 } + +tests: + - description: "unacknowledged write does not set txnNumber" + operations: + - object: *collection0 + name: insertOne + arguments: + document: { _id: 1, x: 11 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: insert + command: + txnNumber: { $$exists: false } diff --git a/source/retryable-writes/tests/unified/updateMany.json b/source/retryable-writes/tests/unified/updateMany.json new file mode 100644 index 0000000000..12c5204ee9 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateMany.json @@ -0,0 +1,112 @@ +{ + "description": "updateMany", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "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": "UpdateMany ignores retryWrites", + "operations": [ + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "filter": {}, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 23 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/updateMany.yml b/source/retryable-writes/tests/unified/updateMany.yml new file mode 100644 index 0000000000..d1febec309 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateMany.yml @@ -0,0 +1,62 @@ +description: updateMany + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateMany ignores retryWrites' + operations: + - + object: *collection0 + name: updateMany + arguments: + filter: { } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 23 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: false } diff --git a/source/retryable-writes/tests/unified/updateOne-errorLabels.json b/source/retryable-writes/tests/unified/updateOne-errorLabels.json new file mode 100644 index 0000000000..e44cef45f6 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne-errorLabels.json @@ -0,0 +1,304 @@ +{ + "description": "updateOne-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "UpdateOne succeeds with RetryableWriteError from server", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne fails if server does not return RetryableWriteError", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne succeeds after PrimarySteppedDown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/updateOne-errorLabels.yml b/source/retryable-writes/tests/unified/updateOne-errorLabels.yml new file mode 100644 index 0000000000..f530e8dba4 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne-errorLabels.yml @@ -0,0 +1,170 @@ +description: updateOne-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne succeeds with RetryableWriteError from server' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + # Driver retries operation and it succeeds + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne fails if server does not return RetryableWriteError' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + # Driver does not retry operation because there was no RetryableWriteError label on response + expectError: + isError: true + errorLabelsOmit: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne succeeds after PrimarySteppedDown' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorCode: 189 + errorLabels: + - RetryableWriteError + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne succeeds after WriteConcernError ShutdownInProgress' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ update ] + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/updateOne-serverErrors.json b/source/retryable-writes/tests/unified/updateOne-serverErrors.json new file mode 100644 index 0000000000..648834ada4 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne-serverErrors.json @@ -0,0 +1,119 @@ +{ + "description": "updateOne-serverErrors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "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": "UpdateOne fails with a RetryableWriteError label after two connection failures", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/updateOne-serverErrors.yml b/source/retryable-writes/tests/unified/updateOne-serverErrors.yml new file mode 100644 index 0000000000..6cd7281db1 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne-serverErrors.yml @@ -0,0 +1,71 @@ +# This file was created automatically using mongodb-spec-converter. +# Please review the generated file, then remove this notice. + +description: updateOne-serverErrors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: [ replicaset ] + - + minServerVersion: 4.1.7 + topologies: [ sharded, load-balanced ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne fails with a RetryableWriteError label after two connection failures' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ update ] + closeConnection: true + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + errorLabelsContain: + - RetryableWriteError + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/unified/updateOne.json b/source/retryable-writes/tests/unified/updateOne.json new file mode 100644 index 0000000000..99ffba8e21 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne.json @@ -0,0 +1,424 @@ +{ + "description": "updateOne", + "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": "UpdateOne is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + }, + { + "commandStartedEvent": { + "commandName": "update", + "command": { + "txnNumber": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "UpdateOne is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 3 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is not committed on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 3 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "UpdateOne with upsert is never committed", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "filter": { + "_id": 3, + "x": 33 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + } + ] +} diff --git a/source/retryable-writes/tests/unified/updateOne.yml b/source/retryable-writes/tests/unified/updateOne.yml new file mode 100644 index 0000000000..5c255b0da8 --- /dev/null +++ b/source/retryable-writes/tests/unified/updateOne.yml @@ -0,0 +1,225 @@ +description: updateOne + +schemaVersion: '1.0' + +runOnRequirements: + - + minServerVersion: '3.6' + topologies: [ replicaset ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name retryable-writes-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name coll + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - + description: 'UpdateOne is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - commandStartedEvent: + commandName: update + command: + txnNumber: { $$exists: true } + - + description: 'UpdateOne is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - + description: 'UpdateOne with upsert is committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + - + description: 'UpdateOne with upsert is not committed on first attempt' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 3 + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + - + description: 'UpdateOne with upsert is never committed' + operations: + - + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: + failBeforeCommitExceptionCode: 1 + - + object: *collection0 + name: updateOne + arguments: + filter: { _id: 3, x: 33 } + update: { $inc: { x: 1 } } + upsert: true + expectError: + isError: true + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/updateOne-serverErrors.json b/source/retryable-writes/tests/updateOne-serverErrors.json deleted file mode 100644 index 84ebf877ad..0000000000 --- a/source/retryable-writes/tests/updateOne-serverErrors.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.99", - "tests": [ - { - "description": "UpdateOne succeeds after PrimarySteppedDown", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne succeeds after WriteConcernError ShutdownInProgress", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/updateOne-serverErrors.yml b/source/retryable-writes/tests/updateOne-serverErrors.yml deleted file mode 100644 index 1d468861d6..0000000000 --- a/source/retryable-writes/tests/updateOne-serverErrors.yml +++ /dev/null @@ -1,58 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -# TODO: this should change to 4.0 once 4.0.0 is released. -minServerVersion: '3.99' - -tests: - - - description: "UpdateOne succeeds after PrimarySteppedDown" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - errorCode: 189 - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "UpdateOne succeeds after WriteConcernError ShutdownInProgress" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["update"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } diff --git a/source/retryable-writes/tests/updateOne.json b/source/retryable-writes/tests/updateOne.json deleted file mode 100644 index c342c56278..0000000000 --- a/source/retryable-writes/tests/updateOne.json +++ /dev/null @@ -1,299 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "3.6", - "tests": [ - { - "description": "UpdateOne is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is not committed on first attempt", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is never committed", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/source/retryable-writes/tests/updateOne.yml b/source/retryable-writes/tests/updateOne.yml deleted file mode 100644 index 900d4e7fb3..0000000000 --- a/source/retryable-writes/tests/updateOne.yml +++ /dev/null @@ -1,138 +0,0 @@ -data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - -minServerVersion: '3.6' - -tests: - - - description: "UpdateOne is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "UpdateOne is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - collection: - data: - - { _id: 1, x: 12 } - - { _id: 2, x: 22 } - - - description: "UpdateOne is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 1 } - update: { $inc: { x : 1 }} - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - - description: "UpdateOne with upsert is committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 34 } - - - description: "UpdateOne with upsert is not committed on first attempt" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 1 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 3 - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } - - { _id: 3, x: 34 } - - - description: "UpdateOne with upsert is never committed" - clientOptions: - retryWrites: true - failPoint: - configureFailPoint: onPrimaryTransactionalWrite - mode: { times: 2 } - data: { failBeforeCommitExceptionCode: 1 } - operation: - name: "updateOne" - arguments: - filter: { _id: 3, x: 33 } - update: { $inc: { x : 1 }} - upsert: true - outcome: - error: true - collection: - data: - - { _id: 1, x: 11 } - - { _id: 2, x: 22 } diff --git a/source/run-command/run-command.md b/source/run-command/run-command.md new file mode 100644 index 0000000000..91abeb1a7e --- /dev/null +++ b/source/run-command/run-command.md @@ -0,0 +1,339 @@ +# Run Command + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +This specification defines requirements and behaviors for drivers' run command and related APIs. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +Command\ +A structure representing a BSON document that has a shape matching a supported MongoDB operation. + +### Implementation requirements + +All drivers MAY offer the operations defined in the following sections. This does not preclude a driver from offering +more. + +### Deviations + +Please refer to [The CRUD specification's Guidance](../crud/crud.md#guidance) on how APIs may deviate between languages. + +Cursor iterating APIs MAY be offered via language syntax or predefined iterable methods. + +### `runCommand` + +The following represents how a runCommand API SHOULD be exposed. + +```typescript +interface Database { + /** + * Takes an argument representing an arbitrary BSON document and executes it against the server. + */ + runCommand(command: BSONDocument, options: RunCommandOptions): BSONDocument; +} + +interface RunCommandOptions { + /** + * An optional readPreference setting to apply to server selection logic. + * This value MUST be applied to the command document as the $readPreference global command argument if not set to primary. + * + * @defaultValue ReadPreference(mode: primary) + * + * @see ../server-selection/server-selection.md#read-preference + */ + readPreference?: ReadPreference; + + /** + * An optional explicit client session. + * The associated logical session id (`lsid`) the driver MUST apply to the command. + * + * @see ../sessions/driver-sessions.md#clientsession + */ + session?: ClientSession; + + /** + * An optional timeout option to govern the amount of time that a single operation can execute before control is returned to the user. + * This timeout applies to all of the work done to execute the operation, including but not limited to server selection, connection checkout, and server-side execution. + * + * @ see https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/client-side-operations-timeout.md + */ + timeoutMS?: number; +} +``` + +#### RunCommand implementation details + +RunCommand provides a way to access MongoDB server commands directly without requiring a driver to implement a bespoke +helper. The API is intended to take a document from a user and apply a number of common driver internal concerns before +forwarding the command to a server. A driver MUST not inspect the user's command, this includes checking for the fields +a driver MUST attach to the command sent as described below. Depending on a driver's BSON implementation this can result +in these fields being overwritten or duplicated, a driver SHOULD document that using these fields has undefined +behavior. A driver MUST not modify the user's command, a clone SHOULD be created before the driver attaches any of the +required fields to the command. + +Drivers that have historically modified user input SHOULD strive to instead clone the input such that appended fields do +not affect the user's input in their next major version. + +##### OP_MSG + +The `$db` global command argument MUST be set on the command sent to the server and it MUST equal the database name +RunCommand was invoked on. + +- See OP_MSG's section on [Global Command Arguments](../message/OP_MSG.md#global-command-arguments) + +##### ReadPreference + +For the purposes of server selection RunCommand MUST assume all commands are read operations. To facilitate server +selection the RunCommand operation MUST accept an optional `readPreference` option. + +- See Server Selection's section on + [Use of read preferences with commands](../server-selection/server-selection.md#use-of-read-preferences-with-commands) + +If the provided ReadPreference is NOT `{mode: primary}` and the selected server is NOT a standalone, the command sent +MUST include the `$readPreference` global command argument. + +- See OP_MSG's section on [Global Command Arguments](../message/OP_MSG.md#global-command-arguments) + +##### Driver Sessions + +A driver's RunCommand MUST provide an optional session option to support explicit sessions and transactions. If a +session is not provided the driver MUST attach an implicit session if the connection supports sessions. Drivers MUST NOT +attempt to check the command document for the presence of an `lsid`. + +Every ClientSession has a corresponding logical session ID representing the server-side session ID. The logical session +ID MUST be included under `lsid` in the command sent to the server without modifying user input. + +- See Driver Sessions' section on + [Sending the session ID to the server on all commands](../sessions/driver-sessions.md#sending-the-session-id-to-the-server-on-all-commands) + +The command sent to the server MUST gossip the `$clusterTime` if cluster time support is detected. + +- See Driver Sessions' section on + [Gossipping the cluster time](../sessions/driver-sessions.md#gossipping-the-cluster-time) + +##### Transactions + +If RunCommand is used within a transaction the read preference MUST be sourced from the transaction's options. The +command sent to the server MUST include the transaction specific fields, summarized as follows: + +- If `runCommand` is executing within a transaction: + - `autocommit` - The autocommit flag MUST be set to false. + - `txnNumber` - MUST be set. +- If `runCommand` is the first operation of the transaction: + - `startTransaction` - MUST be set to true. + - `readConcern` - MUST be set to the transaction's read concern if it is NOT the default. +- See + [Generic RunCommand helper within a transaction](../transactions/transactions.md#generic-runcommand-helper-within-a-transaction) + in the Transactions specification. + +##### ReadConcern and WriteConcern + +RunCommand MUST NOT support read concern and write concern options. Drivers MUST NOT attempt to check the command +document for the presence of a `readConcern` and `writeConcern` field. + +Additionally, unless executing within a transaction, RunCommand MUST NOT set the `readConcern` or `writeConcern` fields +in the command document. For example, default values MUST NOT be inherited from client, database, or collection options. + +If the user-provided command document already includes `readConcern` or `writeConcern` fields, the values MUST be left +as-is. + +- See Read Concern's section on + [Generic Command Method](../read-write-concern/read-write-concern.md#generic-command-method) +- See Write Concern's section on + [Generic Command Method](../read-write-concern/read-write-concern.md#generic-command-method) + +##### Retryability + +All commands executed via RunCommand are non-retryable operations. Drivers MUST NOT inspect the command to determine if +it is a write and MUST NOT attach a `txnNumber`. + +- See Retryable Reads' section on + [Unsupported Read Operations](../retryable-reads/retryable-reads.md#unsupported-read-operations) +- See Retryable Writes' section on + [Behavioral Changes for Write Commands](../retryable-writes/retryable-writes.md#behavioral-changes-for-write-commands) + +##### Stable API + +The command sent MUST attach stable API fields as configured on the MongoClient. + +- See Stable API's section on + [Generic Command Helper Behaviour](../versioned-api/versioned-api.md#generic-command-helper) + +##### Client Side Operations Timeout + +RunCommand MUST provide an optional `timeoutMS` option to support client side operations timeout. Drivers MUST NOT +attempt to check the command document for the presence of a `maxTimeMS` field. Drivers MUST document the behavior of +RunCommand if a `maxTimeMS` field is already set on the command (such as overwriting the command field). + +- See Client Side Operations Timeout's section on + [runCommand](../client-side-operations-timeout/client-side-operations-timeout.md#runcommand) +- See Client Side Operations Timeout's section on + [runCommand behavior](../client-side-operations-timeout/client-side-operations-timeout.md#runcommand-behavior) + +### `runCursorCommand` + +Drivers MAY expose a runCursorCommand API with the following syntax. + +```typescript +interface Database { + /** + * Takes an argument representing an arbitrary BSON document and executes it against the server. + */ + runCursorCommand(command: BSONDocument, options: RunCursorCommandOptions): RunCommandCursor; +} + +interface RunCursorCommandOptions extends RunCommandOptions { + /** + * This option is an enum with possible values CURSOR_LIFETIME and ITERATION. + * For operations that create cursors, timeoutMS can either cap the lifetime of the cursor or be applied separately to the original operation and all subsequent calls. + * To support both of these use cases, these operations MUST support a timeoutMode option. + * + * @defaultValue CURSOR_LIFETIME + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/client-side-operations-timeout.md + */ + timeoutMode?: ITERATION | CURSOR_LIFETIME; + + /** + * See the `cursorType` enum defined in the crud specification. + * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#read + * + * Identifies the type of cursor this is for client side operations timeout to properly apply timeoutMode settings. + * + * A tailable cursor can receive empty `nextBatch` arrays in `getMore` responses. + * However, subsequent `getMore` operations may return documents if new data has become available. + * + * A tailableAwait cursor is an enhancement where instead of dealing with empty responses the server will block until data becomes available. + * + * @defaultValue NON_TAILABLE + */ + cursorType?: CursorType; +} + +/** + * The following are the configurations a driver MUST provide to control how getMores are constructed. + * How the options are controlled should be idiomatic to the driver's language. + * See Executing ``getMore`` Commands. + */ +interface RunCursorCommandGetMoreOptions { + /** Any positive integer is permitted. */ + batchSize?: int; + /** Any non-negative integer is permitted. */ + maxTimeMS?: int; + comment?: BSONValue; +} +``` + +#### RunCursorCommand implementation details + +RunCursorCommand provides a way to access MongoDB server commands that return a cursor directly without requiring a +driver to implement a bespoke cursor implementation. The API is intended to be built upon RunCommand and take a document +from a user and apply a number of common driver internal concerns before forwarding the command to a server. A driver +can expect that the result from running this command will return a document with a `cursor` field and MUST provide the +caller with a language native abstraction to continue iterating the results from the server. If the response from the +server does not include a `cursor` field the driver MUST throw an error either before returning from `runCursorCommand` +or upon first iteration of the cursor. + +High level RunCursorCommand steps: + +- Run the cursor creating command provided by the caller and retain the ClientSession used as well as the server the + command was executed on. +- Create a local cursor instance and store the `firstBatch`, `ns`, and `id` from the response. +- When the current batch has been fully iterated, execute a `getMore` using the same server the initial command was + executed on. +- Store the `nextBatch` from the `getMore` response and update the cursor's `id`. +- Continue to execute `getMore` commands as needed when the caller empties local batches until the cursor is exhausted + or closed (i.e. `id` is zero). + +##### Driver Sessions + +A driver MUST create an implicit ClientSession if none is provided and it MUST be attached for the duration of the +cursor's lifetime. All `getMore` commands constructed for this cursor MUST send the same `lsid` used on the initial +command. A cursor is considered exhausted or closed when the server reports its `id` as zero. When the cursor is +exhausted the client session MUST be ended and the server session returned to the pool as early as possible rather than +waiting for a caller to completely iterate the final batch. + +- See Drivers Sessions' section on [Sessions and Cursors](../sessions/driver-sessions.md#sessions-and-cursors) + +##### Server Selection + +RunCursorCommand MUST support a `readPreference` option that MUST be used to determine server selection. The selected +server MUST be used for subsequent `getMore` commands. + +##### Load Balancers + +When in `loadBalanced` mode, a driver MUST pin the connection used to execute the initial operation, and reuse it for +subsequent `getMore` operations. + +- See Load Balancer's section on [Behaviour With Cursors](../load-balancers/load-balancers.md#behaviour-with-cursors) + +##### Iterating the Cursor + +Drivers MUST provide an API, typically, a method named `next()`, that returns one document per invocation. If the +cursor's batch is empty and the cursor id is nonzero, the driver MUST perform a `getMore` operation. + +##### Executing `getMore` Commands + +The cursor API returned to the caller MUST offer an API to configure `batchSize`, `maxTimeMS`, and `comment` options +that are sent on subsequent `getMore` commands. If it is idiomatic for a driver to allow setting these options in +`RunCursorCommandOptions`, the driver MUST document that the options only pertain to `getMore` commands. A driver MAY +permit users to change `getMore` field settings at any time during the cursor's lifetime and subsequent `getMore` +commands MUST be constructed with the changes to those fields. If that API is offered drivers MUST write tests asserting +`getMore` commands are constructed with any updated fields. + +- See Find, getMore and killCursors commands' section on + [GetMore](../find_getmore_killcursors_commands/find_getmore_killcursors_commands.md#getmore) + +##### Tailable and TailableAwait + +- **See first:** Find, getMore and killCursors commands's section on + [Tailable cursors](../find_getmore_killcursors_commands/find_getmore_killcursors_commands.md#tailable-cursors) + +It is the responsibility of the caller to construct their initial command with `awaitData` and `tailable` flags **as +well as** inform RunCursorCommand of the `cursorType` that should be constructed. Requesting a `cursorType` that does +not align with the fields sent to the server on the initial command SHOULD be documented as undefined behavior. + +##### Resource Cleanup + +Drivers MUST provide an explicit mechanism for releasing the cursor resources, typically a `.close()` method. If the +cursor id is nonzero a KillCursors operation MUST be attempted, the result of the operation SHOULD be ignored. The +ClientSession associated with the cursor MUST be ended and the ServerSession returned to the pool. + +- See Driver Sessions' section on + [When sending a killCursors command](../sessions/driver-sessions.md#when-sending-a-killcursors-command) +- See Find, getMore and killCursors commands' section on + [killCursors](../find_getmore_killcursors_commands/find_getmore_killcursors_commands.md#killcursors) + +##### Client Side Operations Timeout + +RunCursorCommand MUST provide an optional `timeoutMS` option to support client side operations timeout. Drivers MUST NOT +attempt to check the command document for the presence of a `maxTimeMS` field. Drivers MUST document the behavior of +RunCursorCommand if a `maxTimeMS` field is already set on the command. Drivers SHOULD raise an error if both `timeoutMS` +and the `getMore`-specific `maxTimeMS` option are specified (see: +[Executing getMore Commands](#executing-getmore-commands)). Drivers MUST document that attempting to set both options +can have undefined behavior and is not supported. + +When `timeoutMS` and `timeoutMode` are provided the driver MUST support timeout functionality as described in the CSOT +specification. + +- See Client Side Operations Timeout's section on + [Cursors](../client-side-operations-timeout/client-side-operations-timeout.md#cursors) + +## Changelog + +- 2024-09-02: Migrated from reStructuredText to Markdown. +- 2023-05-10: Add runCursorCommand API specification. +- 2023-05-08: `$readPreference` is not sent to standalone servers +- 2023-04-20: Add run command specification. diff --git a/source/run-command/run-command.rst b/source/run-command/run-command.rst new file mode 100644 index 0000000000..672c1389bd --- /dev/null +++ b/source/run-command/run-command.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `run-command.md `_. diff --git a/source/run-command/tests/README.md b/source/run-command/tests/README.md new file mode 100644 index 0000000000..ba8c92ad56 --- /dev/null +++ b/source/run-command/tests/README.md @@ -0,0 +1,9 @@ +# Run Command Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in the `unified` sub-directories are platform-independent tests that drivers can use to prove +their conformance to the RunCommand spec. Tests in the `unified` directory are written using the +[Unified Test Format](../../unified-test-format/unified-test-format.md). diff --git a/source/run-command/tests/unified/runCommand.json b/source/run-command/tests/unified/runCommand.json new file mode 100644 index 0000000000..fde9de92e6 --- /dev/null +++ b/source/run-command/tests/unified/runCommand.json @@ -0,0 +1,634 @@ +{ + "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": "always gossips the $clusterTime on the sent command", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "ping" + } + }, + { + "commandStartedEvent": { + "command": { + "ping": 1, + "$clusterTime": { + "$$exists": true + } + }, + "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", + "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": [ + { + "foo": "bar" + } + ], + "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", + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "withTransaction", + "object": "session", + "arguments": { + "callback": [ + { + "name": "runCommand", + "object": "db", + "arguments": { + "session": "session", + "commandName": "insert", + "command": { + "insert": "collection", + "documents": [ + { + "foo": "transaction" + } + ], + "ordered": true + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "collection", + "documents": [ + { + "foo": "transaction" + } + ], + "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/source/run-command/tests/unified/runCommand.yml b/source/run-command/tests/unified/runCommand.yml new file mode 100644 index 0000000000..bc55d79205 --- /dev/null +++ b/source/run-command/tests/unified/runCommand.yml @@ -0,0 +1,319 @@ +description: runCommand + +schemaVersion: "1.3" + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + - database: + id: &db db + client: *client + databaseName: *db + - collection: + id: &collection collection + database: *db + collectionName: *collection + - database: + id: &dbWithRC dbWithRC + client: *client + databaseName: *dbWithRC + databaseOptions: + readConcern: { level: 'local' } + - database: + id: &dbWithWC dbWithWC + client: *client + databaseName: *dbWithWC + databaseOptions: + writeConcern: { w: 0 } + - session: + id: &session session + client: *client + # Stable API test + - client: + id: &clientWithStableApi clientWithStableApi + observeEvents: [commandStartedEvent] + serverApi: + version: "1" + strict: true + - database: + id: &dbWithStableApi 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: always gossips the $clusterTime on the sent command + runOnRequirements: + # Only replicasets and sharded clusters have a $clusterTime + - topologies: [ replicaset, sharded ] + operations: + # We have to run one command to obtain a clusterTime to gossip + - name: runCommand + object: *db + arguments: + commandName: ping + command: { ping: 1 } + expectResult: { ok: 1 } + - name: runCommand + object: *db + arguments: + commandName: ping + command: { ping: 1 } + expectResult: { ok: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: ping + # Only check the shape of the second ping which should have the $clusterTime received from the first operation + - commandStartedEvent: + command: + ping: 1 + $clusterTime: { $$exists: true } + 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: + # Exclude single topology, which is most likely a standalone server + - topologies: [ replicaset, load-balanced, sharded ] + operations: + - name: runCommand + object: *db + arguments: + commandName: ping + command: { ping: 1 } + readPreference: &readPreference { mode: 'nearest' } + expectResult: { ok: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + ping: 1 + $readPreference: *readPreference + $db: *db + commandName: ping + + - description: does not attach $readPreference to given command on standalone + runOnRequirements: + # This test assumes that the single topology contains a standalone server; + # however, it is possible for a single topology to contain a direct + # connection to another server type. + # See: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.md#topology-type-single + - 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 + # Test with a command that supports a readConcern option. + # expectResult is intentionally omitted because some drivers + # may automatically convert command responses into cursors. + 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: [ { foo: 'bar' } ] + 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, load-balanced ] + operations: + - name: withTransaction + object: *session + arguments: + callback: + - name: runCommand + object: *db + arguments: + session: *session + commandName: insert + command: + insert: *collection + documents: [ { foo: 'transaction' } ] + ordered: true + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + insert: *collection + documents: [ { foo: 'transaction' } ] + ordered: true + lsid: { $$sessionLsid: *session } + txnNumber: 1 + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *db + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session } + txnNumber: 1 + autocommit: false + # omitted fields + 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/source/run-command/tests/unified/runCursorCommand.json b/source/run-command/tests/unified/runCursorCommand.json new file mode 100644 index 0000000000..4f1ec8a01a --- /dev/null +++ b/source/run-command/tests/unified/runCursorCommand.json @@ -0,0 +1,877 @@ +{ + "description": "runCursorCommand", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + { + "session": { + "id": "session", + "client": "client" + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "db" + } + }, + { + "collection": { + "id": "collection", + "database": "db", + "collectionName": "collection" + } + } + ], + "initialData": [ + { + "collectionName": "collection", + "databaseName": "db", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "successfully executes checkMetadataConsistency cursor creating command", + "runOnRequirements": [ + { + "minServerVersion": "7.0", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "checkMetadataConsistency", + "command": { + "checkMetadataConsistency": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "checkMetadataConsistency": 1, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "checkMetadataConsistency" + } + } + ] + } + ] + }, + { + "description": "errors if the command response is not a cursor", + "operations": [ + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ] + }, + { + "description": "creates an implicit session that is reused across getMores", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "collection", + "batchSize": 2 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "batchSize": 2, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "accepts an explicit session that is reused across getMores", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "session": "session", + "command": { + "find": "collection", + "batchSize": 2 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + }, + { + "name": "assertSameLsidOnLastTwoCommands", + "object": "testRunner", + "arguments": { + "client": "client" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "batchSize": 2, + "$db": "db", + "lsid": { + "$$sessionLsid": "session" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$sessionLsid": "session" + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "returns pinned connections to the pool when the cursor is exhausted", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "find", + "batchSize": 2, + "session": "session", + "command": { + "find": "collection", + "batchSize": 2 + } + }, + "saveResultAsEntity": "cursor" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client", + "connections": 1 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectResult": { + "_id": 2, + "x": 22 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectResult": { + "_id": 3, + "x": 33 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectResult": { + "_id": 4, + "x": 44 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor", + "expectResult": { + "_id": 5, + "x": 55 + } + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client", + "connections": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "batchSize": 2, + "$db": "db", + "lsid": { + "$$sessionLsid": "session" + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$sessionLsid": "session" + } + }, + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$sessionLsid": "session" + } + }, + "commandName": "getMore" + } + } + ] + }, + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "returns pinned connections to the pool when the cursor is closed", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "operations": [ + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "find", + "command": { + "find": "collection", + "batchSize": 2 + } + }, + "saveResultAsEntity": "cursor" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client", + "connections": 1 + } + }, + { + "name": "close", + "object": "cursor" + }, + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client", + "connections": 0 + } + } + ] + }, + { + "description": "supports configuring getMore batchSize", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "batchSize": 5, + "command": { + "find": "collection", + "batchSize": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "batchSize": 1, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "batchSize": 5, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "supports configuring getMore maxTimeMS", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "maxTimeMS": 300, + "command": { + "find": "collection", + "maxTimeMS": 200, + "batchSize": 1 + } + }, + "ignoreResultAndError": true + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "ignoreExtraEvents": true, + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "maxTimeMS": 200, + "batchSize": 1, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "maxTimeMS": 300, + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "supports configuring getMore comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "comment": { + "hello": "getMore" + }, + "command": { + "find": "collection", + "batchSize": 1, + "comment": { + "hello": "find" + } + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "batchSize": 1, + "comment": { + "hello": "find" + }, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "comment": { + "hello": "getMore" + }, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "does not close the cursor when receiving an empty batch", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection" + } + }, + { + "name": "createCollection", + "object": "db", + "arguments": { + "collection": "cappedCollection", + "capped": true, + "size": 4096, + "max": 3 + }, + "saveResultAsEntity": "cappedCollection" + }, + { + "name": "insertMany", + "object": "cappedCollection", + "arguments": { + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + }, + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "cursorType": "tailable", + "commandName": "find", + "batchSize": 2, + "command": { + "find": "cappedCollection", + "tailable": true + } + }, + "saveResultAsEntity": "cursor" + }, + { + "name": "iterateOnce", + "object": "cursor" + }, + { + "name": "iterateOnce", + "object": "cursor" + }, + { + "name": "iterateOnce", + "object": "cursor" + }, + { + "name": "close", + "object": "cursor" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "cappedCollection" + }, + "commandName": "drop" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "cappedCollection" + }, + "commandName": "create" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "cappedCollection" + }, + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "cappedCollection", + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "cappedCollection", + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "command": { + "killCursors": "cappedCollection", + "cursors": { + "$$type": "array" + } + }, + "commandName": "killCursors" + } + } + ] + } + ] + } + ] +} diff --git a/source/run-command/tests/unified/runCursorCommand.yml b/source/run-command/tests/unified/runCursorCommand.yml new file mode 100644 index 0000000000..1f9bf532c3 --- /dev/null +++ b/source/run-command/tests/unified/runCursorCommand.yml @@ -0,0 +1,391 @@ +description: runCursorCommand + +schemaVersion: '1.9' + +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent, connectionReadyEvent, connectionCheckedOutEvent, connectionCheckedInEvent] + - session: + id: &session session + client: *client + - database: + id: &db db + client: *client + databaseName: *db + - collection: + id: &collection collection + database: *db + collectionName: *collection + +initialData: + - collectionName: collection + databaseName: *db + documents: &documents + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + +tests: + # This is what this API was invented to do. + - description: successfully executes checkMetadataConsistency cursor creating command + runOnRequirements: + - minServerVersion: '7.0' + topologies: [sharded] + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: checkMetadataConsistency + command: { checkMetadataConsistency: 1 } + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + checkMetadataConsistency: 1 + $db: *db + lsid: { $$exists: true } + commandName: checkMetadataConsistency + + - description: errors if the command response is not a cursor + operations: + - name: createCommandCursor + object: *db + arguments: + commandName: ping + command: { ping: 1 } + expectError: + isClientError: true + + + # Driver Sessions + - description: creates an implicit session that is reused across getMores + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + command: { find: *collection, batchSize: 2 } + expectResult: *documents + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: *collection + batchSize: 2 + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$exists: true } + commandName: getMore + + - description: accepts an explicit session that is reused across getMores + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + session: *session + command: { find: *collection, batchSize: 2 } + expectResult: *documents + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: *collection + batchSize: 2 + $db: *db + lsid: { $$sessionLsid: *session } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$sessionLsid: *session } + commandName: getMore + + # Load Balancers + - description: returns pinned connections to the pool when the cursor is exhausted + runOnRequirements: + - topologies: [ load-balanced ] + operations: + - name: createCommandCursor + object: *db + arguments: + commandName: find + batchSize: 2 + session: *session + command: { find: *collection, batchSize: 2 } + saveResultAsEntity: &cursor cursor + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client + connections: 1 + - name: iterateUntilDocumentOrError + object: *cursor + expectResult: { _id: 1, x: 11 } + - name: iterateUntilDocumentOrError + object: *cursor + expectResult: { _id: 2, x: 22 } + - name: iterateUntilDocumentOrError + object: *cursor + expectResult: { _id: 3, x: 33 } + - name: iterateUntilDocumentOrError + object: *cursor + expectResult: { _id: 4, x: 44 } + - name: iterateUntilDocumentOrError + object: *cursor + expectResult: { _id: 5, x: 55 } + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client + connections: 0 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: *collection + batchSize: 2 + $db: *db + lsid: { $$sessionLsid: *session } + commandName: find # 2 documents + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$sessionLsid: *session } + commandName: getMore # 2 documents + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$sessionLsid: *session } + commandName: getMore # 1 document + # Total documents: 5 + - client: *client + eventType: cmap + events: + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + - description: returns pinned connections to the pool when the cursor is closed + runOnRequirements: + - topologies: [ load-balanced ] + operations: + - name: createCommandCursor + object: *db + arguments: + commandName: find + command: { find: *collection, batchSize: 2 } + saveResultAsEntity: *cursor + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client + connections: 1 + - name: close + object: *cursor + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client + connections: 0 + + # Iterating the Cursor / Executing GetMores + - description: supports configuring getMore batchSize + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + batchSize: 5 + command: { find: *collection, batchSize: 1 } + expectResult: *documents + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: *collection + batchSize: 1 + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + batchSize: 5 + $db: *db + lsid: { $$exists: true } + commandName: getMore + + - description: supports configuring getMore maxTimeMS + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + maxTimeMS: 300 + command: { find: *collection, maxTimeMS: 200, batchSize: 1 } + ignoreResultAndError: true + expectEvents: + - client: *client + eventType: command + # The getMore should receive an error here because we do not have the right kind of cursor + # So drivers should run a killCursors, but neither the error nor the killCursors command is relevant to this test + ignoreExtraEvents: true + events: + - commandStartedEvent: + command: + find: *collection + maxTimeMS: 200 + batchSize: 1 + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + maxTimeMS: 300 + lsid: { $$exists: true } + commandName: getMore + + - description: supports configuring getMore comment + runOnRequirements: + - minServerVersion: '4.4' + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + comment: { hello: 'getMore' } + command: { find: *collection, batchSize: 1, comment: { hello: 'find' } } + expectResult: *documents + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: *collection + batchSize: 1 + comment: { hello: 'find' } + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + comment: { hello: 'getMore' } + $db: *db + lsid: { $$exists: true } + commandName: getMore + + # Tailable cursor + - description: does not close the cursor when receiving an empty batch + runOnRequirements: + - serverless: forbid + operations: + - name: dropCollection + object: *db + arguments: + collection: &cappedCollection cappedCollection + - name: createCollection + object: *db + arguments: + collection: *cappedCollection + capped: true + size: 4096 + max: 3 + saveResultAsEntity: *cappedCollection + - name: insertMany + object: *cappedCollection + arguments: + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - name: createCommandCursor + object: *db + arguments: + cursorType: tailable + commandName: find + batchSize: 2 + command: { find: *cappedCollection, tailable: true } + saveResultAsEntity: &cursor cursor + - name: iterateOnce + object: *cursor + - name: iterateOnce + object: *cursor + - name: iterateOnce + object: *cursor + - name: close + object: *cursor + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + drop: *cappedCollection + commandName: drop + - commandStartedEvent: + command: + create: *cappedCollection + commandName: create + - commandStartedEvent: + command: + insert: *cappedCollection + commandName: insert + - commandStartedEvent: + command: + find: *cappedCollection + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *cappedCollection + $db: *db + lsid: { $$exists: true } + commandName: getMore + - commandStartedEvent: + command: + killCursors: *cappedCollection + cursors: { $$type: array } + commandName: killCursors diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md new file mode 100644 index 0000000000..dc6ba506e6 --- /dev/null +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md @@ -0,0 +1,584 @@ +# SDAM Logging and Monitoring Specification + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Abstract + +The SDAM logging and monitoring specification defines a set of behaviors in the driver for providing runtime information +about server discovery and monitoring (SDAM) in log messages, as well as in events that users can consume +programmatically, either directly or by integrating with third-party APM libraries. + +### Definitions + +#### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +#### Terms + +`ServerAddress` + +> The term `ServerAddress` refers to the implementation in the driver's language of a server host/port pair. This may be +> an object or a string. The name of this object is NOT REQUIRED. + +`TopologyType` + +> The term `TopologyType` refers to the implementation in the driver's language of a topology type (standalone, sharded, +> etc.). This may be a string or object. The name of the object is NOT REQUIRED. + +`Server` + +> The term `Server` refers to the implementation in the driver's language of an abstraction of a mongod or mongos +> process, or a load balancer, as defined by the [SDAM specification](server-discovery-and-monitoring.md#server). + +### Specification + +### Guidance + +#### Documentation + +The documentation provided in the code below is merely for driver authors and SHOULD NOT be taken as required +documentation for the driver. + +#### Messages and Events + +All drivers MUST implement the specified event types as well as log messages. + +Implementation details are noted below when a specific implementation is required. Within each event and log message, +all properties are REQUIRED unless noted otherwise. + +#### Naming + +All drivers MUST name types, properties, and log message values as defined in the following sections. Exceptions to this +rule are noted in the appropriate section. Class and interface names may vary according to the driver and language best +practices. + +#### Publishing and Subscribing + +The driver SHOULD publish events in a manner that is standard to the driver's language publish/subscribe patterns and is +not strictly mandated in this specification. + +Similarly, as described in the [logging specification](../logging/logging.md#implementation-requirements) the driver +SHOULD emit log messages in a manner that is standard for the language. + +### Guarantees + +#### Event Order and Concurrency + +Events and log messages MUST be published in the order that their corresponding changes are processed in the driver. +Events MUST NOT be published concurrently for the same topology ID or server ID, but MAY be published concurrently for +differing topology IDs and server IDs. + +#### Heartbeats + +The driver MUST guarantee that every `ServerHeartbeatStartedEvent` has either a correlating +`ServerHeartbeatSucceededEvent` or `ServerHeartbeatFailedEvent`, and that every "server heartbeat started" log message +has either a correlating "server heartbeat succeeded" or "server heartbeat failed" log message. + +Drivers that use the streaming heartbeat protocol MUST publish a `ServerHeartbeatStartedEvent` and "server heartbeat +started" log message before attempting to read the next `hello` or legacy hello exhaust response. + +#### Error Handling + +If an exception occurs while sending the `hello` or legacy hello operation to the server, the driver MUST generate a +`ServerHeartbeatFailedEvent` and "server heartbeat failed" log message with the exception or message and re-raise the +exception. The SDAM mandated retry of the `hello` or legacy hello call should be visible to consumers. + +#### Topology IDs + +These MUST be a unique value that is specific to the Topology for which the events and log messages are emitted. The +language may decide how to generate the value and what type the value is, as long as it is unique to the Topology. The +ID MUST be created once when the Topology is created and remain the same until the Topology is destroyed. + +#### Initial Server Description + +`ServerDescription` objects MUST be initialized with a default description in an "unknown" state, guaranteeing that the +previous description in the events and log messages will never be null. + +#### Initial Topology Description + +The first `TopologyDescriptionChangedEvent` to be emitted from a monitored Topology MUST set its `previousDescription` +property to be a `TopologyDescription` object in the "unknown" state. + +### Events API + +The first `TopologyDescriptionChangedEvent` to be emitted from a monitored Topology MUST set its `previousDescription` +property to be a `TopologyDescription` object in the "unknown" state. + +## Closing Topology Description + +When a `Topology` object or equivalent is being shut-down or closed, the driver MUST change the `TopologyDescription` to +an "unknown" state. + +______________________________________________________________________ + +## Events API + +This specification defines 9 main events that MUST be published in the scenarios described. 6 of these events are the +core behaviour within the cluster lifecycle, and the remaining 3 server heartbeat events are fired from the server +monitor and follow the guidelines for publishing in the command monitoring specification. + +Events that MUST be published (with their conditions) are as follows. + +| Event Type | Condition | +| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TopologyOpeningEvent` | When a topology description is initialized - this MUST be the first SDAM event fired. | +| `ServerOpeningEvent` | Published when the server description is instantiated with its defaults, and MUST be the first operation to happen after the defaults are set. This is before the Monitor is created and the Monitor socket connection is opened. | +| `ServerDescriptionChangedEvent` | When the old server description is not equal to the new server description | +| `TopologyDescriptionChangedEvent` | When the old topology description is not equal to the new topology description. | +| `ServerClosedEvent` | Published when the server monitor's connection is closed and the server is shutdown. | +| `TopologyClosedEvent` | When a topology is shut down - this MUST be the last SDAM event fired. | +| `ServerHeartbeatStartedEvent` | Published when the server monitor sends its `hello` or legacy hello call to the server. When the monitor is creating a new connection, this event MUST be published just before the socket is created. | +| `ServerHeartbeatSucceededEvent` | Published on successful completion of the server monitor's `hello` or legacy hello call. | +| `ServerHeartbeatFailedEvent` | Published on failure of the server monitor's `hello` or legacy hello call, either with an ok: 0 result or a socket exception from the connection. | + +```typescript +/** + * Published when server description changes, but does NOT include changes to the RTT. + */ +interface ServerDescriptionChangedEvent { + + /** + * Returns the address (host/port pair) of the server. + */ + address: ServerAddress; + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; + + /** + * Returns the previous server description. + */ + previousDescription: ServerDescription; + + /** + * Returns the new server description. + */ + newDescription: ServerDescription; +} + +/** + * Published when server is initialized. + */ +interface ServerOpeningEvent { + + /** + * Returns the address (host/port pair) of the server. + */ + address: ServerAddress; + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; +} + +/** + * Published when server is closed. + */ +interface ServerClosedEvent { + + /** + * Returns the address (host/port pair) of the server. + */ + address: ServerAddress; + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; +} + +/** + * Published when topology description changes. + */ +interface TopologyDescriptionChangedEvent { + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; + + /** + * Returns the old topology description. + */ + previousDescription: TopologyDescription; + + /** + * Returns the new topology description. + */ + newDescription: TopologyDescription; +} + +/** + * Published when topology is initialized. + */ +interface TopologyOpeningEvent { + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; +} + +/** + * Published when topology is closed. + */ +interface TopologyClosedEvent { + + /** + * Returns a unique identifier for the topology. + */ + topologyId: Object; +} + +/** + * Fired when the server monitor's ``hello`` or legacy hello command is started - immediately before + * the ``hello`` or legacy hello command is serialized into raw BSON and written to the socket. + * When the monitor is creating a new monitoring connection, this event is fired just before the + * socket is opened. + */ +interface ServerHeartbeatStartedEvent { + + /** + * Returns the connection id for the command. The connection id is the unique + * identifier of the driver's Connection object that wraps the socket. For languages that + * do not have this object, this MUST a string of "hostname:port" or an object that + * that contains the hostname and port as attributes. + * + * The name of this field is flexible to match the object that is returned from the driver. + * Examples are, but not limited to, 'address', 'serverAddress', 'connectionId', + */ + connectionId: ConnectionId; + + /** + * Determines if this heartbeat event is for an awaitable ``hello`` or legacy hello. + */ + awaited: Boolean; + +} + +/** + * Fired when the server monitor's ``hello`` or legacy hello succeeds. + */ +interface ServerHeartbeatSucceededEvent { + + /** + * Returns the execution time of the event in the highest possible resolution for the platform. + * The calculated value MUST be the time to send the message and receive the reply from the server, + * including BSON serialization and deserialization. The name can imply the units in which the + * value is returned, i.e. durationMS, durationNanos. + * + * When the awaited field is false, the time measurement used MUST be the + * same measurement used for the RTT calculation. When the awaited field is + * true, the time measurement is not used for RTT calculation. + */ + duration: Int64; + + /** + * Returns the command reply. + */ + reply: Document; + + /** + * Returns the connection id for the command. For languages that do not have this, + * this MUST return the driver equivalent which MUST include the server address and port. + * The name of this field is flexible to match the object that is returned from the driver. + */ + connectionId: ConnectionId; + + /** + * Determines if this heartbeat event is for an awaitable ``hello`` or legacy hello. If + * true, then the duration field cannot be used for RTT calculation + * because the command blocks on the server. + */ + awaited: Boolean; + +} + +/** + * Fired when the server monitor's ``hello`` or legacy hello fails, either with an "ok: 0" or a socket exception. + */ +interface ServerHeartbeatFailedEvent { + + /** + * Returns the execution time of the event in the highest possible resolution for the platform. + * The calculated value MUST be the time to send the message and receive the reply from the server, + * including BSON serialization and deserialization. The name can imply the units in which the + * value is returned, i.e. durationMS, durationNanos. + */ + duration: Int64; + + /** + * Returns the failure. Based on the language, this SHOULD be a message string, + * exception object, or error document. + */ + failure: String,Exception,Document; + + /** + * Returns the connection id for the command. For languages that do not have this, + * this MUST return the driver equivalent which MUST include the server address and port. + * The name of this field is flexible to match the object that is returned from the driver. + */ + connectionId: ConnectionId; + + /** + * Determines if this heartbeat event is for an awaitable ``hello`` or legacy hello. If + * true, then the duration field cannot be used for RTT calculation + * because the command blocks on the server. + */ + awaited: Boolean; +} +``` + +The `TopologyDescription` object MUST expose the new methods defined in the API below, in order for subscribers to take +action on certain conditions based on the driver options. + +`TopologyDescription` objects MAY have additional methods and properties. + +```typescript +/** + * Describes the current topology. + */ +interface TopologyDescription { + + /** + * Determines if the topology has a readable server available. See the table in the + * following section for behaviour rules. + */ + hasReadableServer(readPreference: Optional): Boolean + + /** + * Determines if the topology has a writable server available. See the table in the + * following section for behaviour rules. + */ + hasWritableServer(): Boolean +} +``` + +### Determining If A Topology Has Readable/Writable Servers + +The following table describes the rules for determining if a topology type has readable or writable servers. If no read +preference is passed to `hasReadableServer`, the driver MUST default the value to the default read preference, +`primary`, or treat the call as if `primary` was provided. + +| Topology Type | `hasReadableServer` | `hasWritableServer` | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | +| Unknown | `false` | `false` | +| Single | `true` if the server is available | `true` if the server is available | +| ReplicaSetNoPrimary | Called with `primary`: `false`
Called with any other option: uses the read preference to determine if any server in the cluster is suitable for reading.
Called with no option: `false` | `false` | +| ReplicaSetWithPrimary | Called with any valid option: uses the read preference to determine if any server in the cluster is suitable for reading.
Called with no option: `true` | `true` | +| Sharded | `true` if 1+ servers are available | `true` if 1+ servers are available | +| LoadBalanced | `true` | `true` | + +### Log Messages + +Please refer to the [logging specification](../logging/logging.md) for details on logging implementations in general, +including log levels, log components, and structured versus unstructured logging. + +Drivers MUST support logging of SDAM information via the following types of log messages. These messages MUST be logged +at `Debug` level and use the `topology` log component. + +A number of the log messages are intended to match the information contained in the events above. However, note that a +log message regarding a server description change (which would correspond to `ServerDescriptionChangedEvent`) has been +intentionally omitted since the information it would contain is redundant with `TopologyDescriptionChangedEvent` and the +equivalent log message. + +Drivers MAY implement SDAM logging support via an event subscriber if it is convenient to do so. + +The types used in the structured message definitions below are demonstrative, and drivers MAY use similar types instead +so long as the information is present (e.g. a double instead of an integer, or a string instead of an integer if the +structured logging framework does not support numeric types.) + +#### Common Fields + +The following key-value pairs are common to all or several log messages and MUST be included in the "applicable +messages": + +| Key | Applicable Messages | Suggested Type | Value | +| ------------------ | ---------------------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| topologyId | All messages | Flexible | The driver's unique ID for this topology as discussed in [Topology IDs](#topology-ids). The type is flexible depending on the driver's choice of type for topology ID. | +| serverHost | Log messages specific to a particular server, including heartbeat-related messages | String | The hostname, IP address, or Unix domain socket path for the endpoint the pool is for. | +| serverPort | Log messages specific to a particular server, including heartbeat-related messages | Int | (Only present for server-specific log messages) The port for the endpoint the pool is for. Optional; not present for Unix domain sockets. When the user does not specify a port and the default (27017) is used, the driver SHOULD include it here. | +| driverConnectionId | Heartbeat-related log messages | Int | The driver-generated ID for the monitoring connection as defined in the [connection monitoring and pooling specification](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md). Unlike `connectionId` in the above events, this field MUST NOT contain the host/port; that information MUST be in the above fields, `serverHost` and `serverPort`. This field is optional for drivers that do not implement CMAP if they do have an equivalent concept of a connection ID. | +| serverConnectionId | Heartbeat-related log messages | Int | The server's ID for the monitoring connection, if known. This value will be unknown and can be omitted in certain cases, e.g. the first "heartbeat started" message for a monitoring connection. Only present on server versions 4.2+. | + +#### "Starting Topology Monitoring" Log Message + +This message MUST be published under the same circumstances as a `TopologyOpeningEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pair: + +| Key | Suggested Type | Value | +| ------- | -------------- | ------------------------------ | +| message | String | "Starting topology monitoring" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Starting monitoring for topology with ID {{topologyId}} + +#### "Stopped Topology Monitoring" Log Message + +This message MUST be published under the same circumstances as a `TopologyClosedEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pair: + +| Key | Suggested Type | Value | +| ------- | -------------- | ----------------------------- | +| message | String | "Stopped topology monitoring" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Stopped monitoring for topology with ID {{topologyId}} + +#### "Starting Server Monitoring" Log Message + +This message MUST be published under the same circumstances as a `ServerOpeningEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pair: + +| Key | Suggested Type | Value | +| ------- | -------------- | ---------------------------- | +| message | String | "Starting server monitoring" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Starting monitoring for server {{serverHost}}:{{serverPort}} in topology with ID {{topologyId}} + +#### "Stopped Server Monitoring" Log Message + +This message MUST be published under the same circumstances as a `ServerClosedEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pair: + +| Key | Suggested Type | Value | +| ------- | -------------- | --------------------------- | +| message | String | "Stopped server monitoring" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Stopped monitoring for server {{serverHost}}:{{serverPort}} in topology with ID {{topologyId}} + +#### "Topology Description Changed" Log Message + +This message MUST be published under the same circumstances as a `TopologyDescriptionChangedEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Topology description changed" | +| previousDescription | String | A string representation of the previous description of the topology. The format is flexible and could be e.g. the `toString()` implementation for a driver's topology description type, or an extended JSON representation of the topology object. | +| newDescription | String | A string representation of the new description of the server. The format is flexible and could be e.g. the `toString()` implementation for a driver's topology description type, or an extended JSON representation of the topology object. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Description changed for topology with ID {{topologyId}}. Previous description: {{previousDescription}}. New +> description: {{newDescription}} + +#### "Server Heartbeat Started" Log Message + +This message MUST be published under the same circumstances as a `ServerHeartbeatStartedEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | --------------------------------------------------------------------- | +| message | String | "Server heartbeat started" | +| awaited | Boolean | Whether this log message is for an awaitable hello or legacy "hello". | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Heartbeat started for {{serverHost}}:{{serverPort}} on connection with driver-generated ID {{driverConnectionId}} and +> server-generated ID {{serverConnectionId}} in topology with ID {{topologyId}}. Awaited: {{awaited}} + +#### "Server Heartbeat Succeeded" Log Message + +This message MUST be published under the same circumstances as a `ServerHeartbeatSucceededEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Server heartbeat succeeded" | +| awaited | Boolean | Whether this log message is for an awaitable hello or legacy "hello". | +| durationMS | Int32/Int64/Double | The execution time for the heartbeat in milliseconds. See `ServerHeartbeatSucceededEvent` in [Events API](#events-api) for details on calculating this value. | +| reply | String | Relaxed extended JSON representation of the reply to the heartbeat command. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Heartbeat succeeded in {{durationMS}} ms for {{serverHost}}:{{serverPort}} on connection with driver-generated ID +> {{driverConnectionId}} and server-generated ID {{serverConnectionId}} in topology with ID {{topologyId}}. Awaited: +> {{awaited}}. Reply: {{reply}} + +#### "Server Heartbeat Failed" Log Message + +This message MUST be published under the same circumstances as a `ServerHeartbeatFailedEvent` as detailed in +[Events API](#events-api). + +In addition to the relevant common fields, these messages MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Server heartbeat failed" | +| awaited | Boolean | Whether this log message is for an awaitable hello or legacy "hello". | +| durationMS | Int32/Int64/Double | The execution time for the heartbeat in milliseconds. See `ServerHeartbeatFailedEvent` in [Events API](#events-api) for details on calculating this value. | +| failure | Flexible | The error. The type and format of this value is flexible; see the [logging specification](../logging/logging.md#representing-errors-in-log-messages) for details on representing errors in log messages. If the command is considered sensitive, the error MUST be redacted and replaced with a language-appropriate alternative for a redacted error, e.g. an empty string, empty document, or null. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Heartbeat failed in {{durationMS}} ms for {{serverHost}}:{{serverPort}} on connection with driver-generated ID +> {{driverConnectionId}} and server-generated ID {{serverConnectionId}} in topology with ID {{topologyId}}. Awaited: +> {{awaited}}. Failure: {{failure}} + +### Tests + +See the [README](tests/monitoring/README.md). + +## Changelog + +- 2025-01-22: Clarify durationMS in logs may be Int32/Int64/Double. + +- 2024-05-02: Migrated from reStructuredText to Markdown. + +- 2024-03-29: Updated to clarify expected initial value of TopologyDescriptionChangedEvent's previousDescription field + +- 2024-01-04: Updated to clarify when ServerHeartbeatStartedEvent should be emitted + +- 2023-03-31: Renamed to include "logging" in the title. Reorganized contents and made consistent with CLAM spec, and + added requirements for SDAM log messages. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2021-05-06: Updated to use modern terminology. + +- 2020-04-20: Add rules for streaming heartbeat protocol and add "awaited" field to heartbeat events. + +- 2018-12-12: Clarified table of rules for readable/writable servers + +- 2016-08-31: Added table of rules for determining if topology has readable/writable servers. + +- 2016-10-11: TopologyDescription objects MAY have additional methods and properties. + +______________________________________________________________________ diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.rst b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.rst new file mode 100644 index 0000000000..e2e01525df --- /dev/null +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `server-discovery-and-monitoring-logging-and-monitoring.md `_. diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-monitoring.rst b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-monitoring.rst deleted file mode 100644 index 6ef538da44..0000000000 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-monitoring.rst +++ /dev/null @@ -1,410 +0,0 @@ -.. role:: javascript(code) - :language: javascript - -============================= -SDAM Monitoring Specification -============================= - -:Spec: 222 -:Title: SDAM Monitoring Specification -:Spec Version: 1.1 -:Author: Durran Jordan -:Spec Lead: Durran Jordan -:Advisory Group: Jeff Yemin, Craig Wilson, Jesse Davis -:Status: Approved -:Type: Standards -:Minimum Server Version: 2.4 -:Last Modified: 11-Oct-2016 - -.. contents:: - --------- - -Abstract -======== - -The SDAM monitoring specification defines a set of behaviour in the drivers for providing runtime information about server discovery and monitoring events (SDAM) to any 3rd party library as well internal driver use, such as logging. - ------------ -Definitions ------------ - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in `RFC 2119 `_. - -Terms ------ - -``ServerAddress`` - - The term ``ServerAddress`` refers to the implementation in the driver's language of a server host/port pair. This may be an object or a string. The name of this object is NOT REQUIRED. - -``TopologyType`` - - The term ``TopologyType`` refers to the implementation in the driver's language of a topology type (standalone, sharded, etc.). This may be a string or object. The name of the object is NOT REQUIRED. - -``Server`` - - The term ``Server`` refers to the implementation in the driver’s language of either an abstraction of a connection to a single mongod or mongos process, as defined by the `SDAM specification `_. - -------------- -Specification -------------- - -Events ------- -This specification defines 9 main events that MUST be published in the scenarios described. 6 of these events are the core behaviour within the cluster lifecycle, and the remaining 3 server heartbeat events are fired from the server monitor and follow the guidelines for publishing in the command monitoring specification. - -Events that MUST be published (with their conditions) are as follows. - -.. list-table:: - :header-rows: 1 - :widths: 50 50 - - * - Event Type - - Condition - - - * - ``TopologyOpeningEvent`` - - When a topology description is initialized - this MUST be the first SDAM event fired. - * - ``ServerOpeningEvent`` - - Published when the server description is instantiated with its defaults, and MUST be the first operation to happen after the defaults are set. This is before the Monitor is created and the Monitor socket connection is opened. - * - ``ServerDescriptionChangedEvent`` - - When the old server description is not equal to the new server description - * - ``TopologyDescriptionChangedEvent`` - - When the old topology description is not equal to the new topology description. - * - ``ServerClosedEvent`` - - Published when the server monitor’s connection is closed and the server is shutdown. - * - ``TopologyClosedEvent`` - - When a topology is shut down - this MUST be the last SDAM event fired. - * - ``ServerHeartbeatStartedEvent`` - - Published when the server monitor sends its ismaster call to the server. - * - ``ServerHeartbeatSucceededEvent`` - - Published on successful completion of the server monitor’s ismaster call. - * - ``ServerHeartbeatFailedEvent`` - - Published on failure of the server monitor’s ismaster call, either with an ok: 0 result or a socket exception from the connection. - - -Operations ----------- -All drivers MUST implement the API. Implementation details are noted in the comments when a specific implementation is required. Within each API, all methods are REQUIRED unless noted otherwise in the comments. - -Naming ------- -All drivers MUST name operations, parameters and topic names as defined in the following sections. Exceptions to this rule are noted in the appropriate section. Class and interface names may vary according to the driver and language best practices. - -Documentation -------------- -The documentation provided in code below is merely for driver authors and SHOULD NOT be taken as required documentation for the driver. - --------- -Guidance --------- - -Publishing and Subscribing --------------------------- - -The driver SHOULD publish events in a manner that is standard to the driver's language publish/subscribe patterns and is not strictly mandated in this specification. - ----------- -Guarantees ----------- - -Event Order and Concurrency ---------------------------- - -Events MUST be published in the order they were applied in the driver. -Events MUST NOT be published concurrently for the same topology id or server id, but MAY be published concurrently for differing topology ids and server ids. - -Heartbeats ----------- - -The driver MUST guarantee that every ServerHeartbeatStartedEvent has either a correlating ServerHeartbeatSucceededEvent or ServerHeartbeatFailedEvent. - -Error Handling --------------- - -If an exception occurs while sending the ismaster operation to the server, the driver MUST generate a ServerHeartbeatFailedEvent with the exception or message and re-raise the exception. The SDAM mandated retry of the ismaster call should be visible to consumers. - -Topology Ids ------------- - -These MUST be a unique value that is specific to the Topology in which the events are fired. The language may decide how to generate the value and what type the value is, as long as it is unique to the Topology. The id MUST be created once when the Topology is created and remain the same until the Topology is destroyed. - -Topology Description --------------------- - -The TopologyDescription object MUST expose the new methods defined in the API below, in order for subscribers to take action on certain conditions based on the driver options. - -TopologyDescription objects MAY have additional methods and properties. - -Initial Server Description --------------------------- - -ServerDescriptions MUST be initialized with a default description in an “unknown” state, guaranteeing that the previous description in the events will never be null. - ---- -API ---- - -.. code:: typescript - - /** - * Published when server description changes, but does NOT include changes to the RTT. - */ - interface ServerDescriptionChangedEvent { - - /** - * Returns the address (host/port pair) of the server. - */ - address: ServerAddress; - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - - /** - * Returns the previous server description. - */ - previousDescription: ServerDescription; - - /** - * Returns the new server description. - */ - newDescription: ServerDescription; - } - - /** - * Published when server is initialized. - */ - interface ServerOpeningEvent { - - /** - * Returns the address (host/port pair) of the server. - */ - address: ServerAddress; - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - } - - /** - * Published when server is closed. - */ - interface ServerClosedEvent { - - /** - * Returns the address (host/port pair) of the server. - */ - address: ServerAddress; - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - } - - /** - * Published when topology description changes. - */ - interface TopologyDescriptionChangedEvent { - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - - /** - * Returns the old topology description. - */ - previousDescription: TopologyDescription; - - /** - * Returns the new topology description. - */ - newDescription: TopologyDescription; - } - - /** - * Published when topology is initialized. - */ - interface TopologyOpeningEvent { - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - } - - /** - * Published when topology is closed. - */ - interface TopologyClosedEvent { - - /** - * Returns a unique identifier for the topology. - */ - topologyId: Object; - } - - /** - * Fired when the server monitor’s ismaster command is started - immediately before - * the ismaster command is serialized into raw BSON and written to the socket. - */ - interface ServerHeartbeatStartedEvent { - - /** - * Returns the connection id for the command. The connection id is the unique - * identifier of the driver’s Connection object that wraps the socket. For languages that - * do not have this object, this MUST a string of “hostname:port” or an object that - * that contains the hostname and port as attributes. - * - * The name of this field is flexible to match the object that is returned from the driver. - * Examples are, but not limited to, ‘address’, ‘serverAddress’, ‘connectionId’, - */ - connectionId: ConnectionId; - - } - - /** - * Fired when the server monitor’s ismaster succeeds. - */ - interface ServerHeartbeatSucceededEvent { - - /** - * Returns the execution time of the event in the highest possible resolution for the platform. - * The calculated value MUST be the time to send the message and receive the reply from the server, - * including BSON serialization and deserialization. The name can imply the units in which the - * value is returned, i.e. durationMS, durationNanos. The time measurement used - * MUST be the same measurement used for the RTT calculation. - */ - duration: Int64; - - /** - * Returns the command reply. - */ - reply: Document; - - /** - * Returns the connection id for the command. For languages that do not have this, - * this MUST return the driver equivalent which MUST include the server address and port. - * The name of this field is flexible to match the object that is returned from the driver. - */ - connectionId: ConnectionId; - - } - - /** - * Fired when the server monitor’s ismaster fails, either with an “ok: 0” or a socket exception. - */ - interface ServerHeartbeatFailedEvent { - - /** - * Returns the execution time of the event in the highest possible resolution for the platform. - * The calculated value MUST be the time to send the message and receive the reply from the server, - * including BSON serialization and deserialization. The name can imply the units in which the - * value is returned, i.e. durationMS, durationNanos. - */ - duration: Int64; - - /** - * Returns the failure. Based on the language, this SHOULD be a message string, - * exception object, or error document. - */ - failure: String,Exception,Document; - - /** - * Returns the connection id for the command. For languages that do not have this, - * this MUST return the driver equivalent which MUST include the server address and port. - * The name of this field is flexible to match the object that is returned from the driver. - */ - connectionId: ConnectionId; - } - - /** - * Describes the current topology. - */ - interface TopologyDescription { - - /** - * Determines if the topology has a readable server available. See the table in the - * following section for behaviour rules. - */ - hasReadableServer(readPreference: Optional): Boolean - - /** - * Determines if the topology has a writable server available. See the table in the - * following section for behaviour rules. - */ - hasWritableServer(): Boolean - } - -------------------------------------------------------- -Determining If A Topology Has Readable/Writable Servers -------------------------------------------------------- - -The following table describes the behaviour of determining if a topology type has readable or -writable servers. If no read preference is passed to ``hasReadableServer``, the driver MUST default -the value to the default read preference, ``primary``, or treat the call as if ``primary`` was provided. - -+-----------------------+----------------------------------------+----------------------------------------+ -| Topology Type | ``hasReadableServer`` | ``hasWritableServer`` | -+=======================+========================================+========================================+ -| Unknown | ``false`` | ``false`` | -+-----------------------+----------------------------------------+----------------------------------------+ -| Single | ``true`` | ``true`` | -+-----------------------+----------------------------------------+----------------------------------------+ -| ReplicaSetNoPrimary | | Called with ``primary``: ``false`` | ``false`` | -| | | Called with any other option: uses | | -| | the read preference to determine if | | -| | any server in the cluster is | | -| | suitable for reading. | | -| | | Called with no option: ``false`` | | -+-----------------------+----------------------------------------+----------------------------------------+ -| ReplicaSetWithPrimary | | Called with any valid option: uses | ``true`` | -| | the read preference to determine if | | -| | any server in the cluster is | | -| | suitable for reading. | | -| | | Called with no option: ``true`` | | -+-----------------------+----------------------------------------+----------------------------------------+ -| Sharded | ``true`` | ``true`` | -+-----------------------+----------------------------------------+----------------------------------------+ - --------- -Examples --------- - -A Ruby subscriber to topology description changed events that logs the events. - -Ruby: - -.. code:: ruby - - class TopologyDescriptionChangedSubscriber - - def completed(event) - new_description = event.new_description - if (!new_description.has_writable_server?) - LOGGER.warn('New topology description contains no writable server.') - end - end - end - ------ -Tests ------ - -See the `README `_. - - -Changelog -========= - -31 AUG 2016: Added table of rules for determining if topology has readable/writable servers. -11 OCT 2016: TopologyDescription objects MAY have additional methods and properties. - diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.md new file mode 100644 index 0000000000..be72be9519 --- /dev/null +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.md @@ -0,0 +1,143 @@ +# Server Discovery And Monitoring -- Summary + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Abstract + +This spec defines how a MongoDB client discovers and monitors one or more servers. It covers monitoring a single server, +a set of mongoses, or a replica set. How does the client determine what type of servers they are? How does it keep this +information up to date? How does the client find an entire replica set from a seed list, and how does it respond to a +stepdown, election, reconfiguration, or network error? + +All drivers must answer these questions the same. Or, where platforms' limitations require differences among drivers, +there must be as few answers as possible and each must be clearly explained in this spec. Even in cases where several +answers seem equally good, drivers must agree on one way to do it. + +The server discovery and monitoring method is specified in five sections. First, a client is constructed. Second, it +begins monitoring the topology by calling `hello` or legacy hello on all servers. (Multi-threaded and asynchronous +monitoring is described first, then single-threaded monitoring.) Third, as `hello` or legacy hello responses are +received the client parses them, and fourth, it updates its view of the topology. Finally, this spec describes how +drivers update their topology view in response to errors. + +This spec does not describe how a client selects a server for an operation; that is the domain of the specs for Mongos +High Availability and for Read Preferences. There is no discussion of driver architecture and data structures, nor is +there any specification of a user-facing API. This spec is only concerned with the algorithm for monitoring the +topology. + +The Java driver 2.12.1 is the reference implementation for multi-threaded drivers. Mongos 2.6's replica set monitor is +the reference implementation for single-threaded drivers, with a few differences. + +## General Requirements + +A client MUST be able to connect to a single server of any type. This includes querying hidden replica set members, and +connecting to uninitialized members in order to run "replSetInitiate". + +A client MUST be able to discover an entire replica set from a seed list containing one or more replica set members. It +MUST be able to continue monitoring the replica set even when some members go down, or when reconfigs add and remove +members. A client MUST be able to connect to a replica set while there is no primary, or while the primary is down. + +A client MUST be able to connect to a set of mongoses and monitor their availability and round trip time. This spec +defines how mongoses are discovered and monitored, but does not define which mongos is selected for a given operation. + +Multi-threaded or asynchronous clients MUST unblock waiting operations as soon as a suitable server is found, rather +than waiting for all servers to be checked. For example, if the client is discovering a replica set and the application +attempts a write operation, the write operation MUST proceed as soon as the primary is found, rather than blocking until +the client has checked all members. + +## Client Construction + +This spec does not intend to require any drivers to make breaking changes regarding what configuration options are +available, how options are named, or what combinations of options are allowed. + +A multi-threaded or asynchronous client's constructor MUST NOT do any I/O. The constructor MAY start the monitors as +background tasks, or they MAY be started by some "initialize" method (by any name), or on the first use of the client +for an operation. This means that the constructor does not throw an exception if the deployment is unavailable: Instead, +all subsequent operations on the client fail as long as the error persists. + +The justification is that if clients can be constructed when the deployment is in some states but not in other states, +it leads to an unfortunate scenario: When the deployment is passing through a strange state, long-running applications +may keep working, but any applications restarted during this period fail. + +Additionally, since asynchronous clients cannot do I/O in a constructor, it is consistent to prohibit I/O in other +clients' constructors as well. + +Single-threaded clients also MUST NOT do I/O in the constructor. They scan the servers on demand, when the first +operation is attempted. Thus they behave consistently with multi-threaded and asynchronous clients. + +## Monitoring + +The client monitors servers by checking them periodically, or after an error indicates that the client's view of the +topology is wrong, or when no suitable server is found for a write or a read. + +Drivers differ from Mongos 2.6 in two respects. First, if a client frequently rechecks a server, it MUST wait at least +10 ms since the previous check to avoid excessive effort. + +Second, Mongos 2.6 does not monitor arbiters, but drivers MUST do so. It costs little, and in rare cases an arbiter may +be the client's last hope to find the new replica set configuration. + +### Multi-threaded or asynchronous monitoring + +All servers' monitors run independently, in parallel: If some monitors block calling `hello` or legacy hello over slow +connections, other monitors MUST proceed unimpeded. The natural implementation is a thread per server, but the decision +is left to the implementer. + +Multi-threaded and asynchronous drivers MUST call `hello` or legacy hello on servers every 10 seconds by default. (10 +seconds is Mongos's frequency.) This frequency MAY be configurable. + +### Single-threaded monitoring + +Single-threaded clients MUST scan all servers synchronously, inline with regular application operations. For +single-threaded drivers the default frequency MUST be 60 seconds and MUST be configurable. + +If the topology is a replica set, a single-threaded client attempts to contact the primary as soon as possible to get an +authoritative list of members. Otherwise, the client attempts to check all members it knows of, in order from the +least-recently to the most-recently checked. The scanning order is described completely in the spec. + +## Parsing `hello` or legacy hello + +The full algorithm for determining server type from a `hello` or legacy hello response is specified and test cases are +provided. + +Drivers MUST record the server's round trip time after each successful call to `hello` or legacy hello. How round trip +times are averaged is not in this spec's scope. + +## Updating the Topology View + +After each attempt to call `hello` or legacy hello on a server, the client updates its topology view. Initial topology +discovery and long-running monitoring are both specified by the same detailed algorithm. + +When monitoring a replica set, the client strives to use only the servers that the primary says are members. While there +is no known primary, the client MUST add servers from non-primaries' host lists, but it MUST NOT remove servers. +Eventually, when a primary is discovered, any hosts not in the primary's host list are removed from the client's view of +the topology. + +The client MUST NOT use replica set members' "setVersion" to detect reconfigs, since race conditions with setVersion +make it inferior to simply trusting the primary. + +## Error handling + +When an application operation fails because of any network error besides a socket timeout, the client MUST mark the +server "down". The server will eventually be re-checked by periodic monitoring. The specific operation that discovered +the error MUST abort and raise an exception if it was a write. It MAY be retried if it was a read. (The Read Preferences +spec includes retry rules for reads.) + +If a monitor's `hello` or legacy hello call fails on a server, the behavior is different from a failed application +operation. The `hello` or legacy hello call is retried once, immediately, before the server is marked "down". + +In either case the client MUST clear its connection pool for the server: if one socket is bad, it is likely that all +are. + +An algorithm is specified for inspecting error codes (MongoDB 3.6+) and falling back to parsing error messages when +error codes are unavailable (MongoDB 3.4 and earlier). When the client sees such an error it knows its topology view is +out of date. It MUST mark the server type "unknown." Multi-threaded and asynchronous clients MUST re-check the server +soon, and single-threaded clients MUST request a scan before the next operation. The client MUST clear its connection +pool for the server if the server is 4.0 or earlier, and MUST NOT clear its connection pool for the server if the server +is 4.2 or later. + +## Changelog + +- 2024-05-02: Migrated from reStructuredText to Markdown. +- 2022-10-05: Revise spec front matter and add changelog. diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst index bea2409ea8..de235231e0 100644 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-summary.rst @@ -1,224 +1,4 @@ -========================================== -Server Discovery And Monitoring -- Summary -========================================== -:Spec: 101 -:Title: Server Discovery And Monitoring -:Author: A\. Jesse Jiryu Davis -:Advisors: David Golden, Craig Wilson -:Status: Draft -:Type: Standards -:Last Modified: September 8, 2014 - -.. contents:: - --------- - -Abstract --------- - -This spec defines how a MongoDB client discovers and monitors one or more servers. -It covers monitoring a single server, a set of mongoses, or a replica set. -How does the client determine what type of servers they are? -How does it keep this information up to date? -How does the client find an entire replica set from a seed list, -and how does it respond to a stepdown, election, reconfiguration, or network error? - -All drivers must answer these questions the same. -Or, where platforms' limitations require differences among drivers, -there must be as few answers as possible and each must be clearly explained in this spec. -Even in cases where several answers seem equally good, drivers must agree on one way to do it. - -The server discovery and monitoring method is specified in five sections. -First, a client is constructed. -Second, it begins monitoring the topology by calling ismaster on all servers. -(Multi-threaded and asynchronous monitoring is described first, -then single-threaded monitoring.) -Third, as ismaster calls are received -the client parses them, -and fourth, it updates its view of the topology. -Finally, this spec describes how drivers update their topology view -in response to errors. - -This spec does not describe how a client selects a server for an operation; -that is the domain of the specs for Mongos High Availability -and for Read Preferences. -There is no discussion of driver architecture and data structures, -nor is there any specification of a user-facing API. -This spec is only concerned with the algorithm for monitoring the topology. - -The Java driver 2.12.1 is the reference implementation -for multi-threaded drivers. -Mongos 2.6's replica set monitor -is the reference implementation for single-threaded drivers, -with a few differences. - -General Requirements --------------------- - -A client MUST be able to connect to a single server of any type. -This includes querying hidden replica set members, -and connecting to uninitialized members in order to run -"replSetInitiate". - -A client MUST be able to discover an entire replica set from -a seed list containing one or more replica set members. -It MUST be able to continue monitoring the replica set -even when some members go down, -or when reconfigs add and remove members. -A client MUST be able to connect to a replica set -while there is no primary, or while the primary is down. - -A client MUST be able to connect to a set of mongoses -and monitor their availability and round trip time. -This spec defines how mongoses are discovered and monitored, -but does not define which mongos is selected for a given operation. - -A client MUST be able to directly connect to a mongod begun with "--slave". -No additional master-slave features are described in this spec. - -Multi-threaded or asynchronous clients -MUST unblock waiting operations -as soon as a suitable server is found, -rather than waiting for all servers to be checked. -For example, if the client is discovering a replica set -and the application attempts a write operation, -the write operation MUST proceed as soon as the primary is found, -rather than blocking until the client has checked all members. - -Client Construction -------------------- - -This spec does not intend -to require any drivers to make breaking changes regarding -what configuration options are available, -how options are named, -or what combinations of options are allowed. - -A multi-threaded or asynchronous client's constructor MUST NOT do any I/O. -The constructor MAY start the monitors as background tasks, -or they MAY be started by some "initialize" method (by any name), -or on the first use of the client for an operation. -This means that the constructor does not throw an exception -if the deployment is unavailable: -Instead, all subsequent operations on the client fail -as long as the error persists. - -The justification is that -if clients can be constructed when the deployment is in some states -but not in other states, -it leads to an unfortunate scenario: -When the deployment is passing through a strange state, -long-running applications may keep working, -but any applications restarted during this period fail. - -Additionally, since asynchronous clients cannot do I/O in a constructor, -it is consistent to prohibit I/O in other clients' constructors as well. - -Single-threaded clients also MUST NOT do I/O in the constructor. -They scan the servers on demand, -when the first operation is attempted. -Thus they behave consistently with multi-threaded and asynchronous clients. - -Monitoring ----------- - -The client monitors servers by checking them periodically, -or after an error indicates that the client's view of the topology is wrong, -or when no suitable server is found for a write or a read. - -Drivers differ from Mongos 2.6 in two respects. First, -if a client frequently rechecks a server, -it MUST wait at least 10 ms -since the previous check to avoid excessive effort. - -Second, Mongos 2.6 does not monitor arbiters, but drivers MUST do so. -It costs little, and in rare cases an arbiter may be the client's last hope -to find the new replica set configuration. - -Multi-threaded or asynchronous monitoring -''''''''''''''''''''''''''''''''''''''''' - -All servers' monitors run independently, in parallel: -If some monitors block calling ismaster over slow connections, -other monitors MUST proceed unimpeded. -The natural implementation is a thread per server, -but the decision is left to the implementer. - -Multi-threaded and asynchronous drivers -MUST call ismaster on servers every 10 seconds by default. -(10 seconds is Mongos's frequency.) -This frequency MAY be configurable. - -Single-threaded monitoring -'''''''''''''''''''''''''' - -Single-threaded clients MUST scan all servers synchronously, -inline with regular application operations. -For single-threaded drivers the default frequency MUST be 60 seconds -and MUST be configurable. - -If the topology is a replica set, -a single-threaded client attempts to contact the primary as soon as possible -to get an authoritative list of members. -Otherwise, the client attempts to check all members it knows of, -in order from the least-recently to the most-recently checked. -The scanning order is described completely in the spec. - -Parsing ismaster ----------------- - -The full algorithm for determining server type from an ismaster response -is specified and test cases are provided. - -Drivers MUST record the server's round trip time -after each successful call to ismaster. -How round trip times are averaged is not in this spec's scope. - -Updating the Topology View --------------------------- - -After each attempt to call ismaster on a server, -the client updates its topology view. -Initial topology discovery and long-running monitoring -are both specified by the same detailed algorithm. - -When monitoring a replica set, -the client strives to use only the servers that the primary says are members. -While there is no known primary, -the client MUST add servers from non-primaries' host lists, -but it MUST NOT remove servers. -Eventually, when a primary is discovered, any hosts not in the primary's host -list are removed from the client's view of the topology. - -The client MUST NOT use replica set members' "setVersion" -to detect reconfigs, since race conditions with setVersion -make it inferior to simply trusting the primary. - -Error handling --------------- - -When an application operation fails because of -any network error besides a socket timeout, -the client MUST mark the server "down". -The server will eventually be re-checked by periodic monitoring. -The specific operation that discovered the error -MUST abort and raise an exception if it was a write. -It MAY be retried if it was a read. -(The Read Preferences spec includes retry rules for reads.) - -If a monitor's ismaster call fails on a server, -the behavior is different from a failed application operation. -The ismaster call is retried once, immediately, -before the server is marked "down". - -In either case the client SHOULD clear its connection pool for the server: -if one socket is bad, it is likely that all are. - -An algorithm is specified for parsing -"not master" and "node is recovering" errors. -When the client sees such an error it knows its topology view is out of date. -It MUST mark the server type "unknown." -Multi-threaded and asynchronous clients MUST re-check the server soon, -and single-threaded clients MUST request a scan before the next operation. -The client SHOULD clear its connection pool for the server. +.. note:: + This specification has been converted to Markdown and renamed to + `server-discovery-and-monitoring-summary.md `_. diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.md new file mode 100644 index 0000000000..bfcd930a3c --- /dev/null +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.md @@ -0,0 +1,221 @@ +# Server Discovery And Monitoring -- Test Plan + +- Status: Accepted + +- Minimum Server Version: 2.4 See also the YAML test files and their accompanying README in the "tests" directory. + +______________________________________________________________________ + +## All servers unavailable + +A MongoClient can be constructed without an exception, even with all seeds unavailable. + +## Network error writing to primary + +Scenario: With TopologyType ReplicaSetWithPrimary, a write to the primary fails with a network error other than timeout. + +Outcome: The former primary's ServerType MUST become Unknown. The TopologyType MUST change to ReplicaSetNoPrimary. The +client MUST NOT immediately re-check the former primary. + +## "Not writable primary" error when reading without SecondaryOk bit + +Scenario: With TopologyType ReplicaSetWithPrimary, we read from a server we thought was RSPrimary. Thus the SecondaryOk +bit is not set. + +The server response should indicate an error due to the server not being a primary. + +Outcome: The former primary's ServerType MUST become Unknown. The TopologyType MUST change to ReplicaSetNoPrimary. The +client MUST NOT immediately re-check the former primary. + +## "Node is recovering" error reading with SecondaryOk bit + +Scenario: With TopologyType ReplicaSetWithPrimary, we read from a server we thought was RSSecondary. Thus the +SecondaryOk bit *is* set. + +The server response should indicate an error due to the server being in recovering state. + +Outcome: The former secondary's ServerType MUST become Unknown. The TopologyType MUST remain ReplicaSetWithPrimary. A +multi-threaded client MUST immediately re-check the former secondary, a single-threaded client MUST NOT. + +## "Node is recovering" error from a write concern error + +Scenario: With TopologyType ReplicaSetWithPrimary, a write to the primary responds with the following document: + +> { ok: 1, writeConcernError: {code: 91, errmsg: "Replication is being shut down"} } + +Outcome: The former primary's ServerType MUST become Unknown. The TopologyType MUST change to ReplicaSetNoPrimary. A +multi-threaded client MUST immediately re-check the former secondary, a single-threaded client MUST NOT. + +## Parsing "not writable primary" and "node is recovering" errors + +For all these example responses, the client MUST mark the server "Unknown" and store the error message in the +ServerDescription's error field. + +Clients MUST NOT depend on any particular field order in these responses. + +### Write command + +Response to an "insert" command on an arbiter, secondary, recovering member, or ghost: + +> {ok: 0, errmsg: "not writable primary"} + +### Query with SecondaryOk bit + +Response from an arbiter, recovering member, or ghost when SecondaryOk is true: + +> {$err: "not primary or secondary; cannot currently read from this replSet member"} + +The QueryFailure bit is set in responseFlags. + +### Query without SecondaryOk bit + +Response from an arbiter, recovering member, ghost, or secondary when SecondaryOk is false: + +> {$err: "not writable primary and SecondaryOk=false"} + +The QueryFailure bit is set in responseFlags. + +### Count with SecondaryOk bit + +Command response on an arbiter, recovering member, or ghost when SecondaryOk is true: + +> {ok: 0, errmsg: "node is recovering"} + +### Count without SecondaryOk bit + +Command response on an arbiter, recovering member, ghost, or secondary when SecondaryOk is false: + +> {ok: 0, errmsg: "not writable primary"} + +## Topology discovery and direct connection + +### Topology discovery + +Scenario: given a replica set deployment with a secondary, where HOST is the address of the secondary, create a +MongoClient using `mongodb://HOST/?directConnection=false` as the URI. Attempt a write to a collection. + +Outcome: Verify that the write succeeded. + +### Direct connection + +Scenario: given a replica set deployment with a secondary, where HOST is the address of the secondary, create a +MongoClient using `mongodb://HOST/?directConnection=true` as the URI. Attempt a write to a collection. + +Outcome: Verify that the write failed with a NotWritablePrimary error. + +### Existing behavior + +Scenario: given a replica set deployment with a secondary, where HOST is the address of the secondary, create a +MongoClient using `mongodb://HOST/` as the URI. Attempt a write to a collection. + +Outcome: Verify that the write succeeded or failed depending on existing driver behavior with respect to the starting +topology. + +## Monitors sleep at least minHeartbeatFrequencyMS between checks + +This test will be used to ensure monitors sleep for an appropriate amount of time between failed server checks so as to +not flood the server with new connection creations. + +This test requires MongoDB 4.9.0+. + +1. Enable the following failpoint: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 5 }, + data: { + failCommands: ["hello"], // or legacy hello command + errorCode: 1234, + appName: "SDAMMinHeartbeatFrequencyTest" + } + } + ``` + +2. Create a client with directConnection=true, appName="SDAMMinHeartbeatFrequencyTest", and + serverSelectionTimeoutMS=5000. + +3. Start a timer. + +4. Execute a `ping` command. + +5. Stop the timer. Assert that the `ping` took between 2 seconds and 3.5 seconds to complete. + +## Connection Pool Management + +This test will be used to ensure monitors properly create and unpause connection pools when they discover servers. + +This test requires failCommand appName support which is only available in MongoDB 4.2.9+. + +1. Create a client with directConnection=true, appName="SDAMPoolManagementTest", and heartbeatFrequencyMS=500 (or lower + if possible). + +2. Verify via SDAM and CMAP event monitoring that a ConnectionPoolReadyEvent occurs after the first + ServerHeartbeatSucceededEvent event does. + +3. Enable the following failpoint: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 2 }, + data: { + failCommands: ["hello"], // or legacy hello command + errorCode: 1234, + appName: "SDAMPoolManagementTest" + } + } + ``` + +4. Verify that a ServerHeartbeatFailedEvent and a ConnectionPoolClearedEvent (CMAP) are emitted. + +5. Then verify that a ServerHeartbeatSucceededEvent and a ConnectionPoolReadyEvent (CMAP) are emitted. + +6. Disable the failpoint. + +## Connection Pool Backpressure + +This test will be used to ensure that connection establishment failures during the TLS handshake do not result in a pool +clear event. We create a setup client to enable the ingress connection establishment rate limiter, and then induce a +connection storm. After the storm, we verify that some of the connections failed to checkout, but that the pool was not +cleared. + +This test requires MongoDB 7.0+. See the +[MongoDB Server Parameters](https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.ingressConnectionEstablishmentRateLimiterEnabled) +for more details. + +This test MUST be run both with and without TLS enabled. Without TLS enabled, the connection establishment will fail +during the `hello` message, and with TLS enabled it will fail during the TLS handshake. + +If running against a sharded cluster, this test MUST be run against only a single host, similar to how +`useMultipleMongoses:false` would be handled in a unified test. + +1. Create a test client that listens to CMAP events, with maxConnecting=100. The higher maxConnecting will help ensure + contention for creating connections. + +2. Run the following commands to set up the rate limiter. + + ```python + client.admin.command("setParameter", 1, ingressConnectionEstablishmentRateLimiterEnabled=True) + client.admin.command("setParameter", 1, ingressConnectionEstablishmentRatePerSec=20) + client.admin.command("setParameter", 1, ingressConnectionEstablishmentBurstCapacitySecs=1) + client.admin.command("setParameter", 1, ingressConnectionEstablishmentMaxQueueDepth=1) + ``` + +3. Add a document to the test collection so that the sleep operations will actually block: + `client.test.test.insert_one({})`. + +4. Run the following find command on the collection in 100 parallel threads/coroutines. Run these commands concurrently + but block on their completion, and ignore errors raised by the command. + `client.test.test.find_one({"$where": "function() { sleep(2000); return true; }})` + +5. Assert that at least 10 `ConnectionCheckOutFailedEvent` occurred. + +6. Assert that 0 `PoolClearedEvent` occurred. + +7. Ensure that the following steps run at test teardown even if the test fails: + + 7.1. Sleep for 1 second to clear the rate limiter. + + 7.2. Execute the following command: + `client.admin("setParameter", 1, ingressConnectionEstablishmentRateLimiterEnabled=False)`. diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst index 21cf9680f8..635ffb3c80 100644 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst @@ -1,145 +1,4 @@ -============================================ -Server Discovery And Monitoring -- Test Plan -============================================ -:Spec: 101 -:Title: Server Discovery And Monitoring -:Author: A\. Jesse Jiryu Davis -:Advisors: David Golden, Craig Wilson -:Status: Draft -:Type: Standards -:Last Modified: September 8, 2014 - -See also the YAML test files and their accompanying README in the "tests" -directory. - -.. contents:: - --------- - -All servers unavailable ------------------------ - -A MongoClient can be constructed without an exception, -even with all seeds unavailable. - -Network error writing to primary --------------------------------- - -Scenario: With TopologyType ReplicaSetWithPrimary, a write to the primary fails -with a network error other than timeout. - -Outcome: The former primary's ServerType MUST become Unknown. -The TopologyType MUST change to ReplicaSetNoPrimary. -The client MUST NOT immediately re-check the former primary. - -"Not master" error when reading without SlaveOkay bit ------------------------------------------------------ - -Scenario: With TopologyType ReplicaSetWithPrimary, we read from a server we -thought was RSPrimary. Thus the slaveOk bit is not set. - -The response's QueryFailure bit is set and the response document is: - - {$err: "not master and slaveOk=false"} - -Outcome: The former primary's ServerType MUST become Unknown. -The TopologyType MUST change to ReplicaSetNoPrimary. -The client MUST NOT immediately re-check the former primary. - -"Node is recovering" error reading with SlaveOkay bit ------------------------------------------------------ - -Scenario: With TopologyType ReplicaSetWithPrimary, we read from a server we -thought was RSSecondary. Thus the slaveOk bit *is* set. - -The response's QueryFailure bit is set and the response document is: - - {$err: "not master or secondary; cannot currently read from this replSet member"} - -Outcome: The former secondary's ServerType MUST become Unknown. -The TopologyType MUST remain ReplicaSetWithPrimary. -A multi-threaded client MUST immediately re-check the former secondary, -a single-threaded client MUST NOT. - -"Node is recovering" error from a write concern error ------------------------------------------------------ - -Scenario: With TopologyType ReplicaSetWithPrimary, a write to the primary responds -with the following document: - - { ok: 1, writeConcernError: {code: 91, errmsg: "Replication is being shut down"} } - -Outcome: The former primary's ServerType MUST become Unknown. -The TopologyType MUST change to ReplicaSetNoPrimary. -A multi-threaded client MUST immediately re-check the former secondary, -a single-threaded client MUST NOT. - -Parsing "not master" and "node is recovering" errors ----------------------------------------------------- - -For all these example responses, -the client MUST mark the server "Unknown" -and store the error message in the ServerDescription's error field. - -Clients MUST NOT depend on any particular field order in these responses. - -getLastError -'''''''''''' - -GLE response after OP_INSERT on an arbiter, secondary, slave, -recovering member, or ghost: - - {ok: 1, err: "not master"} - -`Possible GLE response in MongoDB 2.6`_ during failover: - - {ok: 1, err: "replicatedToNum called but not master anymore"} - -Note that this error message contains "not master" but does not start with it. - -.. _Possible GLE response in MongoDB 2.6: https://jira.mongodb.org/browse/SERVER-9617 - -Write command -''''''''''''' - -Response to an "insert" command on an arbiter, secondary, slave, -recovering member, or ghost: - - {ok: 0, errmsg: "not master"} - -Query with slaveOk bit -'''''''''''''''''''''' - -Response from an arbiter, recovering member, or ghost -when slaveOk is true: - - {$err: "not master or secondary; cannot currently read from this replSet member"} - -The QueryFailure bit is set in responseFlags. - -Query without slaveOk bit -''''''''''''''''''''''''' - -Response from an arbiter, recovering member, ghost, or secondary -when slaveOk is false: - - {$err: "not master and slaveOk=false"} - -The QueryFailure bit is set in responseFlags. - -Count with slaveOk bit -'''''''''''''''''''''' - -Command response on an arbiter, recovering member, or ghost -when slaveOk is true: - - {ok: 0, errmsg: "node is recovering"} - -Count without slaveOk bit -''''''''''''''''''''''''' - -Command response on an arbiter, recovering member, ghost, or secondary -when slaveOk is false: - - {ok: 0, errmsg: "not master"} +.. note:: + This specification has been converted to Markdown and renamed to + `server-discovery-and-monitoring-tests.md `_. diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md new file mode 100644 index 0000000000..8ddfc47cca --- /dev/null +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md @@ -0,0 +1,2045 @@ +# Server Discovery And Monitoring + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Abstract + +This spec defines how a MongoDB client discovers and monitors one or more servers. It covers monitoring a single server, +a set of mongoses, or a replica set. How does the client determine what type of servers they are? How does it keep this +information up to date? How does the client find an entire replica set from a seed list, and how does it respond to a +stepdown, election, reconfiguration, or network error? + +All drivers must answer these questions the same. Or, where platforms' limitations require differences among drivers, +there must be as few answers as possible and each must be clearly explained in this spec. Even in cases where several +answers seem equally good, drivers must agree on one way to do it. + +MongoDB users and driver authors benefit from having one way to discover and monitor servers. Users can substantially +understand their driver's behavior without inspecting its code or asking its author. Driver authors can avoid subtle +mistakes when they take advantage of a design that has been well-considered, reviewed, and tested. + +The server discovery and monitoring method is specified in four sections. First, a client is +[configured](#configuration). Second, it begins [monitoring](#monitoring) by calling +[hello or legacy hello](../mongodb-handshake/handshake.md#terms) on all servers. (Multi-threaded and asynchronous +monitoring is described first, then single-threaded monitoring.) Third, as hello or legacy hello responses are received +the client [parses them](#parsing-a-hello-or-legacy-hello-response), and fourth, it updates its view of the topology. + +Finally, this spec describes how drivers update their topology view in response to errors, and includes generous +implementation notes for driver authors. + +This spec does not describe how a client chooses a server for an operation; that is the domain of the Server Selection +Spec. But there is a section describing the interaction between monitoring and server selection. + +There is no discussion of driver architecture and data structures, nor is there any specification of a user-facing API. +This spec is only concerned with the algorithm for monitoring the server topology. + +## Meta + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### General Requirements + +**Direct connections:** A client MUST be able to connect to a single server of any type. This includes querying hidden +replica set members, and connecting to uninitialized members (see [RSGhost](#rsghost-and-rsother)) in order to run +"replSetInitiate". Setting a read preference MUST NOT be necessary to connect to a secondary. Of course, the secondary +will reject all operations done with the PRIMARY read preference because the secondaryOk bit is not set, but the initial +connection itself succeeds. Drivers MAY allow direct connections to arbiters (for example, to run administrative +commands). + +**Replica sets:** A client MUST be able to discover an entire replica set from a seed list containing one or more +replica set members. It MUST be able to continue monitoring the replica set even when some members go down, or when +reconfigs add and remove members. A client MUST be able to connect to a replica set while there is no primary, or the +primary is down. + +**Mongos:** A client MUST be able to connect to a set of mongoses and monitor their availability and +[round trip time](#round-trip-time). This spec defines how mongoses are discovered and monitored, but does not define +which mongos is selected for a given operation. + +### Terms + +#### Server + +A mongod or mongos process, or a load balancer. + +#### Deployment + +One or more servers: either a standalone, a replica set, or one or more mongoses. + +#### Topology + +The state of the deployment: its type (standalone, replica set, or sharded), which servers are up, what type of servers +they are, which is primary, and so on. + +#### Client + +Driver code responsible for connecting to MongoDB. + +#### Seed list + +Server addresses provided to the client in its initial configuration, for example from the +[connection string](https://www.mongodb.com/docs/manual/reference/connection-string/). + +#### Data-Bearing Server Type + +A server type from which a client can receive application data: + +- Mongos +- RSPrimary +- RSSecondary +- Standalone +- LoadBalanced + +#### Round trip time + +Also known as RTT. + +The client's measurement of the duration of one hello or legacy hello call. The round trip time is used to support the +"localThresholdMS"[^1] option in the Server Selection Spec. + +#### hello or legacy hello outcome + +The result of an attempt to call the hello or legacy hello command on a server. It consists of three elements: a boolean +indicating the success or failure of the attempt, a document containing the command response (or null if it failed), and +the round trip time to execute the command (or null if it failed). + +#### check + +The client checks a server by attempting to call hello or legacy hello on it, and recording the outcome. + +#### scan + +The process of checking all servers in the deployment. + +#### suitable + +A server is judged "suitable" for an operation if the client can use it for a particular operation. For example, a write +requires a standalone, primary, or mongos. Suitability is fully specified in the +[Server Selection Spec](../server-selection/server-selection.md). + +#### address + +The hostname or IP address, and port number, of a MongoDB server. + +#### network error + +An error that occurs while reading from or writing to a network socket. + +#### network timeout + +A timeout that occurs while reading from or writing to a network socket. + +#### minHeartbeatFrequencyMS + +Defined in the [Server Monitoring spec](server-monitoring.md). This value MUST be 500 ms, and it MUST NOT be +configurable. + +#### pool generation number + +The pool's generation number which starts at 0 and is incremented each time the pool is cleared. Defined in the +[Connection Monitoring and Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md). + +#### connection generation number + +The pool's generation number at the time this connection was created. Defined in the +[Connection Monitoring and Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md). + +#### error generation number + +The error's generation number is the generation of the connection on which the application error occurred. Note that +when a network error occurs before the handshake completes then the error's generation number is the generation of the +pool at the time the connection attempt was started. + +#### State Change Error + +A server reply document indicating a "not writable primary" or "node is recovering" error. Starting in MongoDB 4.4 these +errors may also include a [topologyVersion](#topologyversion) field. + +### Data structures + +This spec uses a few data structures to describe the client's view of the topology. It must be emphasized that a driver +is free to implement the same behavior using different data structures. This spec uses these enums and structs in order +to describe driver **behavior**, not to mandate how a driver represents the topology, nor to mandate an API. + +#### Constants + +##### clientMinWireVersion and clientMaxWireVersion + +Integers. The wire protocol range supported by the client. + +#### Enums + +##### TopologyType + +Single, ReplicaSetNoPrimary, ReplicaSetWithPrimary, Sharded, LoadBalanced, or Unknown. + +See [updating the TopologyDescription](#updating-the-topologydescription). + +##### ServerType + +Standalone, Mongos, PossiblePrimary, RSPrimary, RSSecondary, RSArbiter, RSOther, RSGhost, LoadBalancer or Unknown. + +See [parsing a hello or legacy hello response](#parsing-a-hello-or-legacy-hello-response). + +> [!NOTE] +> Single-threaded clients use the PossiblePrimary type to maintain proper +> [scanning order](server-monitoring.md#scanning-order). Multi-threaded and asynchronous clients do not need this +> ServerType; it is synonymous with Unknown. + + + +#### TopologyDescription + +The client's representation of everything it knows about the deployment's topology. + +Fields: + +- `type`: a [TopologyType](#topologytype) enum value. See [initial TopologyType](#initial-topologytype). +- `setName`: the replica set name. Default null. +- `maxElectionId`: an ObjectId or null. The largest electionId ever reported by a primary. Default null. Part of the + (`electionId`, `setVersion`) tuple. +- `maxSetVersion`: an integer or null. The largest setVersion ever reported by a primary. It may not monotonically + increase, as electionId takes precedence in ordering Default null. Part of the (`electionId`, `setVersion`) tuple. +- `servers`: a set of ServerDescription instances, one for each of the servers in the topology. +- `stale`: a boolean for single-threaded clients, whether the topology must be re-scanned. (Not related to + maxStalenessSeconds, nor to stale primaries.) +- `compatible`: a boolean. False if any server's wire protocol version range is incompatible with the client's. Default + true. +- `compatibilityError`: a string. The error message if "compatible" is false, otherwise null. +- `logicalSessionTimeoutMinutes`: integer or null. Default null. See + [logical session timeout](#logical-session-timeout). + +#### ServerDescription + +The client's view of a single server, based on the most recent hello or legacy hello outcome. + +Again, drivers may store this information however they choose; this data structure is defined here merely to describe +the monitoring algorithm. + +Fields: + +- `address`: the hostname or IP, and the port number, that the client connects to. Note that this is **not** the `me` + field in the server's hello or legacy hello response, in the case that the server reports an address different from + the address the client uses. + +- (=) `error`: information about the last error related to this server. Default null. MUST contain or be able to produce + a string describing the error. + +- `roundTripTime`: the duration of the hello or legacy hello call. Default null. + +- `minRoundTripTime`: the minimum RTT for the server. Default null. + +- `lastWriteDate`: a 64-bit BSON datetime or null. The `lastWriteDate` from the server's most recent hello or legacy + hello response. + +- `opTime`: an opTime or null. An opaque value representing the position in the oplog of the most recently seen write. + Default null. (Only mongos and shard servers record this field when monitoring config servers as replica sets, at + least until + [drivers allow applications to use readConcern "afterOptime".](../max-staleness/max-staleness.md#future-feature-to-support-readconcern-afteroptime)) + +- (=) `type`: a [ServerType](#servertype) enum value. Default Unknown. + +- (=) `minWireVersion`, `maxWireVersion`: the wire protocol version range supported by the server. Both default to 0. + [Use min and maxWireVersion only to determine compatibility](#checking-wire-protocol-compatibility). + +- (=) `me`: The hostname or IP, and the port number, that this server was configured with in the replica set. Default + null. + +- (=) `hosts`, `passives`, `arbiters`: Sets of addresses. This server's opinion of the replica set's members, if any. + These [hostnames are normalized to lower-case](#hostnames-are-normalized-to-lower-case). Default empty. The client + monitors all three types of servers in a replica set. + +- (=) `tags`: map from string to string. Default empty. + +- (=) `setName`: string or null. Default null. + +- (=) `electionId`: an ObjectId, if this is a MongoDB 2.6+ replica set member that believes it is primary. See + [using electionId and setVersion to detect stale primaries](#using-electionid-and-setversion-to-detect-stale-primaries). + Default null. + +- (=) `setVersion`: integer or null. Default null. + +- (=) `primary`: an address. This server's opinion of who the primary is. Default null. + +- `lastUpdateTime`: when this server was last checked. Default "infinity ago". + +- (=) `logicalSessionTimeoutMinutes`: integer or null. Default null. + +- (=) `topologyVersion`: A topologyVersion or null. Default null. The "topologyVersion" from the server's most recent + hello or legacy hello response or [State Change Error](#state-change-error). + +- (=) `iscryptd`: boolean indicating if the server is a + [mongocryptd](../client-side-encryption/client-side-encryption.md#mongocryptd) server. Default null. + +"Passives" are priority-zero replica set members that cannot become primary. The client treats them precisely the same +as other members. + +Fields marked (=) are used for [Server Description Equality](#server-description-equality) comparison. + +### Configuration + +#### No breaking changes + +This spec does not intend to require any drivers to make breaking changes regarding what configuration options are +available, how options are named, or what combinations of options are allowed. + +#### Initial TopologyDescription + +The default values for [TopologyDescription](#topologydescription) fields are described above. Users may override the +defaults as follows: + +##### Initial Servers + +The user MUST be able to set the initial servers list to a [seed list](#seed-list) of one or more addresses. + +The hostname portion of each address MUST be normalized to lower-case. + +##### Initial TopologyType + +If the `directConnection` URI option is specified when a MongoClient is constructed, the TopologyType must be +initialized based on the value of the `directConnection` option and the presence of the `replicaSet` option according to +the following table: + +| directConnection | replicaSet present | Initial TopologyType | +| ---------------- | ------------------ | -------------------- | +| true | no | Single | +| true | yes | Single | +| false | no | Unknown | +| false | yes | ReplicaSetNoPrimary | + +If the `directConnection` option is not specified, newly developed drivers MUST behave as if it was specified with the +false value. + +Since changing the starting topology can reasonably be considered a backwards-breaking change, existing drivers SHOULD +stage implementation according to semantic versioning guidelines. Specifically, support for the `directConnection` URI +option can be added in a minor release. In a subsequent major release, the default starting topology can be changed to +Unknown. Drivers MUST document this in a prior minor release. + +Existing drivers MUST deprecate other URI options, if any, for controlling topology discovery or specifying the +deployment topology. If such a legacy option is specified and the `directConnection` option is also specified, and the +values of the two options are semantically different, the driver MUST report an error during URI option parsing. + +The API for initializing TopologyType using language-specific native options is not specified here. Drivers might +already have a convention, e.g. a single seed means Single, a setName means ReplicaSetNoPrimary, and a list of seeds +means Unknown. There are variations, however: In the Java driver a single seed means Single, but a **list** containing +one seed means Unknown, so it can transition to replica-set monitoring if the seed is discovered to be a replica set +member. In contrast, PyMongo requires a non-null setName in order to begin replica-set monitoring, regardless of the +number of seeds. This spec does not cover language-specific native options that a driver may provide. + +##### Initial setName + +It is allowed to use `directConnection=true` in conjunction with the `replicaSet` URI option. The driver must connect in +Single topology and verify that setName matches the specified name, as per +[verifying setName with TopologyType Single](#verifying-setname-with-topologytype-single). + +When a MongoClient is initialized using language-specific native options, the user MUST be able to set the client's +initial replica set name. A driver MAY require the set name in order to connect to a replica set, or it MAY be able to +discover the replica set name as it connects. + +##### Allowed configuration combinations + +Drivers MUST enforce: + +- TopologyType Single cannot be used with multiple seeds. +- `directConnection=true` cannot be used with multiple seeds. +- If setName is not null, only TopologyType ReplicaSetNoPrimary, and possibly Single, are allowed. (See + [verifying setName with TopologyType Single](#verifying-setname-with-topologytype-single).) +- `loadBalanced=true` cannot be used in conjunction with `directConnection=true` or `replicaSet` + +##### Handling of SRV URIs resolving to single host + +When a driver is given an SRV URI, if the `directConnection` URI option is not specified, and the `replicaSet` URI +option is not specified, the driver MUST start in Unknown topology, and follow the rules in the +[TopologyType table](#topologytype-table) for transitioning to other topologies. In particular, the driver MUST NOT use +the number of hosts from the initial SRV lookup to decide what topology to start in. + + + +#### heartbeatFrequencyMS + +The interval between server [checks](#check), counted from the end of the previous check until the beginning of the next +one. + +For multi-threaded and asynchronous drivers it MUST default to 10 seconds and MUST be configurable. For single-threaded +drivers it MUST default to 60 seconds and MUST be configurable. It MUST be called heartbeatFrequencyMS unless this +breaks backwards compatibility. + +For both multi- and single-threaded drivers, the driver MUST NOT permit users to configure it less than +minHeartbeatFrequencyMS (500ms). + +(See +[heartbeatFrequencyMS defaults to 10 seconds or 60 seconds](#heartbeatfrequencyms-defaults-to-10-seconds-or-60-seconds) +and [what's the point of periodic monitoring?](#whats-the-point-of-periodic-monitoring)) + +### Client construction + +Except for [initial DNS seed list discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) when +given a connection string with `mongodb+srv` scheme, the client's constructor MUST NOT do any I/O. This means that the +constructor does not throw an exception if servers are unavailable: the topology is not yet known when the constructor +returns. Similarly if a server has an incompatible wire protocol version, the constructor does not throw. Instead, all +subsequent operations on the client fail as long as the error persists. + +See [clients do no I/O in the constructor](#clients-do-no-io-in-the-constructor) for the justification. + +#### Multi-threaded and asynchronous client construction + +The constructor MAY start the monitors as background tasks and return immediately. Or the monitors MAY be started by +some method separate from the constructor; for example they MAY be started by some "initialize" method (by any name), or +on the first use of the client for an operation. + +#### Single-threaded client construction + +Single-threaded clients do no I/O in the constructor. They MUST [scan](#scan) the servers on demand, when the first +operation is attempted. + +### Client closing + +When a client is closing, before it emits the `TopologyClosedEvent` as per the +[Events API](./server-discovery-and-monitoring-logging-and-monitoring.md#events-api), it SHOULD [remove](#remove) all +servers from its `TopologyDescription` and set its `TopologyType` to `Unknown`, emitting the corresponding +`TopologyDescriptionChangedEvent`. + +### Monitoring + +See the [Server Monitoring spec](server-monitoring.md) for how a driver monitors each server. In summary, the client +monitors each server in the topology. The scope of server monitoring is to provide the topology with updated +ServerDescriptions based on hello or legacy hello command responses. + +### Parsing a hello or legacy hello response + +The client represents its view of each server with a [ServerDescription](#serverdescription). Each time the client +[checks](#check) a server, it MUST replace its description of that server with a new one if and only if the new +ServerDescription's [topologyVersion](#topologyversion) is greater than or equal to the current ServerDescription's +[topologyVersion](#topologyversion). + +(See [Replacing the TopologyDescription](#replacing-the-topologydescription) for an example implementation.) + +This replacement MUST happen even if the new server description compares equal to the previous one, in order to keep +client-tracked attributes like last update time and round trip time up to date. + +Drivers MUST be able to handle responses to both `hello` and legacy hello commands. When checking results, drivers MUST +first check for the `isWritablePrimary` field and fall back to checking for an `ismaster` field if `isWritablePrimary` +was not found. + +ServerDescriptions are created from hello or legacy hello outcomes as follows: + +#### type + +The new ServerDescription's type field is set to a [ServerType](#servertype). Note that these states do **not** exactly +correspond to [replica set member states](https://www.mongodb.com/docs/manual/reference/replica-states/). For example, +some replica set member states like STARTUP and RECOVERING are identical from the client's perspective, so they are +merged into "RSOther". Additionally, states like Standalone and Mongos are not replica set member states at all. + +| State | Symptoms | +| --------------- | -------------------------------------------------------------------------------------------------------- | +| Unknown | Initial, or after a failed hello or legacy hello call, or "ok: 1" not in hello or legacy hello response. | +| Standalone | No "msg: isdbgrid", no setName, and no "isreplicaset: true". | +| Mongos | "msg: isdbgrid". | +| PossiblePrimary | Not yet checked, but another member thinks it is the primary. | +| RSPrimary | "isWritablePrimary: true" or "ismaster: true", "setName" in response. | +| RSSecondary | "secondary: true", "setName" in response. | +| RSArbiter | "arbiterOnly: true", "setName" in response. | +| RSOther | "setName" in response, "hidden: true" or not primary, secondary, nor arbiter. | +| RSGhost | "isreplicaset: true" in response. | +| LoadBalanced | "loadBalanced=true" in URI. | + +A server can transition from any state to any other. For example, an administrator could shut down a secondary and bring +up a mongos in its place. + +##### RSGhost and RSOther + +The client MUST monitor replica set members even when they cannot be queried. These members are in state RSGhost or +RSOther. + +**RSGhost** members occur in at least three situations: + +- briefly during server startup, +- in an uninitialized replica set, +- or when the server is shunned (removed from the replica set config). + +An RSGhost server has no hosts list nor setName. Therefore the client MUST NOT attempt to use its hosts list nor check +its setName (see [JAVA-1161](https://jira.mongodb.org/browse/JAVA-1161) or +[CSHARP-671](https://jira.mongodb.org/browse/CSHARP-671).) However, the client MUST keep the RSGhost member in its +TopologyDescription, in case the client's only hope for staying connected to the replica set is that this member will +transition to a more useful state. + +For simplicity, this is the rule: any server is an RSGhost that reports "isreplicaset: true". + +Non-ghost replica set members have reported their setNames since MongoDB 1.6.2. See +[only support replica set members running MongoDB 1.6.2 or later](#only-support-replica-set-members-running-mongodb-162-or-later). + +> [!NOTE] +> The Java driver does not have a separate state for RSGhost; it is an RSOther server with no hosts list. + +**RSOther** servers may be hidden, starting up, or recovering. They cannot be queried, but their hosts lists are useful +for discovering the current replica set configuration. + +If a [hidden member](https://www.mongodb.com/docs/manual/core/replica-set-hidden-member/) is provided as a seed, the +client can use it to find the primary. Since the hidden member does not appear in the primary's host list, it will be +removed once the primary is checked. + +#### error + +If the client experiences any error when checking a server, it stores error information in the ServerDescription's error +field. The message contained in this field MUST contain the substrings detailed in the table below when the +ServerDescription is changed to Unknown in the circumstances outlined. + +| circumstance | error substring | +| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| RSPrimary with a stale electionId/setVersion is discovered | `'primary marked stale due to electionId/setVersion mismatch, is stale compared to '` | +| New primary is elected/discovered | `'primary marked stale due to discovery of newer primary'` | + +#### roundTripTime + +Drivers MUST record the server's [round trip time](#round-trip-time) (RTT) after each successful call to hello or legacy +hello. The Server Selection Spec describes how RTT is averaged and how it is used in server selection. Drivers MUST also +record the server's minimum RTT per [Server Monitoring (Measuring RTT)](server-monitoring.md#measuring-rtt). + +If a hello or legacy hello call fails, the RTT is not updated. Furthermore, while a server's type is Unknown its RTT is +null, and if it changes from a known type to Unknown its RTT is set to null. However, if it changes from one known type +to another (e.g. from RSPrimary to RSSecondary) its RTT is updated normally, not set to null nor restarted from scratch. + +#### lastWriteDate and opTime + +The hello or legacy hello response of a replica set member running MongoDB 3.4 and later contains a `lastWrite` +subdocument with fields `lastWriteDate` and `opTime` ([SERVER-8858](https://jira.mongodb.org/browse/SERVER-8858)). If +these fields are available, parse them from the hello or legacy hello response, otherwise set them to null. + +Clients MUST NOT attempt to compensate for the network latency between when the server generated its hello or legacy +hello response and when the client records `lastUpdateTime`. + +#### lastUpdateTime + +Clients SHOULD set lastUpdateTime with a monotonic clock. + +#### Hostnames are normalized to lower-case + +The same as with seeds provided in the initial configuration, all hostnames in the hello or legacy hello response's +"me", "hosts", "passives", and "arbiters" entries MUST be lower-cased. + +This prevents unnecessary work rediscovering a server if a seed "A" is provided and the server responds that "a" is in +the replica set. + +[RFC 4343](http://tools.ietf.org/html/rfc4343): + +> Domain Name System (DNS) names are "case insensitive". + +#### logicalSessionTimeoutMinutes + +MongoDB 3.6 and later include a `logicalSessionTimeoutMinutes` field if logical sessions are enabled in the deployment. +Clients MUST check for this field and set the ServerDescription's logicalSessionTimeoutMinutes field to this value, or +to null otherwise. + +#### topologyVersion + +MongoDB 4.4 and later include a `topologyVersion` field in all hello or legacy hello and +[State Change Error](#state-change-error) responses. Clients MUST check for this field and set the ServerDescription's +topologyVersion field to this value, if present. The topologyVersion helps the client and server determine the relative +freshness of topology information in concurrent messages. (See +[What is the purpose of topologyVersion?](#what-is-the-purpose-of-topologyversion)) + +The topologyVersion is a subdocument with two fields, "processId" and "counter": + +```typescript +{ + topologyVersion: {processId: , counter: }, + ( ... other fields ...) +} +``` + +##### topologyVersion Comparison + +To compare a topologyVersion from a hello or legacy hello or State Change Error response to the current +ServerDescription's topologyVersion: + +1. If the response topologyVersion is unset or the ServerDescription's topologyVersion is null, the client MUST assume + the response is more recent. +2. If the response's topologyVersion.processId is not equal to the ServerDescription's, the client MUST assume the + response is more recent. +3. If the response's topologyVersion.processId is equal to the ServerDescription's, the client MUST use the counter + field to determine which topologyVersion is more recent. + +See [Replacing the TopologyDescription](#replacing-the-topologydescription) for an example implementation of +topologyVersion comparison. + +#### serviceId + +MongoDB 5.0 and later, as well as any mongos-like service, include a `serviceId` field when the service is configured +behind a load balancer. + +#### Other ServerDescription fields + +Other required fields defined in the [ServerDescription](#serverdescription) data structure are parsed from the hello or +legacy hello response in the obvious way. + +#### Server Description Equality + +For the purpose of determining whether to publish SDAM events, two server descriptions having the same address MUST be +considered equal if and only if the values of [ServerDescription](#serverdescription) fields marked (=) are respectively +equal. + +This specification does not prescribe how to compare server descriptions with different addresses for equality. + +### Updating the TopologyDescription + +Each time the client checks a server, it processes the outcome (successful or not) to create a +[ServerDescription](#serverdescription), and then it processes the ServerDescription to update its +[TopologyDescription](#topologydescription). + +The TopologyDescription's [TopologyType](#topologytype) influences how the ServerDescription is processed. The following +subsection specifies how the client updates its TopologyDescription when the TopologyType is Single. The next subsection +treats the other types. + +#### TopologyType Single + +The TopologyDescription's type was initialized as Single and remains Single forever. There is always one +ServerDescription in TopologyDescription.servers. + +Whenever the client checks a server (successfully or not), and regardless of whether the new server description is equal +to the previous server description as defined in [Server Description Equality](#server-description-equality), the +ServerDescription in TopologyDescription.servers MUST be replaced with the new ServerDescription. + + + +##### Checking wire protocol compatibility + +A ServerDescription which is not Unknown is incompatible if: + +- `minWireVersion` > `clientMaxWireVersion`, or +- `maxWireVersion` < `clientMinWireVersion` + +If any ServerDescription is incompatible, the client MUST set the TopologyDescription's "compatible" field to false and +fill out the TopologyDescription's "compatibilityError" field like so: + +- if `ServerDescription.minWireVersion` > `clientMaxWireVersion`: + + "Server at `host`:`port` requires wire version `minWireVersion`, but this version of `driverName` only supports up to + `clientMaxWireVersion`." + +- if `ServerDescription.maxWireVersion` < `clientMinWireVersion`: + + "Server at `host`:`port` reports wire version `maxWireVersion`, but this version of `driverName` requires at least + `clientMinWireVersion` (MongoDB `mongoVersion`)." + +Replace `mongoVersion` with the appropriate MongoDB minor version, for example if `clientMinWireVersion` is 2 and it +connects to MongoDB 2.4, format the error like: + +> "Server at example.com:27017 reports wire version 0, but this version of My Driver requires at least 2 (MongoDB 2.6)." + +In this second case, the exact required MongoDB version is known and can be named in the error message, whereas in the +first case the implementer does not know which MongoDB versions will be compatible or incompatible in the future. + +##### Verifying setName with TopologyType Single + +A client MAY allow the user to supply a setName with an initial TopologyType of Single. In this case, if the +ServerDescription's setName is null or wrong, the ServerDescription MUST be replaced with a default ServerDescription of +type Unknown. + +#### TopologyType LoadBalanced + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#server-discovery-logging-and-monitoring) for +details. + +#### Other TopologyTypes + +If the TopologyType is **not** Single, the topology can contain zero or more servers. The state of topology containing +zero servers is terminal (because servers can only be added if they are reported by a server already in the topology). A +client SHOULD emit a warning if it is constructed with no seeds in the initial seed list. A client SHOULD emit a warning +when, in the process of updating its topology description, it removes the last server from the topology. + +Whenever a client completes a hello or legacy hello call, it creates a new ServerDescription with the proper +[ServerType](#servertype). It replaces the server's previous description in TopologyDescription.servers with the new +one. + +Apply the logic for [checking wire protocol compatibility](#checking-wire-protocol-compatibility) to each +ServerDescription in the topology. If any server's wire protocol version range does not overlap with the client's, the +client updates the "compatible" and "compatibilityError" fields as described above for TopologyType Single. Otherwise +"compatible" is set to true. + +It is possible for a multi-threaded client to receive a hello or legacy hello outcome from a server after the server has +been removed from the TopologyDescription. For example, a monitor begins checking a server "A", then a different monitor +receives a response from the primary claiming that "A" has been removed from the replica set, so the client removes "A" +from the TopologyDescription. Then, the check of server "A" completes. + +In all cases, the client MUST ignore hello or legacy hello outcomes from servers that are not in the +TopologyDescription. + +The following subsections explain in detail what actions the client takes after replacing the ServerDescription. + +##### TopologyType table + +The new ServerDescription's type is the vertical axis, and the current TopologyType is the horizontal. Where a +ServerType and a TopologyType intersect, the table shows what action the client takes. + +"no-op" means, do nothing **after** replacing the server's old description with the new one. + +| | TopologyType Unknown | TopologyType Sharded | TopologyType ReplicaSetNoPrimary | TopologyType ReplicaSetWithPrimary | +| ---------------------- | ----------------------------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| ServerType Unknown | no-op | no-op | no-op | [checkIfHasPrimary](#checkifhasprimary) | +| ServerType Standalone | [updateUnknownWithStandalone](#updateunknownwithstandalone) | [remove](#remove) | [remove](#remove) | [remove](#remove) and [checkIfHasPrimary](#checkifhasprimary) | +| ServerType Mongos | Set topology type to Sharded | no-op | [remove](#remove) | [remove](#remove) and [checkIfHasPrimary](#checkifhasprimary) | +| ServerType RSPrimary | Set topology type to ReplicaSetWithPrimary then [updateRSFromPrimary](#updatersfromprimary) | [remove](#remove) | Set topology type to ReplicaSetWithPrimary then [updateRSFromPrimary](#updatersfromprimary) | [updateRSFromPrimary](#updatersfromprimary) | +| ServerType RSSecondary | Set topology type to ReplicaSetNoPrimary then [updateRSWithoutPrimary](#updaterswithoutprimary) | [remove](#remove) | [updateRSWithoutPrimary](#updaterswithoutprimary) | [updateRSWithPrimaryFromMember](#updaterswithprimaryfrommember) | +| ServerType RSArbiter | Set topology type to ReplicaSetNoPrimary then [updateRSWithoutPrimary](#updaterswithoutprimary) | [remove](#remove) | [updateRSWithoutPrimary](#updaterswithoutprimary) | [updateRSWithPrimaryFromMember](#updaterswithprimaryfrommember) | +| ServerType RSOther | Set topology type to ReplicaSetNoPrimary then [updateRSWithoutPrimary](#updaterswithoutprimary) | [remove](#remove) | [updateRSWithoutPrimary](#updaterswithoutprimary) | [updateRSWithPrimaryFromMember](#updaterswithprimaryfrommember) | +| ServerType RSGhost | no-op[^2] | [remove](#remove) | no-op | [checkIfHasPrimary](#checkifhasprimary) | + +##### TopologyType explanations + +This subsection complements the [TopologyType table](#topologytype-table) with prose explanations of the TopologyTypes +(besides Single and LoadBalanced). + +**TopologyType Unknown** + +A starting state. + +**Actions**: + +- If the incoming ServerType is Unknown (that is, the hello or legacy hello call failed), keep the server in + TopologyDescription.servers. The TopologyType remains Unknown. +- The + [TopologyType remains Unknown when an RSGhost is discovered](#topologytype-remains-unknown-when-an-rsghost-is-discovered), + too. +- If the type is Standalone, run [updateUnknownWithStandalone](#updateunknownwithstandalone). +- If the type is Mongos, set the TopologyType to Sharded. +- If the type is RSPrimary, record its setName and call [updateRSFromPrimary](#updatersfromprimary). +- If the type is RSSecondary, RSArbiter or RSOther, record its setName, set the TopologyType to ReplicaSetNoPrimary, and + call [updateRSWithoutPrimary](#updaterswithoutprimary). + +**TopologyType Sharded** + +A steady state. Connected to one or more mongoses. + +**Actions**: + +- If the server is Unknown or Mongos, keep it. +- Remove others. + +**TopologyType ReplicaSetNoPrimary** + +A starting state. The topology is definitely a replica set, but no primary is known. + +**Actions**: + +- Keep Unknown servers. +- Keep RSGhost servers: they are members of some replica set, perhaps this one, and may recover. (See + [RSGhost and RSOther](#rsghost-and-rsother).) +- Remove any Standalones or Mongoses. +- If the type is RSPrimary call [updateRSFromPrimary](#updatersfromprimary). +- If the type is RSSecondary, RSArbiter or RSOther, run [updateRSWithoutPrimary](#updaterswithoutprimary). + +**TopologyType ReplicaSetWithPrimary** + +A steady state. The primary is known. + +**Actions**: + +- If the server type is Unknown, keep it, and run [checkIfHasPrimary](#checkifhasprimary). +- Keep RSGhost servers: they are members of some replica set, perhaps this one, and may recover. (See + [RSGhost and RSOther](#rsghost-and-rsother).) Run [checkIfHasPrimary](#checkifhasprimary). +- Remove any Standalones or Mongoses and run [checkIfHasPrimary](#checkifhasprimary). +- If the type is RSPrimary run [updateRSFromPrimary](#updatersfromprimary). +- If the type is RSSecondary, RSArbiter or RSOther, run [updateRSWithPrimaryFromMember](#updaterswithprimaryfrommember). + +#### Actions + +##### updateUnknownWithStandalone + +This subroutine is executed with the ServerDescription from Standalone when the TopologyType is Unknown: + +```python +if description.address not in topologyDescription.servers: + return + +if settings.seeds has one seed: + topologyDescription.type = Single +else: + remove this server from topologyDescription and stop monitoring it +``` + +See +[TopologyType remains Unknown when one of the seeds is a Standalone](#topologytype-remains-unknown-when-one-of-the-seeds-is-a-standalone). + + + +##### updateRSWithoutPrimary + +This subroutine is executed with the ServerDescription from an RSSecondary, RSArbiter, or RSOther when the TopologyType +is ReplicaSetNoPrimary: + +```python +if description.address not in topologyDescription.servers: + return + +if topologyDescription.setName is null: + topologyDescription.setName = description.setName + +else if topologyDescription.setName != description.setName: + remove this server from topologyDescription and stop monitoring it + return + +for each address in description's "hosts", "passives", and "arbiters": + if address is not in topologyDescription.servers: + add new default ServerDescription of type "Unknown" + begin monitoring the new server + +if description.primary is not null: + find the ServerDescription in topologyDescription.servers whose + address equals description.primary + + if its type is Unknown, change its type to PossiblePrimary + +if description.address != description.me: + remove this server from topologyDescription and stop monitoring it + return +``` + +Unlike [updateRSFromPrimary](#updatersfromprimary), this subroutine does **not** remove any servers from the +TopologyDescription based on the list of servers in the "hosts" field of the hello or legacy hello response. The only +server that might be removed is the server itself that the hello or legacy hello response is from. + +The special handling of description.primary ensures that a single-threaded client [scans](#scan) the possible primary +before other members. + +See [replica set monitoring with and without a primary](#replica-set-monitoring-with-and-without-a-primary). + +##### updateRSWithPrimaryFromMember + +This subroutine is executed with the ServerDescription from an RSSecondary, RSArbiter, or RSOther when the TopologyType +is ReplicaSetWithPrimary: + +```python +if description.address not in topologyDescription.servers: + # While we were checking this server, another thread heard from the + # primary that this server is not in the replica set. + return + +# SetName is never null here. +if topologyDescription.setName != description.setName: + remove this server from topologyDescription and stop monitoring it + checkIfHasPrimary() + return + +if description.address != description.me: + remove this server from topologyDescription and stop monitoring it + checkIfHasPrimary() + return + +# Had this member been the primary? +if there is no primary in topologyDescription.servers: + topologyDescription.type = ReplicaSetNoPrimary + + if description.primary is not null: + find the ServerDescription in topologyDescription.servers whose + address equals description.primary + + if its type is Unknown, change its type to PossiblePrimary +``` + +The special handling of description.primary ensures that a single-threaded client [scans](#scan) the possible primary +before other members. + + + +##### updateRSFromPrimary + +This subroutine is executed with a ServerDescription of type RSPrimary: + +```python +if serverDescription.address not in topologyDescription.servers: + return + +if topologyDescription.setName is null: + topologyDescription.setName = serverDescription.setName + +else if topologyDescription.setName != serverDescription.setName: + # We found a primary but it doesn't have the setName + # provided by the user or previously discovered. + remove this server from topologyDescription and stop monitoring it + checkIfHasPrimary() + return + +# Election ids are ObjectIds, see +# see "Using electionId and setVersion to detect stale primaries" +# for comparison rules. + +if serverDescription.maxWireVersion >= 17: # MongoDB 6.0+ + # Null values for both electionId and setVersion are always considered less than + if serverDescription.electionId > topologyDescription.maxElectionId or ( + serverDescription.electionId == topologyDescription.maxElectionId + and serverDescription.setVersion >= topologyDescription.maxSetVersion + ): + topologyDescription.maxElectionId = serverDescription.electionId + topologyDescription.maxSetVersion = serverDescription.setVersion + else: + # Stale primary. + # The error field MUST include the substring "primary marked stale due to electionId/setVersion mismatch" + replace serverDescription with a default ServerDescription of type "Unknown" + checkIfHasPrimary() + return +else: + # Maintain old comparison rules, namely setVersion is checked before electionId + if serverDescription.setVersion is not null and serverDescription.electionId is not null: + if ( + topologyDescription.maxSetVersion is not null + and topologyDescription.maxElectionId is not null + and ( + topologyDescription.maxSetVersion > serverDescription.setVersion + or ( + topologyDescription.maxSetVersion == serverDescription.setVersion + and topologyDescription.maxElectionId > serverDescription.electionId + ) + ) + ): + # Stale primary. + # The error field MUST include the substring "primary marked stale due to electionId/setVersion mismatch" + replace serverDescription with a default ServerDescription of type "Unknown" + checkIfHasPrimary() + return + + topologyDescription.maxElectionId = serverDescription.electionId + + if serverDescription.setVersion is not null and ( + topologyDescription.maxSetVersion is null + or serverDescription.setVersion > topologyDescription.maxSetVersion + ): + topologyDescription.maxSetVersion = serverDescription.setVersion + + +for each server in topologyDescription.servers: + if server.address != serverDescription.address: + if server.type is RSPrimary: + # See note below about invalidating an old primary. + # the error field MUST include the substring "primary marked stale due to discovery of newer primary" + replace the server with a default ServerDescription of type "Unknown" + +for each address in serverDescription's "hosts", "passives", and "arbiters": + if address is not in topologyDescription.servers: + add new default ServerDescription of type "Unknown" + begin monitoring the new server + +for each server in topologyDescription.servers: + if server.address not in serverDescription's "hosts", "passives", or "arbiters": + remove the server and stop monitoring it + +checkIfHasPrimary() +``` + +A note on invalidating the old primary: when a new primary is discovered, the client finds the previous primary (there +should be none or one) and replaces its description with a default ServerDescription of type "Unknown". Additionally, +the `error` field of the new `ServerDescription` object MUST include a descriptive error explaining that it was +invalidated because the primary was determined to be stale. A multi-threaded client MUST +[request an immediate check](server-monitoring.md#requesting-an-immediate-check) for that server as soon as possible. + +If the old primary server version is 4.0 or earlier, the client MUST clear its connection pool for the old primary, too: +the connections are all bad because the old primary has closed its sockets. If the old primary server version is 4.2 or +newer, the client MUST NOT clear its connection pool for the old primary. + +See [replica set monitoring with and without a primary](#replica-set-monitoring-with-and-without-a-primary). + +If the server is primary with an obsolete electionId or setVersion, it is likely a stale primary that is going to step +down. Mark it Unknown and let periodic monitoring detect when it becomes secondary. See +[using electionId and setVersion to detect stale primaries](#using-electionid-and-setversion-to-detect-stale-primaries). +Drivers MAY additionally specify whether this was due to an electionId or setVersion mismatch as described in the +[ServerDescripion.error section](#error). + +A note on checking "me": Unlike `updateRSWithPrimaryFromMember`, there is no need to remove the server if the address is +not equal to "me": since the server address will not be a member of either "hosts", "passives", or "arbiters", the +server will already have been removed. + +##### checkIfHasPrimary + +Set TopologyType to ReplicaSetWithPrimary if there is an RSPrimary in TopologyDescription.servers, otherwise set it to +ReplicaSetNoPrimary. + +For example, if the TopologyType is ReplicaSetWithPrimary and the client is processing a new ServerDescription of type +Unknown, that could mean the primary just disconnected, so checkIfHasPrimary must run to check if the TopologyType +should become ReplicaSetNoPrimary. + +Another example is if the client first reaches the primary via its external IP, but the response's host list includes +only internal IPs. In that case the client adds the primary's internal IP to the TopologyDescription and begins +monitoring it, and removes the external IP. Right after removing the external IP from the description, the TopologyType +MUST be ReplicaSetNoPrimary, since no primary is available at this moment. + +##### remove + +Remove the server from TopologyDescription.servers and stop monitoring it. + +In multi-threaded clients, a monitor may be currently checking this server and may not immediately abort. Once the check +completes, this server's hello or legacy hello outcome MUST be ignored, and the monitor SHOULD halt. + +#### Logical Session Timeout + +Whenever a client updates the TopologyDescription from a hello or legacy hello response, it MUST set +TopologyDescription.logicalSessionTimeoutMinutes to the smallest logicalSessionTimeoutMinutes value among +ServerDescriptions of all data-bearing server types. If any have a null logicalSessionTimeoutMinutes, then +TopologyDescription.logicalSessionTimeoutMinutes MUST be set to null. + +See the Driver Sessions Spec for the purpose of this value. + +### Connection Pool Management + +For drivers that support connection pools, after a server check is completed successfully, if the server is determined +to be [data-bearing](server-discovery-and-monitoring.md#data-bearing-server-type) or a +[direct connection](server-discovery-and-monitoring.md#general-requirements) to the server is requested, and does not +already have a connection pool, the driver MUST create the connection pool for the server. Additionally, if a driver +implements a CMAP compliant connection pool, the server's pool (even if it already existed) MUST be marked as "ready". +See the [Server Monitoring spec](server-monitoring.md) for more information. + +Clearing the connection pool for a server MUST be synchronized with the update to the corresponding ServerDescription +(e.g. by holding the lock on the TopologyDescription when clearing the pool). This prevents a possible race between the +monitors and application threads. See +[Why synchronize clearing a server's pool with updating the topology?](#why-synchronize-clearing-a-servers-pool-with-updating-the-topology) +for more information. + +### Error handling + +#### Network error during server check + +See error handling in the [Server Monitoring spec](server-monitoring.md). + +#### Application errors + +When processing a network or command error, clients MUST first check the error's generation number. If the error's +generation number is equal to the pool's generation number then error handling MUST continue according to +[Network error when reading or writing](#network-error-when-reading-or-writing) or +["not writable primary" and "node is recovering"](#not-writable-primary-and-node-is-recovering). Otherwise, the error is +considered stale and the client MUST NOT update any topology state. (See +[Why ignore errors based on CMAP's generation number?](#why-ignore-errors-based-on-cmaps-generation-number)) + +##### Error handling pseudocode + +Application operations can fail in various places, for example: + +- A network error, network timeout, or command error may occur while establishing a new connection. Establishing a + connection includes the MongoDB handshake and completing authentication (if configured). +- A network error or network timeout may occur while reading or writing to an established connection. +- A command error may be returned from the server. +- A "writeConcernError" field may be included in the command response. + +Depending on the context, these errors may update SDAM state by marking the server Unknown and may clear the server's +connection pool. Some errors also require other side effects, like cancelling a check or requesting an immediate check. +Drivers may use the following pseudocode to guide their implementation: + +```python +def handleError(error): + address = error.address + topologyVersion = error.topologyVersion + + with client.lock: + # Ignore stale errors based on generation and topologyVersion. + if isStaleError(client.topologyDescription, error) + return + + if isStateChangeError(error): + # Don't mark server unknown in load balanced mode. + if type != LoadBalanced + # Mark the server Unknown + unknown = new ServerDescription(type=Unknown, error=error, topologyVersion=topologyVersion) + onServerDescriptionChanged(unknown, connection pool for server) + if isShutdown(code) or (error was from <4.2): + # the pools must only be cleared while the lock is held. + if type == LoadBalanced: + clear connection pool for serviceId + else: + clear connection pool for server + if multi-threaded: + request immediate check + else: + # Check right now if this is "not writable primary", since it might be a + # useful secondary. If it's "node is recovering" leave it for the + # next full scan. + if isNotWritablePrimary(error): + check failing server + elif isNetworkError(error) or (not error.completedHandshake): + # Ignore errors that have a backpressure error label applied. + if error.hasLabel("SystemOverloadedError"): + continue + if type != LoadBalanced + # Mark the server Unknown + unknown = new ServerDescription(type=Unknown, error=error) + onServerDescriptionChanged(unknown, connection pool for server) + clear connection pool for server + else + if serviceId + clear connection pool for serviceId + # Cancel inprogress check + cancel monitor check + +def isStaleError(topologyDescription, error): + currentServer = topologyDescription.servers[server.address] + currentGeneration = currentServer.pool.generation + generation = get connection generation from error + if generation < currentGeneration: + # Stale generation number. + return True + + currentTopologyVersion = currentServer.topologyVersion + # True if the current error's topologyVersion is greater than the server's + # We use >= instead of > because any state change should result in a new topologyVersion + return compareTopologyVersion(currentTopologyVersion, error.commandResponse.get("topologyVersion")) >= 0 +``` + +The following pseudocode checks a response for a "not master" or "node is recovering" error: + +```python +recoveringCodes = [11600, 11602, 13436, 189, 91] +notWritablePrimaryCodes = [10107, 13435, 10058] +shutdownCodes = [11600, 91] + +def isRecovering(message, code): + if code: + if code in recoveringCodes: + return true + else: + # if no code, use the error message. + return ("not master or secondary" in message + or "node is recovering" in message) + +def isNotWritablePrimary(message, code): + if code: + if code in notWritablePrimaryCodes: + return true + else: + # if no code, use the error message. + if isRecovering(message, None): + return false + return ("not master" in message) + +def isShutdown(code): + if code and code in shutdownCodes: + return true + return false + +def isStateChangeError(error): + message = error.errmsg + code = error.code + return isRecovering(message, code) or isNotWritablePrimary(message, code) + +def parseGle(response): + if "err" in response: + handleError(CommandError(response, response["err"], response["code"])) + +# Parse response to any command +def parseCommandResponse(response): + if not response["ok"]: + handleError(CommandError(response, response["errmsg"], response["code"])) + else if response["writeConcernError"]: + wce = response["writeConcernError"] + handleError(WriteConcernError(response, wce["errmsg"], wce["code"])) + +def parseQueryResponse(response): + if the "QueryFailure" bit is set in response flags: + handleError(CommandError(response, response["$err"], response["code"])) +``` + +The following sections describe the handling of different classes of application errors in detail including network +errors, network timeout errors, state change errors, and authentication errors. + +##### Network error when reading or writing + +To describe how the client responds to network errors during application operations, we distinguish three phases of +connecting to a server and using it for application operations: + +- *Connection establishment and hello*: the client establishes a new connection to the server and completes an initial + handshake by calling "hello" or legacy hello and reading the response +- *Authentication step*: the client optionally completes an authentication step +- *After the handshake completes*: the client uses the established connection for application operations + +If there is a network error or timeout on the connection establishment or the hello, the client MUST NOT change the +server's description. + +If there is an network error or timeout during the authentication step, the client MUST replace the server's description +with a default ServerDescription of type Unknown when the TopologyType is not LoadBalanced, and fill the +ServerDescription's error field with useful information. + +If there is a network error or timeout on the connection before the handshake completes, and the TopologyType is +LoadBalanced, the client MUST keep the ServerDescription as LoadBalancer. + +If there is a network timeout on the connection after the handshake completes, the client MUST NOT mark the server +Unknown. (A timeout may indicate a slow operation on the server, rather than an unavailable server.) If, however, there +is some other network error on the connection after the handshake completes, the client MUST replace the server's +description with a default ServerDescription of type Unknown if the TopologyType is not LoadBalanced, and fill the +ServerDescription's error field with useful information, the same as if an error or timeout occurred before the +handshake completed. + +When the client marks a server Unknown due to a network error or timeout, the Unknown ServerDescription MUST be sent +through the same process for [updating the TopologyDescription](#updating-the-topologydescription) as if it had been a +failed hello or legacy hello outcome from a server check: for example, if the TopologyType is ReplicaSetWithPrimary and +a write to the RSPrimary server fails because of a network error (other than timeout), then a new ServerDescription is +created for the primary, with type Unknown, and the client executes the proper subroutine for an Unknown server when the +TopologyType is ReplicaSetWithPrimary: referring to the table above we see the subroutine is +[checkIfHasPrimary](#checkifhasprimary). The result is the TopologyType changes to ReplicaSetNoPrimary. See the test +scenario called "Network error writing to primary". + +The client MUST close all idle sockets in its connection pool for the server: if one socket is bad, it is likely that +all are. + +Clients MUST NOT request an immediate check of the server; since application sockets are used frequently, a network +error likely means the server has just become unavailable, so an immediate refresh is likely to get a network error, +too. + +The server will not remain Unknown forever. It will be refreshed by the next periodic check or, if an application +operation needs the server sooner than that, then a re-check will be triggered by the server selection algorithm. + +##### "not writable primary" and "node is recovering" + +These errors are detected from a write command response or query response. Clients MUST check if the server error is a +"node is recovering" error or a "not writable primary" error. + +If the response includes an error code, it MUST be solely used to determine if error is a "node is recovering" or "not +writable primary" error. Clients MUST match the errors by the numeric error code and not by the code name, as the code +name can change from one server version to the next. + +The following error codes indicate a replica set member is temporarily unusable. These are called "node is recovering" +errors: + +| Error Name | Error Code | +| ------------------------------- | ---------- | +| InterruptedAtShutdown | 11600 | +| InterruptedDueToReplStateChange | 11602 | +| NotPrimaryOrSecondary | 13436 | +| PrimarySteppedDown | 189 | +| ShutdownInProgress | 91 | + +And the following error codes indicate a "not writable primary" error: + +| Error Name | Error Code | +| ----------------------- | ---------- | +| NotWritablePrimary | 10107 | +| NotPrimaryNoSecondaryOk | 13435 | +| LegacyNotPrimary | 10058 | + +Clients MUST fallback to checking the error message if and only if the response does not include an error code. The +error is considered a "node is recovering" error if the substrings "node is recovering" or "not master or secondary" are +anywhere in the error message. Otherwise, if the substring "not master" is in the error message it is a "not writable +primary" error. + +Additionally, if the response includes a write concern error, then the code and message of the write concern error MUST +be checked the same way a response error is checked above. + +Errors contained within the writeErrors field MUST NOT be checked. + +See the test scenario called "parsing 'not writable primary' and 'node is recovering' errors" for example response +documents. + +When the client sees a "not writable primary" or "node is recovering" error and the error's +[topologyVersion](#topologyversion) is strictly greater than the current ServerDescription's topologyVersion it MUST +replace the server's description with a ServerDescription of type Unknown. Clients MUST store useful information in the +new ServerDescription's error field, including the error message from the server. Clients MUST store the error's +[topologyVersion](#topologyversion) field in the new ServerDescription if present. (See +[What is the purpose of topologyVersion?](#what-is-the-purpose-of-topologyversion)) + +Multi-threaded and asynchronous clients MUST +[request an immediate check](server-monitoring.md#requesting-an-immediate-check) of the server. Unlike in the "network +error" scenario above, a "not writable primary" or "node is recovering" error means the server is available but the +client is wrong about its type, thus an immediate re-check is likely to provide useful information. + +For single-threaded clients, in the case of a "not writable primary" or "node is shutting down" error, the client MUST +mark the topology as "stale" so the next server selection scans all servers. For a "node is recovering" error, +single-threaded clients MUST NOT mark the topology as "stale". If a node is recovering for some time, an immediate scan +may not gain useful information. + +The following subset of "node is recovering" errors is defined to be "node is shutting down" errors: + +| Error Name | Error Code | +| --------------------- | ---------- | +| InterruptedAtShutdown | 11600 | +| ShutdownInProgress | 91 | + +When handling a "not writable primary" or "node is recovering" error, the client MUST clear the server's connection pool +if and only if the error is "node is shutting down" or the error originated from server version < 4.2. + +(See +[when does a client see "not writable primary" or "node is recovering"?](#when-does-a-client-see-not-writable-primary-or-node-is-recovering), +[use error messages to detect "not master" and "node is recovering"](#use-error-messages-to-detect-not-master-and-node-is-recovering), +and [other transient errors](#other-transient-errors) and +[Why close connections when a node is shutting down?](#why-close-connections-when-a-node-is-shutting-down).) + +##### MongoDB Handshake errors + +If the driver encounters errors that do not have the backpressure error label (`SystemOverloadedError`) applied when +establishing application connections (this includes the initial handshake and authentication), the driver MUST mark the +server Unknown and clear the server's connection pool if the TopologyType is not LoadBalanced. (See +[Why mark a server Unknown after an auth error?](#why-mark-a-server-unknown-after-an-auth-error)) + +### Monitoring SDAM events + +The required driver specification for providing lifecycle hooks into server discovery and monitoring for applications to +consume can be found in the [SDAM Monitoring Specification](server-discovery-and-monitoring-logging-and-monitoring.md). + +### Implementation notes + +This section intends to provide generous guidance to driver authors. It is complementary to the reference +implementations. Words like "should", "may", and so on are used more casually here. + +See also, the implementation notes in the [Server Monitoring spec](server-monitoring.md). + +#### Multi-threaded or asynchronous server selection + +While no suitable server is available for an operation, +[the client MUST re-check all servers every minHeartbeatFrequencyMS](#the-client-must-re-check-all-servers-every-minheartbeatfrequencyms). +(See [requesting an immediate check](server-monitoring.md#requesting-an-immediate-check).) + +#### Single-threaded server selection + +When a client that uses [single-threaded monitoring](server-monitoring.md#single-threaded-monitoring) fails to select a +suitable server for any operation, it [scans](#scan) the servers, then attempts selection again, to see if the scan +discovered suitable servers. It repeats, waiting [minHeartbeatFrequencyMS](#minheartbeatfrequencyms) after each scan, +until a timeout. + +#### Documentation + +##### Giant seed lists + +Drivers' manuals should warn against huge seed lists, since it will slow initialization for single-threaded clients and +generate load for multi-threaded and asynchronous drivers. + +#### Multi-threaded + +#### Warning about the maxWireVersion from a monitor's hello or legacy hello response + +Clients consult some fields from a server's hello or legacy hello response to decide how to communicate with it: + +- maxWireVersion +- maxBsonObjectSize +- maxMessageSizeBytes +- maxWriteBatchSize + +It is tempting to take these values from the last hello or legacy hello response a *monitor* received and store them in +the ServerDescription, but this is an anti-pattern. Multi-threaded and asynchronous clients that do so are prone to +several classes of race, for example: + +- Setup: A MongoDB 3.0 Standalone with authentication enabled, the client must log in with SCRAM-SHA-1. +- The monitor thread discovers the server and stores maxWireVersion on the ServerDescription +- An application thread wants a socket, selects the Standalone, and is about to check the maxWireVersion on its + ServerDescription when... +- The monitor thread gets disconnected from server and marks it Unknown, with default maxWireVersion of 0. +- The application thread resumes, creates a socket, and attempts to log in using MONGODB-CR, since maxWireVersion is + *now* reported as 0. +- Authentication fails, the server requires SCRAM-SHA-1. + +Better to call hello or legacy hello for each new socket, as required by the [Auth Spec](../auth/auth.md), and use the +hello or legacy hello response associated with that socket for maxWireVersion, maxBsonObjectSize, etc.: all the fields +required to correctly communicate with the server. + +The hello or legacy hello responses received by monitors determine if the topology as a whole is compatible with the +driver, and which servers are suitable for selection. The monitors' responses should not be used to determine how to +format wire protocol messages to the servers. + +##### Immutable data + +Multi-threaded drivers should treat ServerDescriptions and TopologyDescriptions as immutable: the client replaces them, +rather than modifying them, in response to new information about the topology. Thus readers of these data structures can +simply acquire a reference to the current one and read it, without holding a lock that would block a monitor from making +further updates. + +##### Process one hello or legacy hello outcome at a time + +Although servers are checked in parallel, the function that actually creates the new TopologyDescription should be +synchronized so only one thread can run it at a time. + +##### Replacing the TopologyDescription + +Drivers may use the following pseudocode to guide their implementation. The client object has a lock and a condition +variable. It uses the lock to ensure that only one new ServerDescription is processed at a time, and it must be acquired +before invoking this function. Once the client has taken the lock it must do no I/O: + +```python +def onServerDescriptionChanged(server, pool): + # "server" is the new ServerDescription. + # "pool" is the pool associated with the server + + if server.address not in client.topologyDescription.servers: + # The server was once in the topologyDescription, otherwise + # we wouldn't have been monitoring it, but an intervening + # state-change removed it. E.g., we got a host list from + # the primary that didn't include this server. + return + + newTopologyDescription = client.topologyDescription.copy() + + # Ignore this update if the current topologyVersion is greater than + # the new ServerDescription's. + if isStaleServerDescription(td, server): + return + + # Replace server's previous description. + address = server.address + newTopologyDescription.servers[address] = server + + # for drivers that implement CMAP, mark the connection pool as ready after a successful check + if (server.type in (Mongos, RSPrimary, RSSecondary, Standalone, LoadBalanced)) + or (server.type != Unknown and newTopologyDescription.type == Single): + pool.ready() + + take any additional actions, + depending on the TopologyType and server... + + # Replace TopologyDescription and notify waiters. + client.topologyDescription = newTopologyDescription + client.condition.notifyAll() + +def compareTopologyVersion(tv1, tv2): + """Return -1 if tv1tv2""" + if tv1 is None or tv2 is None: + # Assume greater. + return -1 + pid1 = tv1['processId'] + pid2 = tv2['processId'] + if pid1 == pid2: + counter1 = tv1['counter'] + counter2 = tv2['counter'] + if counter1 == counter2: + return 0 + elif counter1 < counter2: + return -1 + else: + return 1 + else: + # Assume greater. + return -1 + +def isStaleServerDescription(topologyDescription, server): + # True if the new ServerDescription's topologyVersion is greater than + # or equal to the current server's. + currentServer = topologyDescription.servers[server.address] + currentTopologyVersion = currentServer.topologyVersion + return compareTopologyVersion(currentTopologyVersion, server.topologyVersion) > 0 +``` + +Notifying the condition unblocks threads waiting in the server-selection loop for a suitable server to be discovered. + +> [!NOTE] +> The Java driver uses a CountDownLatch instead of a condition variable, and it atomically swaps the old and new +> CountDownLatches so it does not need "client.lock". It does, however, use a lock to ensure that only one thread runs +> onServerDescriptionChanged at a time. + +## Rationale + +### Clients do no I/O in the constructor + +An alternative proposal was to distinguish between "discovery" and "monitoring". When discovery begins, the client +checks all its seeds, and discovery is complete once all servers have been checked, or after some maximum time. +Application operations cannot proceed until discovery is complete. + +If the discovery phase is distinct, then single- and multi-threaded drivers could accomplish discovery in the +constructor, and throw an exception from the constructor if the deployment is unavailable or misconfigured. This is +consistent with prior behavior for many drivers. It will surprise some users that the constructor now succeeds, but all +operations fail. + +Similarly for misconfigured seed lists: the client may discover a mix of mongoses and standalones, or find multiple +replica set names. It may surprise some users that the constructor succeeds and the client attempts to proceed with a +compatible subset of the deployment. + +Nevertheless, this spec prohibits I/O in the constructor for the following reasons: + +#### Common case + +In the common case, the deployment is available and usable. This spec favors allowing operations to proceed as soon as +possible in the common case, at the cost of surprising behavior in uncommon cases. + +#### Simplicity + +It is simpler to omit a special discovery phase and treat all server [checks](#check) the same. + +#### Consistency + +Asynchronous clients cannot do I/O in a constructor, so it is consistent to prohibit I/O in other clients' constructors +as well. + +#### Restarts + +If clients can be constructed when the deployment is in some states but not in other states, it leads to an unfortunate +scenario: When the deployment is passing through a strange state, long-running clients may keep working, but any clients +restarted during this period fail. + +Say an administrator changes one replica set member's setName. Clients that are already constructed remove the bad +member and stay usable, but if any client is restarted its constructor fails. Web servers that dynamically adjust their +process pools will show particularly undesirable behavior. + +### heartbeatFrequencyMS defaults to 10 seconds or 60 seconds + +Many drivers have different values. The time has come to standardize. Lacking a rigorous methodology for calculating the +best frequency, this spec chooses 10 seconds for multi-threaded or asynchronous drivers because some already use that +value. + +Because scanning has a greater impact on the performance of single-threaded drivers, they MUST default to a longer +frequency (60 seconds). + +An alternative is to check servers less and less frequently the longer they remain unchanged. This idea is rejected +because it is a goal of this spec to answer questions about monitoring such as, + +- "How rapidly can I rotate a replica set to a new set of hosts?" +- "How soon after I add a secondary will query load be rebalanced?" +- "How soon will a client notice a change in round trip time, or tags?" + +Having a constant monitoring frequency allows us to answer these questions simply and definitively. Losing the ability +to answer these questions is not worth any minor gain in efficiency from a more complex scheduling method. + +### The client MUST re-check all servers every minHeartbeatFrequencyMS + +While an application is waiting to do an operation for which there is no suitable server, a multi-threaded client MUST +re-check all servers very frequently. The slight cost is worthwhile in many scenarios. For example: + +1. A client and a MongoDB server are started simultaneously. +2. The client checks the server before it begins listening, so the check fails. +3. The client waits in the server-selection loop for the topology to change. + +In this state, the client should check the server very frequently, to give it ample opportunity to connect to the server +before timing out in server selection. + +### No knobs + +This spec does not intend to introduce any new configuration options unless absolutely necessary. + +### The client MUST monitor arbiters + +Mongos 2.6 does not monitor arbiters, but it costs little to do so, and in the rare case that all data members are moved +to new hosts in a short time, an arbiter may be the client's last hope to find the new replica set configuration. + +### Only support replica set members running MongoDB 1.6.2 or later + +Replica set members began reporting their setNames in that version. Supporting earlier versions is impractical. + +### TopologyType remains Unknown when an RSGhost is discovered + +If the TopologyType is Unknown and the client receives a hello or legacy hello response from +an[RSGhost](#rsghost-and-rsother), the TopologyType could be set to ReplicaSetNoPrimary. However, an RSGhost does not +report its setName, so the setName would still be unknown. This adds an additional state to the existing list: +"TopologyType ReplicaSetNoPrimary **and** no setName." The additional state adds substantial complexity without any +benefit, so this spec says clients MUST NOT change the TopologyType when an RSGhost is discovered. + +### TopologyType remains Unknown when one of the seeds is a Standalone + +If TopologyType is Unknown and there are multiple seeds, and one of them is discovered to be a standalone, it MUST be +removed. The TopologyType remains Unknown. + +This rule supports the following common scenario: + +1. Servers A and B are in a replica set. +2. A seed list with A and B is stored in a configuration file. +3. An administrator removes B from the set and brings it up as standalone for maintenance, without changing its port + number. +4. The client is initialized with seeds A and B, TopologyType Unknown, and no setName. +5. The first hello or legacy hello response is from B, the standalone. + +What if the client changed TopologyType to Single at this point? It would be unable to use the replica set; it would +have to remove A from the TopologyDescription once A's hello or legacy hello response comes. + +The user's intent in this case is clearly to use the replica set, despite the outdated seed list. So this spec requires +clients to remove B from the TopologyDescription and keep the TopologyType as Unknown. Then when A's response arrives, +the client can set its TopologyType to ReplicaSet (with or without primary). + +On the other hand, if there is only one seed and the seed is discovered to be a Standalone, the TopologyType MUST be set +to Single. + +See the "member brought up as standalone" test scenario. + +### Replica set monitoring with and without a primary + +The client strives to fill the "servers" list only with servers that the **primary** said were members of the replica +set, when the client most recently contacted the primary. + +The primary's view of the replica set is authoritative for two reasons: + +1. The primary is never on the minority side of a network partition. During a partition it is the primary's list of + servers the client should use. +2. Since reconfigs must be executed on the primary, the primary is the first to know of them. Reconfigs propagate to + non-primaries eventually, but the client can receive hello or legacy hello responses from non-primaries that + reflect any past state of the replica set. See the "Replica set discovery" test scenario. + +If at any time the client believes there is no primary, the TopologyDescription's type is set to ReplicaSetNoPrimary. +While there is no known primary, the client MUST **add** servers from non-primaries' host lists, but it MUST NOT remove +servers from the TopologyDescription. + +Eventually, when a primary is discovered, any hosts not in the primary's host list are removed. + +### Using electionId and setVersion to detect stale primaries + +Replica set members running MongoDB 2.6.10+ or 3.0+ include an integer called "setVersion" and an ObjectId called +"electionId" in their hello or legacy hello response. Starting with MongoDB 3.2.0, replica sets can use two different +replication protocol versions; electionIds from one protocol version must not be compared to electionIds from a +different protocol. + +Because protocol version changes require replica set reconfiguration, clients use the tuple (electionId, setVersion) to +detect stale primaries. The tuple order comparison MUST be checked in the order of electionId followed by setVersion +since that order of comparison is guaranteed monotonicity. + +The client remembers the greatest electionId and setVersion reported by a primary, and distrusts primaries from older +electionIds or from the same electionId but with lesser setVersion. + +- It compares electionIds as 12-byte sequence i.e. memory comparison. +- It compares setVersions as integer values. + +This prevents the client from oscillating between the old and new primary during a split-brain period, and helps provide +read-your-writes consistency with write concern "majority" and read preference "primary". + +Prior to MongoDB server version 6.0 drivers had the logic opposite from the server side Replica Set Management logic by +ordering the tuple by `setVersion` before the `electionId`. In order to remain compatibility with backup systems, etc. +drivers continue to maintain the reversed logic when connected to a topology that reports a maxWireVersion less than +`17`. Server versions 6.0 and beyond MUST order the tuple by `electionId` then `setVersion`. + +#### Requirements for read-your-writes consistency + +Using (electionId, setVersion) only provides read-your-writes consistency if: + +- The application uses the same MongoClient instance for write-concern "majority" writes and read-preference "primary" + reads, and +- All members use MongoDB 2.6.10+, 3.0.0+ or 3.2.0+ with replication protocol 0 and clocks are *less* than 30 seconds + skewed, or +- All members run MongoDB 3.2.0 and replication protocol 1 and clocks are *less* skewed than the election timeout + (`electionTimeoutMillis`, which defaults to 10 seconds), or +- All members run MongoDB 3.2.1+ and replication protocol 1 (in which case clocks need not be synchronized). + +#### Scenario + +Consider the following situation: + +1. Server A is primary. +2. A network partition isolates A from the set, but the client still sees it. +3. Server B is elected primary. +4. The client discovers that B is primary, does a write-concern "majority" write operation on B and receives + acknowledgment. +5. The client receives a hello or legacy hello response from A, claiming A is still primary. +6. If the client trusts that A is primary, the next read-preference "primary" read sees stale data from A that may *not* + include the write sent to B. + +See [SERVER-17975](https://jira.mongodb.org/browse/SERVER-17975), "Stale reads with WriteConcern Majority and +ReadPreference Primary." + +#### Detecting a stale primary + +To prevent this scenario, the client uses electionId and setVersion to determine which primary was elected last. In this +case, it would not consider "A" a primary, nor read from it because server B will have a greater electionId but the same +setVersion. + +#### Monotonicity + +The electionId is an ObjectId compared bytewise in order. + +(ie. 000000000000000000000001 > 000000000000000000000000, FF0000000000000000000000 > FE0000000000000000000000 etc.) + +In some server versions, it is monotonic with respect to a particular servers' system clock, but is not globally +monotonic across a deployment. However, if inter-server clock skews are small, it can be treated as a monotonic value. + +In MongoDB 2.6.10+ (which has [SERVER-13542](https://jira.mongodb.org/browse/SERVER-13542) backported), MongoDB 3.0.0+ +or MongoDB 3.2+ (under replication protocol version 0), the electionId's leading bytes are a server timestamp. As long +as server clocks are skewed *less* than 30 seconds, electionIds can be reliably compared. (This is precise enough, +because in replication protocol version 0, servers are designed not to complete more than one election every 30 seconds. +Elections do not take 30 seconds--they are typically much faster than that--but there is a 30-second cooldown before the +next election can complete.) + +Beginning in MongoDB 3.2.0, under replication protocol version 1, the electionId begins with a timestamp, but the +cooldown is shorter. As long as inter-server clock skew is *less* than the configured election timeout +(`electionTimeoutMillis`, which defaults to 10 seconds), then electionIds can be reliably compared. + +Beginning in MongoDB 3.2.1, under replication protocol version 1, the electionId is guaranteed monotonic without relying +on any clock synchronization. + +### Using me field to detect seed list members that do not match host names in the replica set configuration + +Removal from the topology of seed list members where the "me" property does not match the address used to connect +prevents clients from being able to select a server, only to fail to re-select that server once the primary has +responded. + +This scenario illustrates the problems that arise if this is NOT done: + +- The client specifies a seed list of A, B, C +- Server A responds as a secondary with hosts D, E, F +- The client executes a query with read preference of secondary, and server A is selected +- Server B responds as a primary with hosts D, E, F. Servers A, B, C are removed, as they don't appear in the primary's + hosts list +- The client iterates the cursor and attempts to execute a getMore against server A. +- Server selection fails because server A is no longer part of the topology. + +With checking for "me" in place, it looks like this instead: + +- The client specifies a seed list of A, B, C +- Server A responds as a secondary with hosts D, E, F, where "me" is D, and so the client adds D, E, F as type "Unknown" + and starts monitoring them, but removes A from the topology. +- The client executes a query with read preference of secondary, and goes into the server selection loop +- Server D responds as a secondary where "me" is D +- Server selection completes by matching D +- The client iterates the cursor and attempts to execute a getMore against server D. +- Server selection completes by matching D. + +### Ignore setVersion unless the server is primary + +It was thought that if all replica set members report a setVersion, and a secondary's response has a higher setVersion +than any seen, that the secondary's host list could be considered as authoritative as the primary's. (See +[Replica set monitoring with and without a primary](#replica-set-monitoring-with-and-without-a-primary).) + +This scenario illustrates the problem with setVersion: + +- We have a replica set with servers A, B, and C. +- Server A is the primary, with setVersion 4. +- An administrator runs replSetReconfig on A, which increments its setVersion to 5. +- The client checks Server A and receives the new config. +- Server A crashes before any secondary receives the new config. +- Server B is elected primary. It has the old setVersion 4. +- The client ignores B's version of the config because its setVersion is not greater than 5. + +The client may never correct its view of the topology. + +Even worse: + +- An administrator runs replSetReconfig on Server B, which increments its setVersion to 5. +- Server A restarts. This results in *two* versions of the config, both claiming to be version 5. + +If the client trusted the setVersion in this scenario, it would trust whichever config it received first. + +mongos 2.6 ignores setVersion and only trusts the primary. This spec requires all clients to ignore setVersion from +non-primaries. + +### Use error messages to detect "not master" and "node is recovering" + +When error codes are not available, error messages are checked for the substrings "not master" and "node is recovering". +This is because older server versions returned unstable error codes or no error codes in many circumstances. + +### Other transient errors + +There are other transient errors a server may return, e.g. retryable errors listed in the retryable writes spec. SDAM +does not consider these because they do not imply the connected server should be marked as "Unknown". For example, the +following errors may be returned from a mongos when it cannot route to a shard: + +| Error Name | Error Code | +| --------------- | ---------- | +| HostNotFound | 7 | +| HostUnreachable | 6 | +| NetworkTimeout | 89 | +| SocketException | 9001 | + +When these are returned, the mongos should *not* be marked as "Unknown", since it is more likely an issue with the +shard. + +### Why ignore errors based on CMAP's generation number? + +Using CMAP's generation number solves the following race condition among application threads and the monitor during +error handling: + +1. Two concurrent writes begin on application threads A and B. +2. The server restarts. +3. Thread A receives the first non-timeout network error, and the client marks the server Unknown, and clears the + server's pool. +4. The client re-checks the server and marks it Primary. +5. Thread B receives the second non-timeout network error and the client marks the server Unknown again. + +The core issue is that the client processes errors in arbitrary order and may overwrite fresh information about the +server's status with stale information. Using CMAP's generation number avoids the race condition because the duplicate +(or stale) network error can be identified (changes in **bold**): + +1. Two concurrent writes begin on application threads A and B, **with generation 1**. +2. The server restarts. +3. Thread A receives the first non-timeout network error, and the client marks the server Unknown, and clears the + server's pool. **The pool's generation is now 2.** +4. The client re-checks the server and marks it Primary. +5. Thread B receives the second non-timeout network error, **and the client ignores the error because the error + originated from a connection with generation 1.** + +### Why synchronize clearing a server's pool with updating the topology? + +Doing so solves the following race condition among application threads and the monitor during error handling, similar to +the previous example: + +1. A write begins on an application thread. +2. The server restarts. +3. The application thread receives a non-timeout network error. +4. The application thread acquires the lock on the TopologyDescription, marks the Server as Unknown, and releases the + lock. +5. The monitor re-checks the server and marks it Primary and its pool as "ready". +6. Several other application threads enter the WaitQueue of the server's pool. +7. The application thread clears the server's pool, evicting all those new threads from the WaitQueue, causing them to + return errors or to retry. Additionally, the pool is now "paused", but the server is considered the Primary, + meaning future operations will be routed to the server and fail until the next heartbeat marks the pool as "ready" + again. + +If marking the server as Unknown and clearing its pool were synchronized, then the monitor marking the server as Primary +after its check would happen after the pool was cleared and thus avoid putting it an inconsistent state. + +### What is the purpose of topologyVersion? + +[topologyVersion](#topologyversion) solves the following race condition among application threads and the monitor when +handling State Change Errors: + +1. Two concurrent writes begin on application threads A and B. +2. The primary steps down. +3. Thread A receives the first State Change Error, the client marks the server Unknown. +4. The client re-checks the server and marks it Secondary. +5. Thread B receives a delayed State Change Error and the client marks the server Unknown again. + +The core issue is that the client processes errors in arbitrary order and may overwrite fresh information about the +server's status with stale information. Using topologyVersion avoids the race condition because the duplicate (or stale) +State Change Errors can be identified (changes in **bold**): + +1. Two concurrent writes begin on application threads A and B. + 1. **The primary's ServerDescription.topologyVersion == tv1** +2. The primary steps down **and sets its topologyVersion to tv2**. +3. Thread A receives the first State Change Error **containing tv2**, the client marks the server Unknown (**with + topologyVersion: tv2**). +4. The client re-checks the server and marks it Secondary (**with topologyVersion: tv2**). +5. Thread B receives a delayed State Change Error (**with topologyVersion: tv2**) **and the client ignores the error + because the error's topologyVersion (tv2) is not greater than the current ServerDescription (tv2).** + +### Why mark a server Unknown after an auth error? + +The [Authentication spec](../auth/auth.md) requires that when authentication fails on a server, the driver MUST clear +the server's connection pool. Clearing the pool without marking the server Unknown would leave the pool in the "paused" +state while the server is still selectable. When auth fails due to invalid credentials, marking the server Unknown also +serves to rate limit new connections; future operations will need to wait for the server to be rediscovered. + +Note that authentication may fail for a variety of reasons, for example: + +- A network error, or network timeout error may occur. +- The server may return a [State Change Error](#state-change-error). +- The server may return a AuthenticationFailed command error (error code 18) indicating that the provided credentials + are invalid. + +Does this mean that authentication failures due to invalid credentials will manifest as server selection timeout errors? +No, authentication errors are still returned to the application immediately. A subsequent operation will block until the +server is rediscovered and immediately attempt authentication on a new connection. + +### Clients use the hostnames listed in the replica set config, not the seed list + +Very often users have DNS aliases they use in their [seed list](#seed-list) instead of the hostnames in the replica set +config. For example, the name "host_alias" might refer to a server also known as "host1", and the URI is: + +```text +mongodb://host_alias/?replicaSet=rs +``` + +When the client connects to "host_alias", its hello or legacy hello response includes the list of hostnames from the +replica set config, which does not include the seed: + +```javascript +{ + hosts: ["host1:27017", "host2:27017"], + setName: "rs", + ... other hello or legacy hello response fields ... +} +``` + +This spec requires clients to connect to the hostnames listed in the hello or legacy hello response. Furthermore, if the +response is from a primary, the client MUST remove all hostnames not listed. In this case, the client disconnects from +"host_alias" and tries "host1" and "host2". (See [updateRSFromPrimary](#updatersfromprimary).) + +Thus, replica set members must be reachable from the client by the hostnames listed in the replica set config. + +An alternative proposal is for clients to continue using the hostnames in the seed list. It could add new hosts from the +hello or legacy hello response, and where a host is known by two names, the client can deduplicate them using the "me" +field and prefer the name in the seed list. + +This proposal was rejected because it does not support key features of replica sets: failover and zero-downtime +reconfiguration. + +In our example, if "host1" and "host2" are not reachable from the client, the client continues to use "host_alias" only. +If that server goes down or is removed by a replica set reconfig, the client is suddenly unable to reach the replica set +at all: by allowing the client to use the alias, we have hidden the fact that the replica set's failover feature will +not work in a crisis or during a reconfig. + +In conclusion, to support key features of replica sets, we require that the hostnames used in a replica set config are +reachable from the client. + +## Backwards Compatibility + +The Java driver 2.12.1 has a "heartbeatConnectRetryFrequency". Since this spec recommends the option be named +"minHeartbeatFrequencyMS", the Java driver must deprecate its old option and rename it minHeartbeatFrequency (for +consistency with its other options which also lack the "MS" suffix). + +## Reference Implementation + +- Java driver 3.x +- PyMongo 3.x +- Perl driver 1.0.0 (in progress) + +## Future Work + +MongoDB is likely to add some of the following features, which will require updates to this spec: + +- Eventually consistent collections (SERVER-2956) +- Mongos discovery (SERVER-1834) +- Put individual databases into maintenance mode, instead of the whole server (SERVER-7826) +- Put setVersion in write-command responses (SERVER-13909) + +## Questions and Answers + +### When does a client see "not writable primary" or "node is recovering"? + +These errors indicate one of these: + +- A write was attempted on an unwritable server (arbiter, secondary, ghost, or recovering). +- A read was attempted on an unreadable server (arbiter, ghost, or recovering) or a read was attempted on a read-only + server without the secondaryOk bit set. +- An operation was attempted on a server that is now shutting down. + +In any case the error is a symptom that a ServerDescription's type no longer reflects reality. + +On MongoDB 4.0 and earlier, a primary closes its connections when it steps down, so in many cases the next operation +causes a network error rather than "not writable primary". The driver can see a "not writable primary" error in the +following scenario: + +1. The client discovers the primary. +2. The primary steps down. +3. Before the client checks the server and discovers the stepdown, the application attempts an operation. +4. The client's connection pool is empty, either because it has never attempted an operation on this server, or because + all connections are in use by other threads. +5. The client creates a connection to the old primary. +6. The client attempts to write, or to read without the secondaryOk bit, and receives "not writable primary". + +See ["not writable primary" and "node is recovering"](#not-writable-primary-and-node-is-recovering), and the test +scenario called "parsing 'not writable primary' and 'node is recovering' errors". + +### Why close connections when a node is shutting down? + +When a server shuts down, it will return one of the "node is shutting down" errors for each attempted operation and +eventually will close all connections. Keeping a connection to a server which is shutting down open would only produce +errors on this connection - such a connection will never be usable for any operations. In contrast, when a server 4.2 or +later returns "not writable primary" error the connection may be usable for other operations (such as secondary reads). + +### What's the point of periodic monitoring? + +Why not just wait until a "not writable primary" error or "node is recovering" error informs the client that its +TopologyDescription is wrong? Or wait until server selection fails to find a suitable server, and only scan all servers +then? + +Periodic monitoring accomplishes three objectives: + +- Update each server's type, tags, and [round trip time](#round-trip-time). Read preferences and the mongos selection + algorithm require this information remains up to date. +- Discover new secondaries so that secondary reads are evenly spread. +- Detect incremental changes to the replica set configuration, so that the client remains connected to the set even + while it is migrated to a completely new set of hosts. + +If the application uses some servers very infrequently, monitoring can also proactively detect state changes (primary +stepdown, server becoming unavailable) that would otherwise cause future errors. + +### Why is auto-discovery the preferred default? + +Auto-discovery is most resilient and is therefore preferred. + +### Why is it possible for maxSetVersion to go down? + +`maxElectionId` and `maxSetVersion` are actually considered a pair of values Drivers MAY consider implementing +comparison in code as a tuple of the two to ensure their always updated together: + +```typescript +// New tuple old tuple +{ electionId: 2, setVersion: 1 } > { electionId: 1, setVersion: 50 } +``` + +In this scenario, the maxSetVersion goes from 50 to 1, but the maxElectionId is raised to 2. + +## Acknowledgments + +Jeff Yemin's code for the Java driver 2.12, and his patient explanation thereof, is the major inspiration for this spec. +Mathias Stearn's beautiful design for replica set monitoring in mongos 2.6 contributed as well. Bernie Hackett gently +oversaw the specification process. + +## Changelog + +- 2015-12-17: Require clients to compare (setVersion, electionId) tuples. + +- 2015-10-09: Specify electionID comparison method. + +- 2015-06-16: Added cooldownMS. + +- 2016-05-04: Added link to SDAM monitoring. + +- 2016-07-18: Replace mentions of the "Read Preferences Spec" with "Server Selection Spec", and + "secondaryAcceptableLatencyMS" with "localThresholdMS". + +- 2016-07-21: Updated for Max Staleness support. + +- 2016-08-04: Explain better why clients use the hostnames in RS config, not URI. + +- 2016-08-31: Multi-threaded clients SHOULD use hello or legacy hello replies to update the topology when they handshake + application connections. + +- 2016-10-06: In updateRSWithoutPrimary the hello or legacy hello response's "primary" field should be used to update + the topology description, even if address != me. + +- 2016-10-29: Allow for idleWritePeriodMS to change someday. + +- 2016-11-01: "Unknown" is no longer the default TopologyType, the default is now explicitly unspecified. Update + instructions for setting the initial TopologyType when running the spec tests. + +- 2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the future. + +- 2017-02-28: Update "network error when reading or writing": timeout while connecting does mark a server Unknown, + unlike a timeout while reading or writing. Justify the different behaviors, and also remove obsolete reference to + auto-retry. + +- 2017-06-13: Move socketCheckIntervalMS to Server Selection Spec. + +- 2017-08-01: Parse logicalSessionTimeoutMinutes from hello or legacy hello reply. + +- 2017-08-11: Clearer specification of "incompatible" logic. + +- 2017-09-01: Improved incompatibility error messages. + +- 2018-03-28: Specify that monitoring must not do mechanism negotiation or authentication. + +- 2019-05-29: Renamed InterruptedDueToStepDown to InterruptedDueToReplStateChange + +- 2020-02-13: Drivers must run SDAM flow even when server description is equal to the last one. + +- 2020-03-31: Add topologyVersion to ServerDescription. Add rules for ignoring stale application errors. + +- 2020-05-07: Include error field in ServerDescription equality comparison. + +- 2020-06-08: Clarify reasoning behind how SDAM determines if a topologyVersion is stale. + +- 2020-12-17: Mark the pool for a server as "ready" after performing a successful check. Synchronize pool clearing with + SDAM updates. + +- 2021-01-17: Require clients to compare (electionId, setVersion) tuples. + +- 2021-02-11: Errors encountered during auth are handled by SDAM. Auth errors mark the server Unknown and clear the + pool. + +- 2021-04-12: Adding in behaviour for load balancer mode. + +- 2021-05-03: Require parsing "isWritablePrimary" field in responses. + +- 2021-06-09: Connection pools must be created and eventually marked ready for any server if a direct connection is + used. + +- 2021-06-29: Updated to use modern terminology. + +- 2022-01-19: Add iscryptd and 90th percentile RTT fields to ServerDescription. + +- 2022-07-11: Convert integration tests to the unified format. + +- 2022-09-30: Update `updateRSFromPrimary` to include logic before and after 6.0 servers + +- 2022-10-05: Remove spec front matter, move footnote, and reformat changelog. + +- 2022-11-17: Add minimum RTT tracking and remove 90th percentile RTT. + +- 2024-01-17: Add section on expected client close behaviour + +- 2024-05-08: Migrated from reStructuredText to Markdown. + +- 2024-08-09: Updated wire versions in tests to 4.0+. + +- 2024-08-16: Updated host b wire versions in `too_new` and `too_old` tests + +- 2024-11-04: Make the description of `TopologyDescription.servers` consistent with the spec tests. + +- 2024-11-11: Removed references to `getLastError` + +- 2025-01-22: Add error messages when a new primary is elected or a primary with a stale electionId or setVersion is + discovered. + +- 2025-11-21: Add handling of backpressure error labels. + +______________________________________________________________________ + +[^1]: "localThresholdMS" was called "secondaryAcceptableLatencyMS" in the Read Preferences Spec, before it was superseded + by the Server Selection Spec. + +[^2]: [TopologyType remains Unknown when an RSGhost is discovered](#topologytype-remains-unknown-when-an-rsghost-is-discovered). diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst index d9cdc8cec2..ddd00719ec 100644 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst @@ -1,2348 +1,4 @@ -=============================== -Server Discovery And Monitoring -=============================== - -:Spec: 101 -:Title: Server Discovery And Monitoring -:Author: A\. Jesse Jiryu Davis -:Advisors: David Golden, Craig Wilson -:Status: Accepted -:Type: Standards -:Version: 2.12 -:Last Modified: March 28, 2018 - -.. contents:: - --------- - -Abstract --------- - -This spec defines how a MongoDB client discovers and monitors one or more servers. -It covers monitoring a single server, a set of mongoses, or a replica set. -How does the client determine what type of servers they are? -How does it keep this information up to date? -How does the client find an entire replica set from a seed list, -and how does it respond to a stepdown, election, reconfiguration, or network error? - -All drivers must answer these questions the same. -Or, where platforms' limitations require differences among drivers, -there must be as few answers as possible and each must be clearly explained in this spec. -Even in cases where several answers seem equally good, drivers must agree on one way to do it. - -MongoDB users and driver authors benefit from having one way to discover and monitor servers. -Users can substantially understand their driver's behavior without inspecting its code or asking its author. -Driver authors can avoid subtle mistakes -when they take advantage of a design that has been well-considered, reviewed, and tested. - -The server discovery and monitoring method is specified in four sections. -First, a client is `configured`_. -Second, it begins `monitoring`_ by calling ismaster on all servers. -(Multi-threaded and asynchronous monitoring is described first, -then single-threaded monitoring.) -Third, as ismaster calls are received -the client `parses them`_, -and fourth, it `updates its view of the topology`_. - -Finally, this spec describes how `drivers update their topology view -in response to errors`_, -and includes generous implementation notes for driver authors. - -This spec does not describe how a client chooses a server for an operation; -that is the domain of the Server Selection Spec. -But there is a section describing -the `interaction between monitoring and server selection`_. - -There is no discussion of driver architecture and data structures, -nor is there any specification of a user-facing API. -This spec is only concerned with the algorithm for monitoring the server topology. - -Meta ----- - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL -NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and -"OPTIONAL" in this document are to be interpreted as described in -`RFC 2119`_. - -.. _RFC 2119: https://www.ietf.org/rfc/rfc2119.txt - -Specification -------------- - -General Requirements -'''''''''''''''''''' - -**Direct connections:** -A client MUST be able to connect to a single server of any type. -This includes querying hidden replica set members, -and connecting to uninitialized members (see `RSGhost`_) in order to run -"replSetInitiate". -Setting a read preference MUST NOT be necessary to connect to a secondary. -Of course, -the secondary will reject all operations done with the PRIMARY read preference -because the slaveOk bit is not set, -but the initial connection itself succeeds. -Drivers MAY allow direct connections to arbiters -(for example, to run administrative commands). - -**Replica sets:** -A client MUST be able to discover an entire replica set from -a seed list containing one or more replica set members. -It MUST be able to continue monitoring the replica set -even when some members go down, -or when reconfigs add and remove members. -A client MUST be able to connect to a replica set -while there is no primary, or the primary is down. - -**Mongos:** -A client MUST be able to connect to a set of mongoses -and monitor their availability and `round trip time`_. -This spec defines how mongoses are discovered and monitored, -but does not define which mongos is selected for a given operation. - -**Master-slave:** -A client MUST be able to directly connect to a mongod begun with "--slave". -No additional master-slave features are described in this spec. - -Terms -''''' - -Server -`````` - -A mongod or mongos process. - -Deployment -`````````` - -One or more servers: -either a standalone, a replica set, or one or more mongoses. - -Topology -```````` - -The state of the deployment: -its type (standalone, replica set, or sharded), -which servers are up, what type of servers they are, -which is primary, and so on. - -Client -`````` - -Driver code responsible for connecting to MongoDB. - -Seed list -````````` - -Server addresses provided to the client in its initial configuration, -for example from the `connection string`_. - -Data-Bearing Server Type -```````````````````````` - -A server type from which a client can receive application data: - -* Mongos -* RSPrimary -* RSSecondary -* Standalone - -Round trip time -``````````````` - -Also known as RTT. - -The client's measurement of the duration of one ismaster call. -The round trip time is used to support the "localThresholdMS" [1]_ -option in the Server Selection Spec. - -ismaster outcome -```````````````` - -The result of an attempt to call the "ismaster" command on a server. -It consists of three elements: -a boolean indicating the success or failure of the attempt, -a document containing the command response (or null if it failed), -and the round trip time to execute the command (or null if it failed). - -.. _checks: -.. _checking: -.. _checked: - -check -````` - -The client checks a server by attempting to call ismaster on it, -and recording the outcome. - -.. _scans: -.. _rescan: - -scan -```` - -The process of checking all servers in the deployment. - -suitable -```````` - -A server is judged "suitable" for an operation if the client can use it -for a particular operation. -For example, a write requires a standalone -(or the master of a master-slave set), -primary, or mongos. -Suitability is fully specified in the `Server Selection Spec -`_. - -address -``````` - -The hostname or IP address, and port number, of a MongoDB server. - -network error -````````````` - -An error that occurs while reading from or writing to a network socket. - -network timeout -``````````````` - -A timeout that occurs while reading from or writing to a network socket. - -Data structures -''''''''''''''' - -This spec uses a few data structures -to describe the client's view of the topology. -It must be emphasized that -a driver is free to implement the same behavior -using different data structures. -This spec uses these enums and structs in order to describe driver **behavior**, -not to mandate how a driver represents the topology, -nor to mandate an API. - -Constants -````````` - -clientMinWireVersion and clientMaxWireVersion -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Integers. The wire protocol range supported by the client. - -Enums -````` - -TopologyType -~~~~~~~~~~~~ - -Single, ReplicaSetNoPrimary, ReplicaSetWithPrimary, Sharded, or Unknown. - -See `updating the TopologyDescription`_. - -ServerType -~~~~~~~~~~ - -Standalone, Mongos, -PossiblePrimary, RSPrimary, RSSecondary, RSArbiter, RSOther, RSGhost, -or Unknown. - -See `parsing an ismaster response`_. - -.. note:: Single-threaded clients use the PossiblePrimary type - to maintain proper `scanning order`_. - Multi-threaded and asynchronous clients do not need this ServerType; - it is synonymous with Unknown. - -TopologyDescription -``````````````````` - -The client's representation of everything it knows about the deployment's topology. - -Fields: - -* type: a `TopologyType`_ enum value. See `initial TopologyType`_. -* setName: the replica set name. Default null. -* maxSetVersion: an integer or null. The largest setVersion ever reported by - a primary. Default null. -* maxElectionId: an ObjectId or null. The largest electionId ever reported by - a primary. Default null. -* servers: a set of ServerDescription instances. - Default contains one server: "localhost:27017", ServerType Unknown. -* stale: a boolean for single-threaded clients, whether the topology must - be re-scanned. - (Not related to maxStalenessSeconds, nor to `stale primaries`_.) -* compatible: a boolean. - False if any server's wire protocol version range - is incompatible with the client's. - Default true. -* compatibilityError: a string. - The error message if "compatible" is false, otherwise null. -* logicalSessionTimeoutMinutes: integer or null. Default null. See - `logical session timeout`_. - -ServerDescription -````````````````` - -The client's view of a single server, -based on the most recent `ismaster outcome`_. - -Again, drivers may store this information however they choose; -this data structure is defined here -merely to describe the monitoring algorithm. - -Fields: - -* address: the hostname or IP, and the port number, - that the client connects to. - Note that this is **not** the server's ismaster.me field, - in the case that the server reports an address different - from the address the client uses. -* error: information about the last error related to this server. Default null. -* roundTripTime: the duration of the ismaster call. Default null. -* lastWriteDate: a 64-bit BSON datetime or null. - The "lastWriteDate" from the server's most recent ismaster response. -* opTime: an ObjectId or null. - The last opTime reported by the server; an ObjectId or null. - (Only mongos and shard servers record this field when monitoring - config servers as replica sets.) -* type: a `ServerType`_ enum value. Default Unknown. -* minWireVersion, maxWireVersion: - the wire protocol version range supported by the server. - Both default to 0. - `Use min and maxWireVersion only to determine compatibility`_. -* me: The hostname or IP, and the port number, that this server was configured with in the replica set. Default null. -* hosts, passives, arbiters: Sets of addresses. - This server's opinion of the replica set's members, if any. - These `hostnames are normalized to lower-case`_. - Default empty. - The client `monitors all three types of servers`_ in a replica set. -* tags: map from string to string. Default empty. -* setName: string or null. Default null. -* setVersion: integer or null. Default null. -* electionId: an ObjectId, if this is a MongoDB 2.6+ replica set member that - believes it is primary. See `using setVersion and electionId to detect stale primaries`_. - Default null. -* primary: an address. This server's opinion of who the primary is. - Default null. -* lastUpdateTime: when this server was last checked. Default "infinity ago". -* logicalSessionTimeoutMinutes: integer or null. Default null. - -"Passives" are priority-zero replica set members that cannot become primary. -The client treats them precisely the same as other members. - -.. _configured: - -Configuration -''''''''''''' - -No breaking changes -``````````````````` - -This spec does not intend -to require any drivers to make breaking changes regarding -what configuration options are available, -how options are named, -or what combinations of options are allowed. - -Initial TopologyDescription -``````````````````````````` - -The default values for `TopologyDescription`_ fields are described above. -Users may override the defaults as follows: - -Initial Servers -~~~~~~~~~~~~~~~ - -The user MUST be able to set the initial servers list to a `seed list`_ -of one or more addresses. - -The hostname portion of each address MUST be normalized to lower-case. - -Initial TopologyType -~~~~~~~~~~~~~~~~~~~~ - -The user MUST be able to set the initial TopologyType to Single. - -The user MAY be able to initialize it to ReplicaSetNoPrimary. -This provides the user a way to tell the client -it can only connect to replica set members. -Similarly the user MAY be able to initialize it to Sharded, -to connect only to mongoses. - -The user MAY be able to initialize it to Unknown, to allow for discovery of any -topology type based only on ismaster responses. - -The API for initializing TopologyType is not specified here. -Drivers might already have a convention, e.g. a single seed means Single, -a setName means ReplicaSetNoPrimary, -and a list of seeds means Unknown. -There are variations, however: -In the Java driver a single seed means Single, -but a **list** containing one seed means Unknown, -so it can transition to replica-set monitoring if the seed is discovered -to be a replica set member. -In contrast, PyMongo requires a non-null setName -in order to begin replica-set monitoring, -regardless of the number of seeds. -This spec does not imply existing driver APIs must change -as long as all the required features are somehow supported. - -Initial setName -~~~~~~~~~~~~~~~ - -The user MUST be able to set the client's initial replica set name. -A driver MAY require the set name in order to connect to a replica set, -or it MAY be able to discover the replica set name as it connects. - -Allowed configuration combinations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MUST enforce: - -* TopologyType Single cannot be used with multiple seeds. -* If setName is not null, only TopologyType ReplicaSetNoPrimary, - and possibly Single, - are allowed. - (See `verifying setName with TopologyType Single`_.) - -heartbeatFrequencyMS -```````````````````` - -The interval between server `checks`_, counted from the end of the previous -check until the beginning of the next one. - -For multi-threaded and asynchronous drivers -it MUST default to 10 seconds and MUST be configurable. -For single-threaded drivers it MUST default to 60 seconds -and MUST be configurable. -It MUST be called heartbeatFrequencyMS -unless this breaks backwards compatibility. - -For both multi- and single-threaded drivers, -the driver MUST NOT permit users to configure it less than minHeartbeatFrequencyMS (500ms). - -(See `heartbeatFrequencyMS defaults to 10 seconds or 60 seconds`_ -and `what's the point of periodic monitoring?`_) - -Client construction -''''''''''''''''''' - -The client's constructor MUST NOT do any I/O. -This means that the constructor does not throw an exception -if servers are unavailable: -the topology is not yet known when the constructor returns. -Similarly if a server has an incompatible wire protocol version, -the constructor does not throw. -Instead, all subsequent operations on the client fail -as long as the error persists. - -See `clients do no I/O in the constructor`_ for the justification. - -Multi-threaded and asynchronous client construction -``````````````````````````````````````````````````` - -The constructor MAY start the monitors as background tasks -and return immediately. -Or the monitors MAY be started by some method separate from the constructor; -for example they MAY be started by some "initialize" method (by any name), -or on the first use of the client for an operation. - -Single-threaded client construction -``````````````````````````````````` - -Single-threaded clients do no I/O in the constructor. -They MUST `scan`_ the servers on demand, -when the first operation is attempted. - -Monitoring -'''''''''' - -The client monitors servers by `checking`_ them periodically, -pausing `heartbeatFrequencyMS`_ between checks. -Clients check servers sooner in response to certain events. - -The socket used to check a server MUST use the same -`connectTimeoutMS `_ -as regular sockets. -Multi-threaded clients SHOULD set monitoring sockets' socketTimeoutMS to the -connectTimeoutMS. -(See `socket timeout for monitoring is connectTimeoutMS`_. -Drivers MAY let users configure the timeouts for monitoring sockets -separately if necessary to preserve backwards compatibility.) - -The client begins monitoring a server when: - -* ... the client is initialized and begins monitoring each seed. - See `initial servers`_. -* ... `updateRSWithoutPrimary`_ or `updateRSFromPrimary`_ - discovers new replica set members. - -When checking a server, clients MUST NOT include SCRAM mechanism -negotiation requests with the ``isMaster`` command, as doing so would -make monitoring checks more expensive for the server. - -The following subsections specify how monitoring works, -first in multi-threaded or asynchronous clients, -and second in single-threaded clients. -This spec provides detailed requirements for monitoring -because it intends to make all drivers behave consistently. - -.. _mt-monitoring: - -Multi-threaded or asynchronous monitoring -````````````````````````````````````````` - -(See also: `implementation notes for multi-threaded clients`_.) - -Servers are monitored in parallel -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All servers' monitors run independently, in parallel: -If some monitors block calling ismaster over slow connections, -other monitors MUST proceed unimpeded. - -The natural implementation is a thread per server, -but the decision is left to the implementer. -(See `thread per server`_.) - -Servers are monitored with dedicated sockets -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`A monitor SHOULD NOT use the client's regular connection pool`_ -to acquire a socket; -it uses a dedicated socket that does not count toward the pool's -maximum size. - -Drivers MUST NOT authenticate on sockets used for monitoring. - -Servers are checked periodically -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Each monitor `checks`_ its server and notifies the client of the outcome -so the client can update the TopologyDescription. - -After each check, the next check SHOULD be scheduled `heartbeatFrequencyMS`_ later; -a check MUST NOT run while a previous check is still in progress. - -.. _request an immediate check: - -Requesting an immediate check -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -At any time, the client can request that a monitor check its server immediately. -(For example, after a "not master" error. See `error handling`_.) -If the monitor is sleeping when this request arrives, -it MUST wake and check as soon as possible. -If an ismaster call is already in progress, -the request MUST be ignored. -If the previous check ended less than `minHeartbeatFrequencyMS`_ ago, -the monitor MUST sleep until the minimum delay has passed, -then check the server. - -Application operations are unblocked when a server is found -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Each time a check completes, threads waiting for a `suitable`_ server -are unblocked. Each unblocked thread MUST proceed if the new TopologyDescription -now contains a suitable server. - -As an optimization, the client MAY leave threads blocked -if a check completes without detecting any change besides -`round trip time`_: no operation that was blocked will -be able to proceed anyway. - -Clients update the topology from each handshake -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a client successfully calls ismaster to handshake a new connection for application -operations, it SHOULD use the ismaster reply to update the ServerDescription -and TopologyDescription, the same as with an ismaster reply on a monitoring -socket. If the ismaster call fails, the client SHOULD mark the server Unknown -and update its TopologyDescription, the same as a failed server check on -monitoring socket. - -.. _st-monitoring: - -Single-threaded monitoring -`````````````````````````` - -cooldownMS -~~~~~~~~~~ - -After a single-threaded client gets a network error trying to `check`_ a -server, the client skips re-checking the server until cooldownMS has passed. - -This avoids spending connectTimeoutMS on each unavailable server -during each scan. - -This value MUST be 5000 ms, and it MUST NOT be configurable. - -Scanning -~~~~~~~~ - -Single-threaded clients MUST `scan`_ all servers synchronously, -inline with regular application operations. -Before each operation, the client checks if `heartbeatFrequencyMS`_ has -passed since the previous scan ended, or if the topology is marked "stale"; -if so it scans all the servers before -selecting a server and performing the operation. - -Selection failure triggers an immediate scan, see -`single-threaded server selection`_. - -Scanning order -~~~~~~~~~~~~~~ - -If the topology is a replica set, -the client attempts to contact the primary as soon as possible -to get an authoritative list of members. -Otherwise, the client attempts to check all members it knows of, -in order from the least-recently to the most-recently checked. - -When all servers have been checked the scan is complete. -New servers discovered **during** the scan -MUST be checked before the scan is complete. -Sometimes servers are removed during a scan -so they are not checked, depending on the order of events. - -The scanning order is expressed in this pseudocode:: - - scanStartTime = now() - # You'll likely need to convert units here. - beforeCoolDown = scanStartTime - cooldownMS - - while true: - serversToCheck = all servers with lastUpdateTime before scanStartTime - - remove from serversToCheck any Unknowns with lastUpdateTime > beforeCoolDown - - if no serversToCheck: - # This scan has completed. - break - - if a server in serversToCheck is RSPrimary: - check it - else if there is a PossiblePrimary: - check it - else if any servers are not of type Unknown or RSGhost: - check the one with the oldest lastUpdateTime - if several servers have the same lastUpdateTime, choose one at random - else: - check the Unknown or RSGhost server with the oldest lastUpdateTime - if several servers have the same lastUpdateTime, choose one at random - -This algorithm might be better understood with an example: - -#. The client is configured with one seed and TopologyType Unknown. - It begins a scan. -#. When it checks the seed, it discovers a secondary. -#. The secondary's ismaster response includes the "primary" field - with the address of the server that the secondary thinks is primary. -#. The client creates a ServerDescription with that address, - type PossiblePrimary, and lastUpdateTime "infinity ago". - (See `updateRSWithoutPrimary`_.) -#. On the next iteration, there is still no RSPrimary, - so the new PossiblePrimary is the top-priority server to check. -#. The PossiblePrimary is checked and replaced with an RSPrimary. - The client has now acquired an authoritative host list. - Any new hosts in the list are added to the TopologyDescription - with lastUpdateTime "infinity ago". - (See `updateRSFromPrimary`_.) -#. The client continues scanning until all known hosts have been checked. - -Another common case might be scanning a pool of mongoses. -When the client first scans its seed list, -they all have the default lastUpdateTime "infinity ago", -so it scans them in random order. -This randomness provides some load-balancing if many clients start at once. -A client's subsequent scans of the mongoses -are always in the same order, -since their lastUpdateTimes are always in the same order -by the time a scan ends. - -minHeartbeatFrequencyMS -``````````````````````` - -If a client frequently rechecks a server, -it MUST wait at least minHeartbeatFrequencyMS milliseconds -since the previous check ended, to avoid pointless effort. -This value MUST be 500 ms, and it MUST NOT be configurable. -(See `no knobs`_.) - -.. _parses them: - -Parsing an ismaster response -'''''''''''''''''''''''''''' - -The client represents its view of each server with a `ServerDescription`_. -Each time the client `checks`_ a server, -it replaces its description of that server with a new one. - -ServerDescriptions are created from ismaster outcomes as follows: - -type -```` - -The new ServerDescription's type field is set to a `ServerType`_. -Note that these states do **not** exactly correspond to -`replica set member states -`_. -For example, some replica set member states like STARTUP and RECOVERING -are identical from the client's perspective, so they are merged into "RSOther". -Additionally, states like Standalone and Mongos -are not replica set member states at all. - -+-------------------+---------------------------------------------------------------+ -| State | Symptoms | -+===================+===============================================================+ -| Unknown | Initial, or after a network error or failed ismaster call, | -| | or "ok: 1" not in ismaster response. | -+-------------------+---------------------------------------------------------------+ -| Standalone | No "msg: isdbgrid", no setName, and no "isreplicaset: true". | -+-------------------+---------------------------------------------------------------+ -| Mongos | "msg: isdbgrid". | -+-------------------+---------------------------------------------------------------+ -| PossiblePrimary | Not yet checked, but another member thinks it is the primary. | -+-------------------+---------------------------------------------------------------+ -| RSPrimary | "ismaster: true", "setName" in response. | -+-------------------+---------------------------------------------------------------+ -| RSSecondary | "secondary: true", "setName" in response. | -+-------------------+---------------------------------------------------------------+ -| RSArbiter | "arbiterOnly: true", "setName" in response. | -+-------------------+---------------------------------------------------------------+ -| RSOther | "setName" in response, "hidden: true" or not primary, | -| | secondary, nor arbiter. | -+-------------------+---------------------------------------------------------------+ -| RSGhost | "isreplicaset: true" in response. | -+-------------------+---------------------------------------------------------------+ - -A server can transition from any state to any other. -For example, an administrator could shut down a secondary -and bring up a mongos in its place. - -.. _RSGhost: - -RSGhost and RSOther -~~~~~~~~~~~~~~~~~~~ - -The client MUST monitor replica set members -even when they cannot be queried. -These members are in state RSGhost or RSOther. - -**RSGhost** members occur in at least three situations: - -* briefly during server startup, -* in an uninitialized replica set, -* or when the server is shunned (removed from the replica set config). - -An RSGhost server has no hosts list nor setName. -Therefore the client MUST NOT attempt to use its hosts list -nor check its setName -(see `JAVA-1161 `_ -or `CSHARP-671 `_.) -However, the client MUST keep the RSGhost member in its TopologyDescription, -in case the client's only hope for staying connected to the replica set -is that this member will transition to a more useful state. - -RSGhosts may report their setNames in the future -(see `SERVER-13458 `_). -For simplicity, this is the rule: -any server is an RSGhost that reports "isreplicaset: true". - -Non-ghost replica set members have reported their setNames -since MongoDB 1.6.2. -See `only support replica set members running MongoDB 1.6.2 or later`_. - -.. note:: The Java driver does not have a separate state for RSGhost; - it is an RSOther server with no hosts list. - -**RSOther** servers may be hidden, starting up, or recovering. -They cannot be queried, but their hosts lists are useful -for discovering the current replica set configuration. - -If a `hidden member `_ -is provided as a seed, -the client can use it to find the primary. -Since the hidden member does not appear in the primary's host list, -it will be removed once the primary is checked. - -error -````` - -If the client experiences any error when checking a server, -it stores error information in the ServerDescription's error field. - -roundTripTime -````````````` - -Drivers MUST record the server's `round trip time`_ (RTT) -after each successful call to ismaster. The Server Selection Spec describes how -RTT is averaged and how it is used in server selection. - -If an ismaster call fails, the RTT is not updated. -Furthermore, while a server's type is Unknown its RTT is null, -and if it changes from a known type to Unknown its RTT is set to null. -However, if it changes from one known type to another -(e.g. from RSPrimary to RSSecondary) its RTT is updated normally, -not set to null nor restarted from scratch. - -lastWriteDate and opTime -```````````````````````` - -The isMaster response of a replica set member running MongoDB 3.4 and later -contains a ``lastWrite`` subdocument with fields ``lastWriteDate`` and ``opTime`` -(`SERVER-8858`_). -If these fields are available, parse them from the ismaster response, -otherwise set them to null. - -Clients MUST NOT attempt to compensate for the network latency between when the server -generated its isMaster response and when the client records ``lastUpdateTime``. - -.. _SERVER-8858: https://jira.mongodb.org/browse/SERVER-8858 - -lastUpdateTime -`````````````` - -Clients SHOULD set lastUpdateTime with a monotonic clock. - -Hostnames are normalized to lower-case -`````````````````````````````````````` - -The same as with seeds provided in the initial configuration, -all hostnames in the ismaster response's "me", "hosts", "passives", and "arbiters" -entries must be lower-cased. - -This prevents unnecessary work rediscovering a server -if a seed "A" is provided and the server -responds that "a" is in the replica set. - -`RFC 4343 `_: - - Domain Name System (DNS) names are "case insensitive". - -logicalSessionTimeoutMinutes -```````````````````````````` - -MongoDB 3.6 and later include a ``logicalSessionTimeoutMinutes`` field if -logical sessions are enabled in the deployment. Clients MUST check for this -field and set the ServerDescription's logicalSessionTimeoutMinutes field to this -value, or to null otherwise. - -Other ServerDescription fields -`````````````````````````````` - -Other required fields -defined in the `ServerDescription`_ data structure -are parsed from the ismaster response in the obvious way. - -.. _updates its view of the topology: - -Updating the TopologyDescription -'''''''''''''''''''''''''''''''' - -Each time the client checks a server, -it processes the outcome (successful or not) -to create a `ServerDescription`_, -and then it processes the ServerDescription to update its `TopologyDescription`_. - -The TopologyDescription's `TopologyType`_ influences -how the ServerDescription is processed. -The following subsection -specifies how the client updates its TopologyDescription -when the TopologyType is Single. -The next subsection treats the other types. - -TopologyType Single -``````````````````` - -The TopologyDescription's type was initialized as Single -and remains Single forever. -There is always one ServerDescription in TopologyDescription.servers. - -Whenever the client checks a server (successfully or not) -the ServerDescription in TopologyDescription.servers -is replaced with the new ServerDescription. - -.. _is compatible: - -Checking wire protocol compatibility -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ServerDescription is incompatible if: - -* minWireVersion > clientMaxWireVersion, or -* maxWireVersion < clientMinWireVersion - -If any ServerDescription is incompatible, the client MUST set the -TopologyDescription's "compatible" field to false and fill out the -TopologyDescription's "compatibilityError" field like so: - -- if ServerDescription.minWireVersion > clientMaxWireVersion: - - "Server at $host:$port requires wire version $minWireVersion, but this version - of $driverName only supports up to $clientMaxWireVersion." - -- if ServerDescription.maxWireVersion < clientMinWireVersion: - - "Server at $host:$port reports wire version $maxWireVersion, but this version - of $driverName requires at least $clientMinWireVersion (MongoDB - $mongoVersion)." - -Replace $mongoVersion with the appropriate MongoDB minor version, for example if -clientMinWireVersion is 2 and it connects to MongoDB 2.4, format the error like: - - "Server at example.com:27017 reports wire version 0, but this version - of My Driver requires at least 2 (MongoDB 2.6)." - -In this second case, the exact required MongoDB version is known and can be -named in the error message, whereas in the first case the implementor does not -know which MongoDB versions will be compatible or incompatible in the future. - -Verifying setName with TopologyType Single -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A client MAY allow the user to supply a setName -with an initial TopologyType of Single. -In this case, if the ServerDescription's setName is null or wrong, -the client MUST throw an error on every operation. - -Other TopologyTypes -``````````````````` - -If the TopologyType is **not** Single, there can be one or more seeds. - -Whenever a client completes an ismaster call, -it creates a new ServerDescription with the proper `ServerType`_. -It replaces the server's previous description in TopologyDescription.servers -with the new one. - -Apply the logic for `checking wire protocol compatibility`_ to each -ServerDescription in the topology. -If any server's wire protocol version range does not overlap with the client's, -the client updates the "compatible" and "compatibilityError" fields -as described above for TopologyType Single. -Otherwise "compatible" is set to true. - -It is possible for a multi-threaded client to receive an ismaster outcome -from a server after the server has been removed from the TopologyDescription. -For example, a monitor begins checking a server "A", -then a different monitor receives a response from the primary -claiming that "A" has been removed from the replica set, -so the client removes "A" from the TopologyDescription. -Then, the check of server "A" completes. - -In all cases, the client MUST ignore ismaster outcomes from servers -that are not in the TopologyDescription. - -The following subsections explain in detail what actions the client takes -after replacing the ServerDescription. - -TopologyType table -~~~~~~~~~~~~~~~~~~ - -The new ServerDescription's type is the vertical axis, -and the current TopologyType is the horizontal. -Where a ServerType and a TopologyType intersect, -the table shows what action the client takes. - -"no-op" means, -do nothing **after** replacing the server's old description -with the new one. - -.. csv-table:: - :header-rows: 1 - :stub-columns: 1 - - ,TopologyType Unknown,TopologyType Sharded,TopologyType ReplicaSetNoPrimary,TopologyType ReplicaSetWithPrimary - ServerType Unknown,no-op,no-op,no-op,`checkIfHasPrimary`_ - ServerType Standalone,`updateUnknownWithStandalone`_,`remove`_,`remove`_,`remove`_ and `checkIfHasPrimary`_ - ServerType Mongos,Set type to TopologyType Sharded,no-op,`remove`_,`remove`_ and `checkIfHasPrimary`_ - ServerType RSPrimary,`updateRSFromPrimary`_,`remove`_, `updateRSFromPrimary`_,`updateRSFromPrimary`_ - ServerType RSSecondary,Set type to ReplicaSetNoPrimary then `updateRSWithoutPrimary`_,`remove`_,`updateRSWithoutPrimary`_,`updateRSWithPrimaryFromMember`_ - ServerType RSArbiter,Set type to ReplicaSetNoPrimary then `updateRSWithoutPrimary`_,`remove`_,`updateRSWithoutPrimary`_,`updateRSWithPrimaryFromMember`_ - ServerType RSOther,Set type to ReplicaSetNoPrimary then `updateRSWithoutPrimary`_,`remove`_,`updateRSWithoutPrimary`_,`updateRSWithPrimaryFromMember`_ - ServerType RSGhost,no-op [#]_,`remove`_,no-op,`checkIfHasPrimary`_ - -.. [#] `TopologyType remains Unknown when an RSGhost is discovered`_. - -TopologyType explanations -~~~~~~~~~~~~~~~~~~~~~~~~~ - -This subsection complements the `TopologyType table`_ -with prose explanations of the TopologyTypes (besides Single). - -TopologyType Unknown - A starting state. - - **Actions**: - - * If the incoming ServerType is Unknown (that is, the ismaster call failed), - keep the server in TopologyDescription.servers. - The TopologyType remains Unknown. - * The `TopologyType remains Unknown when an RSGhost is discovered`_, too. - * If the type is Standalone, run `updateUnknownWithStandalone`_. - * If the type is Mongos, set the TopologyType to Sharded. - * If the type is RSPrimary, record its setName - and call `updateRSFromPrimary`_. - * If the type is RSSecondary, RSArbiter or RSOther, record its setName, - set the TopologyType to ReplicaSetNoPrimary, - and call `updateRSWithoutPrimary`_. - -TopologyType Sharded - A steady state. Connected to one or more mongoses. - - **Actions**: - - * If the server is Unknown or Mongos, keep it. - * Remove others. - -TopologyType ReplicaSetNoPrimary - A starting state. - The topology is definitely a replica set, - but no primary is known. - - **Actions**: - - * Keep Unknown servers. - * Keep RSGhost servers: they are members of some replica set, - perhaps this one, and may recover. - (See `RSGhost and RSOther`_.) - * Remove any Standalones or Mongoses. - * If the type is RSPrimary call `updateRSFromPrimary`_. - * If the type is RSSecondary, RSArbiter or RSOther, - run `updateRSWithoutPrimary`_. - -TopologyType ReplicaSetWithPrimary - A steady state. The primary is known. - - **Actions**: - - * If the server type is Unknown, keep it, - and run `checkIfHasPrimary`_. - * Keep RSGhost servers: they are members of some replica set, - perhaps this one, and may recover. - (See `RSGhost and RSOther`_.) - Run `checkIfHasPrimary`_. - * Remove any Standalones or Mongoses - and run `checkIfHasPrimary`_. - * If the type is RSPrimary run `updateRSFromPrimary`_. - * If the type is RSSecondary, RSArbiter or RSOther, - run `updateRSWithPrimaryFromMember`_. - -Actions -~~~~~~~ - -.. _updateUnknownWithStandalone: - -updateUnknownWithStandalone - This subroutine is executed - with the ServerDescription from Standalone (including a slave) - when the TopologyType is Unknown:: - - if description.address not in topologyDescription.servers: - return - - if settings.seeds has one seed: - topologyDescription.type = Single - else: - remove this server from topologyDescription and stop monitoring it - - See `TopologyType remains Unknown when one of the seeds is a Standalone`_. - -.. _updateRSWithoutPrimary: - -updateRSWithoutPrimary - This subroutine is executed - with the ServerDescription from an RSSecondary, RSArbiter, or RSOther - when the TopologyType is ReplicaSetNoPrimary:: - - if description.address not in topologyDescription.servers: - return - - if topologyDescription.setName is null: - topologyDescription.setName = description.setName - - else if topologyDescription.setName != description.setName: - remove this server from topologyDescription and stop monitoring it - return - - for each address in description's "hosts", "passives", and "arbiters": - if address is not in topologyDescription.servers: - add new default ServerDescription of type "Unknown" - begin monitoring the new server - - if description.primary is not null: - find the ServerDescription in topologyDescription.servers whose - address equals description.primary - - if its type is Unknown, change its type to PossiblePrimary - - if description.address != description.me: - remove this server from topologyDescription and stop monitoring it - return - - Unlike `updateRSFromPrimary`_, - this subroutine does **not** remove any servers from the TopologyDescription - based on the list of servers in isMaster.hosts. The only server that might be - removed is the server itself that the isMaster response is from. - - The special handling of description.primary - ensures that a single-threaded client - `scans`_ the possible primary before other members. - - See `replica set monitoring with and without a primary`_. - -.. _updateRSWithPrimaryFromMember: - -updateRSWithPrimaryFromMember - This subroutine is executed - with the ServerDescription from an RSSecondary, RSArbiter, or RSOther - when the TopologyType is ReplicaSetWithPrimary:: - - if description.address not in topologyDescription.servers: - # While we were checking this server, another thread heard from the - # primary that this server is not in the replica set. - return - - # SetName is never null here. - if topologyDescription.setName != description.setName: - remove this server from topologyDescription and stop monitoring it - - if description.address != description.me: - remove this server from topologyDescription and stop monitoring it - checkIfHasPrimary() - return - - # Had this member been the primary? - if there is no primary in topologyDescription.servers: - topologyDescription.type = ReplicaSetNoPrimary - - if description.primary is not null: - find the ServerDescription in topologyDescription.servers whose - address equals description.primary - - if its type is Unknown, change its type to PossiblePrimary - - The special handling of description.primary - ensures that a single-threaded client - `scans`_ the possible primary before other members. - -.. _updateRSFromPrimary: - -updateRSFromPrimary - This subroutine is executed with a ServerDescription of type RSPrimary:: - - if description.address not in topologyDescription.servers: - return - - if topologyDescription.setName is null: - topologyDescription.setName = description.setName - - else if topologyDescription.setName != description.setName: - # We found a primary but it doesn't have the setName - # provided by the user or previously discovered. - remove this server from topologyDescription and stop monitoring it - checkIfHasPrimary() - return - - if description.setVersion is not null and description.electionId is not null: - # Election ids are ObjectIds, see - # "using setVersion and electionId to detect stale primaries" - # for comparison rules. - if (topologyDescription.maxSetVersion is not null and - topologyDescription.maxElectionId is not null and ( - topologyDescription.maxSetVersion > description.setVersion or ( - topologyDescription.maxSetVersion == description.setVersion and - topologyDescription.maxElectionId > description.electionId - ) - ): - - # Stale primary. - replace description with a default ServerDescription of type "Unknown" - checkIfHasPrimary() - return - - topologyDescription.maxElectionId = description.electionId - - if (description.setVersion is not null and - (topologyDescription.maxSetVersion is null or - description.setVersion > topologyDescription.maxSetVersion)): - - topologyDescription.maxSetVersion = description.setVersion - - for each server in topologyDescription.servers: - if server.address != description.address: - if server.type is RSPrimary: - # See note below about invalidating an old primary. - replace the server with a default ServerDescription of type "Unknown" - - for each address in description's "hosts", "passives", and "arbiters": - if address is not in topologyDescription.servers: - add new default ServerDescription of type "Unknown" - begin monitoring the new server - - for each server in topologyDescription.servers: - if server.address not in description's "hosts", "passives", or "arbiters": - remove the server and stop monitoring it - - checkIfHasPrimary() - - A note on invalidating the old primary: - when a new primary is discovered, - the client finds the previous primary (there should be none or one) - and replaces its description - with a default ServerDescription of type "Unknown." - A multi-threaded client MUST check that server as soon as possible. - (The Monitor provides a "request refresh" feature for this purpose, - see `multi-threaded or asynchronous monitoring`_.) - - The client SHOULD clear its connection pool for the old primary, too: - the connections are all bad because the old primary has closed its sockets. - - See `replica set monitoring with and without a primary`_. - - If the server is primary with an obsolete setVersion or electionId, it is - likely a stale primary that is going to step down. Mark it Unknown and let periodic - monitoring detect when it becomes secondary. See - `using setVersion and electionId to detect stale primaries`_. - - A note on checking "me": Unlike `updateRSWithPrimaryFromMember`, there is no need to remove the server if the address is not equal to - "me": since the server address will not be a member of either "hosts", "passives", or "arbiters", the server will already have been - removed. - -.. _checkIfHasPrimary: - -checkIfHasPrimary - Set TopologyType to ReplicaSetWithPrimary if there is an RSPrimary - in TopologyDescription.servers, otherwise set it to ReplicaSetNoPrimary. - - For example, if the TopologyType is ReplicaSetWithPrimary - and the client is processing a new ServerDescription of type Unknown, - that could mean the primary just disconnected, - so checkIfHasPrimary must run to check if the TopologyType should become - ReplicaSetNoPrimary. - - Another example is if the client first reaches the primary via its external - IP, but the response's host list includes only internal IPs. - In that case the client adds the primary's internal IP to the - TopologyDescription and begins monitoring it, and removes the external IP. - Right after removing the external IP from the description, - the TopologyType MUST be ReplicaSetNoPrimary, since no primary is - available at this moment. - -.. _remove: - -remove - Remove the server from TopologyDescription.servers and stop monitoring it. - - In multi-threaded clients, a monitor may be currently checking this server - and may not immediately abort. - Once the check completes, this server's ismaster outcome MUST be ignored, - and the monitor SHOULD halt. - -Logical Session Timeout -``````````````````````` - -Whenever a client updates the TopologyDescription from an ismaster response, -it MUST set TopologyDescription.logicalSessionTimeoutMinutes to the smallest -logicalSessionTimeoutMinutes value among ServerDescriptions of all data-bearing -server types. If any have a null logicalSessionTimeoutMinutes, -then TopologyDescription.logicalSessionTimeoutMinutes MUST be set to null. - -See the Driver Sessions Spec for the purpose of this value. - -.. _drivers update their topology view in response to errors: - -Error handling -'''''''''''''' - -Network error during server check -````````````````````````````````` - -When a server `check`_ fails due to a network error, -the client SHOULD clear its connection pool for the server: -if the monitor's socket is bad it is likely that all are. -(See `JAVA-1252 `_.) - -Once a server is connected, the client MUST change its type -to Unknown -only after it has retried the server once. -(This rule applies to server checks during monitoring. -It does *not* apply when multi-threaded -`clients update the topology from each handshake`_.) - -In this pseudocode, "description" is the prior ServerDescription:: - - def checkServer(description): - try: - call ismaster - return new ServerDescription - except NetworkError as e0: - clear connection pool for the server - - if description.type is Unknown or PossiblePrimary: - # Failed on first try to reach this server, give up. - return new ServerDescription with type=Unknown, error=e0 - else: - # We've been connected to this server in the past, retry once. - try: - reconnect and call ismaster - return new ServerDescription - except NetworkError as e1: - return new ServerDescription with type=Unknown, error=e1 - -(See `retry ismaster calls once`_ and -`JAVA-1159 `_.) - -Network error when reading or writing -````````````````````````````````````` - -To describe how the client responds to network errors during application operations, -we distinguish two phases of connecting to a server and using it for application operations: - -- *Before the handshake completes*: the client establishes a new connection to the server - and completes an initial handshake by calling "isMaster" and reading the response, - and optionally completing authentication -- *After the handshake completes*: the client uses the established connection for - application operations - -If there is a network error or timeout on the connection before the handshake completes, -the client MUST replace the server's description -with a default ServerDescription of type Unknown, -and fill the ServerDescription's error field with useful information. - -If there is a network timeout on the connection after the handshake completes, -the client MUST NOT mark the server Unknown. -(A timeout may indicate a slow operation on the server, -rather than an unavailable server.) -If, however, there is some other network error on the connection after the handshake completes, -the client MUST replace the server's description -with a default ServerDescription of type Unknown, -and fill the ServerDescription's error field with useful information, -the same as if an error or timeout occurred before the handshake completed. - -When the client marks a server Unknown due to a network error or timeout, -the Unknown ServerDescription MUST be sent through the same process for -`updating the TopologyDescription`_ as if it had been a failed ismaster outcome -from a server check: for example, if the TopologyType is ReplicaSetWithPrimary -and a write to the RSPrimary server fails because of a network error -(other than timeout), then a new ServerDescription is created for the primary, -with type Unknown, and the client executes the proper subroutine for an -Unknown server when the TopologyType is ReplicaSetWithPrimary: -referring to the table above we see the subroutine is `checkIfHasPrimary`_. -The result is the TopologyType changes to ReplicaSetNoPrimary. -See the test scenario called "Network error writing to primary". - -The client SHOULD close all idle sockets in its connection pool for the server: -if one socket is bad, it is likely that all are. - -Clients MUST NOT request an immediate check of the server; -since application sockets are used frequently, a network error likely means -the server has just become unavailable, -so an immediate refresh is likely to get a network error, too. - -The server will not remain Unknown forever. -It will be refreshed by the next periodic check or, -if an application operation needs the server sooner than that, -then a re-check will be triggered by the server selection algorithm. - -"not master" and "node is recovering" -````````````````````````````````````` -These errors are detected from a getLastError response, -write command response, or query response. Clients MUST check if the server -error is a "node is recovering" error or a "not master" error. - -If the response includes an error code, it MUST be checked first to attempt -to determine if error is a "node is recovering" or "not master" error. - -The following error codes indicate a replica set member is temporarily -unusable. These are called "node is recovering" errors: - -.. list-table:: - :header-rows: 1 - - * - Error Name - - Error Code - * - InterruptedAtShutdown - - 11600 - * - InterruptedDueToReplStateChange - - 11602 - * - NotMasterOrSecondary - - 13436 - * - PrimarySteppedDown - - 189 - * - ShutdownInProgress - - 91 - -And the following error codes indicate a "not master" error: - -.. list-table:: - :header-rows: 1 - - * - Error Name - - Error Code - * - NotMaster - - 10107 - * - NotMasterNoSlaveOk - - 13435 - -If any other error code is included in the response, or an error code is -omitted, clients MUST check the error message. The error is considered a "node -is recovering" error if the substrings "node is recovering" or "not master or -secondary" are anywhere in the error message. Otherwise, if the substring "not -master" is in the error message it is a "not master" error. - -Additionally, if the response includes a write concern error, then the code -and message of the write concern error MUST be checked the same way a response -error is checked above. - -The following pseudocode checks a response for a "not master" or "node is -recovering" error:: - - recovering_codes = [11600, 11602, 13436, 189, 91] - notmaster_codes = [10107, 13435] - - def is_recovering(message, code): - if code and code in recovering_codes: - return true - # if no code or an unrecognized code, use the error message. - return ("not master or secondary" in message - or "node is recovering" in message) - - def is_notmaster(message, code): - if code and code in nonmaster_codes: - return true - # if no code or an unrecognized code, use the error message. - if is_recovering(message, None): - return false - return ("not master" in message) - - def is_notmaster_or_recovering(message, code): - return is_recovering(message, code) or is_notmaster(message, code) - - def parse_gle(response): - if "err" in response: - if is_notmaster_or_recovering(response["err"], response["code"]): - handle_notmaster_or_recovering(response["err"], response["code"]) - - # Parse response to any command besides getLastError. - def parse_command_response(response): - if not response["ok"]: - if is_notmaster_or_recovering(response["errmsg"], response["code"]): - handle_notmaster_or_recovering(response["errmsg"], response["code"]) - else if response["writeConcernError"]: - wce = response["writeConcernError"] - if is_notmaster_or_recovering(wce["errmsg"], wce["code"]): - handle_notmaster_or_recovering(wce["errmsg"], wce["code"]) - - def parse_query_response(response): - if the "QueryFailure" bit is set in response flags: - if is_notmaster_or_recovering(response["$err"], response["code"]): - handle_notmaster_or_recovering(response["$err"], response["code"]) - - def handle_notmaster_or_recovering(message, code): - replace server's description with - new ServerDescription(type=Unknown, error=message, code=code) - - if multi-threaded: - request immediate check - else: - # Check right now if this is "not master", since it might be a - # useful secondary. If it's "node is recovering" leave it for the - # next full scan. - if is_notmaster(message): - check failing server - - clear connection pool for server - -See the test scenario called -"parsing 'not master' and 'node is recovering' errors" -for example response documents. - -When the client sees a "not master" or "node is recovering" error -it MUST replace the server's description -with a default ServerDescription of type Unknown. -It MUST store useful information in the new ServerDescription's error field, -including the error message from the server. - -Multi-threaded and asynchronous clients MUST `request an immediate check`_ -of the server. -Unlike in the "network error" scenario above, -a "not master" or "node is recovering" error means the server is available -but the client is wrong about its type, -thus an immediate re-check is likely to provide useful information. - -For single-threaded clients, in the case of a "not master" error, the client -MUST mark the topology as "stale" so the next server selection scans all -servers. For a "node is recovering" error, single-threaded clients MUST NOT -mark the topology as "stale". If a node is recovering for some time, an -immediate scan may not gain useful information. - -The client SHOULD clear its connection pool for the server. - -(See `when does a client see "not master" or "node is recovering"?`_, `use -error messages to detect "not master" and "node is recovering"`_, and `other -transient errors`_.) - -Monitoring SDAM events -'''''''''''''''''''''' - -The required driver specification for providing lifecycle hooks into server -discovery and monitoring for applications to consume can be found in the -`SDAM Monitoring Specification `_. - -Implementation notes -'''''''''''''''''''' - -This section intends to provide generous guidance to driver authors. -It is complementary to the reference implementations. -Words like "should", "may", and so on are used more casually here. - -.. _interaction between monitoring and server selection: - -Multi-threaded or asynchronous server selection -``````````````````````````````````````````````` - -While no suitable server is available for an operation, -`the client MUST re-check all servers every minHeartbeatFrequencyMS`_. -(See `requesting an immediate check`_.) - -Single-threaded server selection -```````````````````````````````` - -When a client that uses `single-threaded monitoring`_ -fails to select a suitable server for any operation, -it `scans`_ the servers, then attempts selection again, -to see if the scan discovered suitable servers. It repeats, waiting -`minHeartbeatFrequencyMS`_ after each scan, until a timeout. - -Documentation -````````````` - -Giant seed lists -~~~~~~~~~~~~~~~~ - -Drivers' manuals should warn against huge seed lists, -since it will slow initialization for single-threaded clients -and generate load for multi-threaded and asynchronous drivers. - -.. _implementation notes for multi-threaded clients: - -Multi-threaded -`````````````` - -.. _use min and maxWireVersion only to determine compatibility: - -Warning about the maxWireVersion from a monitor's ismaster response -``````````````````````````````````````````````````````````````````` - -Clients consult some fields from a server's ismaster response -to decide how to communicate with it: - -* maxWireVersion -* maxBsonObjectSize -* maxMessageSizeBytes -* maxWriteBatchSize - -It is tempting to take these values -from the last ismaster response a *monitor* received -and store them in the ServerDescription, but this is an anti-pattern. -Multi-threaded and asynchronous clients that do so -are prone to several classes of race, for example: - -* Setup: A MongoDB 3.0 Standalone with authentication enabled, - the client must log in with SCRAM-SHA-1. -* The monitor thread discovers the server - and stores maxWireVersion on the ServerDescription -* An application thread wants a socket, selects the Standalone, - and is about to check the maxWireVersion on its ServerDescription when... -* The monitor thread gets disconnected from server and marks it Unknown, - with default maxWireVersion of 0. -* The application thread resumes, creates a socket, - and attempts to log in using MONGODB-CR, - since maxWireVersion is *now* reported as 0. -* Authentication fails, the server requires SCRAM-SHA-1. - -Better to call ismaster for each new socket, as required by the `Auth Spec -`_, -and use the ismaster response associated with that socket -for maxWireVersion, maxBsonObjectSize, etc.: -all the fields required to correctly communicate with the server. - -The ismaster responses received by monitors determine if the topology -as a whole `is compatible`_ with the driver, -and which servers are suitable for selection. -The monitors' responses should not be used to determine how to format -wire protocol messages to the servers. - -Monitor thread -~~~~~~~~~~~~~~ - -Most platforms can use an event object -to control the monitor thread. -The event API here is assumed to be like the standard `Python Event -`_. -`heartbeatFrequencyMS`_ is configurable, -`minHeartbeatFrequencyMS`_ is always 500 milliseconds:: - - def run(): - while this monitor is not stopped: - check server and create newServerDescription - onServerDescriptionChanged(newServerDescription) - - start = gettime() - - # Can be awakened by requestCheck(). - event.wait(heartbeatFrequencyMS) - event.clear() - - waitTime = gettime() - start - if waitTime < minHeartbeatFrequencyMS: - # Cannot be awakened. - sleep(minHeartbeatFrequencyMS - waitTime) - -`Requesting an immediate check`_:: - - def requestCheck(): - event.set() - -Immutable data -~~~~~~~~~~~~~~ - -Multi-threaded drivers should treat -ServerDescriptions and -TopologyDescriptions as immutable: -the client replaces them, rather than modifying them, -in response to new information about the topology. -Thus readers of these data structures -can simply acquire a reference to the current one -and read it, without holding a lock that would block a monitor -from making further updates. - -Process one ismaster outcome at a time -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Although servers are checked in parallel, -the function that actually creates the new TopologyDescription -should be synchronized so only one thread can run it at a time. - -.. _onServerDescriptionChanged: - -Replacing the TopologyDescription -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers may use the following pseudocode to guide -their implementation. -The client object has a lock and a condition variable. -It uses the lock to ensure that only one new ServerDescription is processed -at a time. -Once the client has taken the lock it must do no I/O:: - - def onServerDescriptionChanged(server): - # "server" is the new ServerDescription. - - # This thread cannot do any I/O until the lock is released. - client.lock.acquire() - - if server.address not in client.topologyDescription.servers: - # The server was once in the topologyDescription, otherwise - # we wouldn't have been monitoring it, but an intervening - # state-change removed it. E.g., we got a host list from - # the primary that didn't include this server. - client.lock.release() - return - - newTopologyDescription = client.topologyDescription.copy() - - # Replace server's previous description. - address = server.address - newTopologyDescription.servers[address] = server - - take any additional actions, - depending on the TopologyType and server... - - # Replace TopologyDescription and notify waiters. - client.topologyDescription = newTopologyDescription - client.condition.notifyAll() - client.lock.release() - -.. https://github.com/mongodb/mongo-java-driver/blob/5fb47a3bf86c56ed949ce49258a351773f716d07/src/main/com/mongodb/BaseCluster.java#L160 - -Notifying the condition unblocks threads waiting in the server-selection loop -for a suitable server to be discovered. .. note:: - The Java driver uses a CountDownLatch instead of a condition variable, - and it atomically swaps the old and new CountDownLatches - so it does not need "client.lock". - It does, however, use a lock to ensure that only one thread runs - onServerDescriptionChanged at a time. - -Rationale ---------- - -Clients do no I/O in the constructor -'''''''''''''''''''''''''''''''''''' - -An alternative proposal was to distinguish between "discovery" and "monitoring". -When discovery begins, the client checks all its seeds, -and discovery is complete once all servers have been checked, -or after some maximum time. -Application operations cannot proceed until discovery is complete. - -If the discovery phase is distinct, -then single- and multi-threaded drivers -could accomplish discovery in the constructor, -and throw an exception from the constructor -if the deployment is unavailable or misconfigured. -This is consistent with prior behavior for many drivers. -It will surprise some users that the constructor now succeeds, -but all operations fail. - -Similarly for misconfigured seed lists: -the client may discover a mix of mongoses and standalones, -or find multiple replica set names. -It may surprise some users that the constructor succeeds -and the client attempts to proceed with a compatible subset of the deployment. - -Nevertheless, this spec prohibits I/O in the constructor -for the following reasons: - -Common case -``````````` - -In the common case, the deployment is available and usable. -This spec favors allowing operations to proceed as soon as possible -in the common case, -at the cost of surprising behavior in uncommon cases. - -Simplicity -`````````` - -It is simpler to omit a special discovery phase -and treat all server `checks`_ the same. - -Consistency -``````````` - -Asynchronous clients cannot do I/O in a constructor, -so it is consistent to prohibit I/O in other clients' constructors as well. - -Restarts -```````` - -If clients can be constructed when the deployment is in some states -but not in other states, -it leads to an unfortunate scenario: -When the deployment is passing through a strange state, -long-running clients may keep working, -but any clients restarted during this period fail. - -Say an administrator changes one replica set member's setName. -Clients that are already constructed remove the bad member and stay usable, -but if any client is restarted its constructor fails. -Web servers that dynamically adjust their process pools -will show particularly undesirable behavior. - -heartbeatFrequencyMS defaults to 10 seconds or 60 seconds -''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Many drivers have different values. The time has come to standardize. -Lacking a rigorous methodology for calculating the best frequency, -this spec chooses 10 seconds for multi-threaded or asynchronous drivers -because some already use that value. - -Because scanning has a greater impact on -the performance of single-threaded drivers, -they MUST default to a longer frequency (60 seconds). - -An alternative is to check servers less and less frequently -the longer they remain unchanged. -This idea is rejected because -it is a goal of this spec to answer questions about monitoring such as, - -* "How rapidly can I rotate a replica set to a new set of hosts?" -* "How soon after I add a secondary will query load be rebalanced?" -* "How soon will a client notice a change in round trip time, or tags?" - -Having a constant monitoring frequency allows us to answer these questions -simply and definitively. -Losing the ability to answer these questions is not worth -any minor gain in efficiency from a more complex scheduling method. - -The client MUST re-check all servers every minHeartbeatFrequencyMS -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -While an application is waiting to do an operation -for which there is no suitable server, -a multi-threaded client MUST re-check all servers very frequently. -The slight cost is worthwhile in many scenarios. For example: - -#. A client and a MongoDB server are started simultaneously. -#. The client checks the server before it begins listening, - so the check fails. -#. The client waits in the server-selection loop for the topology to change. - -In this state, the client should check the server very frequently, -to give it ample opportunity to connect to the server before -timing out in server selection. - -No knobs -'''''''' - -This spec does not intend to introduce any new configuration options -unless absolutely necessary. - -.. _monitors all three types of servers: - -The client MUST monitor arbiters -'''''''''''''''''''''''''''''''' - -Mongos 2.6 does not monitor arbiters, -but it costs little to do so, -and in the rare case that -all data members are moved to new hosts in a short time, -an arbiter may be the client's last hope -to find the new replica set configuration. - -Only support replica set members running MongoDB 1.6.2 or later -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Replica set members began reporting their setNames in that version. -Supporting earlier versions is impractical. - -TopologyType remains Unknown when an RSGhost is discovered -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -If the TopologyType is Unknown and the client receives an ismaster response -from an`RSGhost`_, the TopologyType could be set to ReplicaSetNoPrimary. -However, an RSGhost does not report its setName, -so the setName would still be unknown. -This adds an additional state to the existing list: -"TopologyType ReplicaSetNoPrimary **and** no setName." -The additional state adds substantial complexity -without any benefit, so this spec says clients MUST NOT change the TopologyType -when an RSGhost is discovered. - -TopologyType remains Unknown when one of the seeds is a Standalone -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -If TopologyType is Unknown and there are multiple seeds, -and one of them is discovered to be a standalone, -it MUST be removed. -The TopologyType remains Unknown. - -This rule supports the following common scenario: - -#. Servers A and B are in a replica set. -#. A seed list with A and B is stored in a configuration file. -#. An administrator removes B from the set and brings it up as standalone - for maintenance, without changing its port number. -#. The client is initialized with seeds A and B, - TopologyType Unknown, and no setName. -#. The first ismaster response is from B, the standalone. - -What if the client changed TopologyType to Single at this point? -It would be unable to use the replica set; it would have to remove A -from the TopologyDescription once A's ismaster response comes. - -The user's intent in this case is clearly to use the replica set, -despite the outdated seed list. So this spec requires clients to remove B -from the TopologyDescription and keep the TopologyType as Unknown. -Then when A's response arrives, the client can set its TopologyType -to ReplicaSet (with or without primary). - -On the other hand, -if there is only one seed and the seed is discovered to be a Standalone, -the TopologyType MUST be set to Single. - -See the "member brought up as standalone" test scenario. - -Thread per server -''''''''''''''''' - -Mongos uses a monitor thread per replica set, rather than a thread per server. -A thread per server is impractical if mongos is monitoring a large number of -replica sets. -But a driver only monitors one. - -In mongos, threads trying to do reads and writes join the effort to scan -the replica set. -Such threads are more likely to be abundant in mongos than in drivers, -so mongos can rely on them to help with monitoring. - -In short: mongos has different scaling concerns than -a multi-threaded or asynchronous driver, -so it allocates threads differently. - -Socket timeout for monitoring is connectTimeoutMS -''''''''''''''''''''''''''''''''''''''''''''''''' - -When a client waits for a server to respond to a connection, -the client does not know if the server will respond eventually or if it is down. -Users can help the client guess correctly -by supplying a reasonable connectTimeoutMS for their network: -on some networks a server is probably down if it hasn't responded in 10 ms, -on others a server might still be up even if it hasn't responded in 10 seconds. - -The socketTimeoutMS, on the other hand, must account for both network latency -and the operation's duration on the server. -Applications should typically set a very long or infinite socketTimeoutMS -so they can wait for long-running MongoDB operations. - -Multi-threaded clients use distinct sockets for monitoring and for application -operations. -A socket used for monitoring does two things: it connects and calls ismaster. -Both operations are fast on the server, so only network latency matters. -Thus both operations SHOULD use connectTimeoutMS, since that is the value -users supply to help the client guess if a server is down, -based on users' knowledge of expected latencies on their networks. - -A monitor SHOULD NOT use the client's regular connection pool -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -If a multi-threaded driver's connection pool enforces a maximum size -and monitors use sockets from the pool, -there are two bad options: -either monitors compete with the application for sockets, -or monitors have the exceptional ability -to create sockets even when the pool has reached its maximum size. -The former risks starving the monitor. -The latter is more complex than it is worth. -(A lesson learned from PyMongo 2.6's pool, which implemented this option.) - -Since this rule is justified for drivers that enforce a maximum pool size, -this spec recommends that all drivers follow the same rule -for the sake of consistency. - -Replica set monitoring with and without a primary -''''''''''''''''''''''''''''''''''''''''''''''''' - -The client strives to fill the "servers" list -only with servers that the **primary** -said were members of the replica set, -when the client most recently contacted the primary. - -The primary's view of the replica set is authoritative for two reasons: - -1. The primary is never on the minority side of a network partition. - During a partition it is the primary's list of - servers the client should use. -2. Since reconfigs must be executed on the primary, - the primary is the first to know of them. - Reconfigs propagate to non-primaries eventually, - but the client can receive ismaster responses from non-primaries - that reflect any past state of the replica set. - See the "Replica set discovery" test scenario. - -If at any time the client believes there is no primary, -the TopologyDescription's type is set to ReplicaSetNoPrimary. -While there is no known primary, -the client MUST **add** servers from non-primaries' host lists, -but it MUST NOT remove servers from the TopologyDescription. - -Eventually, when a primary is discovered, any hosts not in the primary's host -list are removed. - -.. _stale primaries: - -Using setVersion and electionId to detect stale primaries -''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Replica set members running MongoDB 2.6.10+ or 3.0+ include an integer called -"setVersion" and an ObjectId called -"electionId" in their ismaster response. -Starting with MongoDB 3.2.0, replica sets can use two different replication -protocol versions; electionIds from one protocol version must not be compared -to electionIds from a different protocol. - -Because protocol version changes require replica set reconfiguration, -clients use the tuple (setVersion, electionId) to detect stale primaries. - -The client remembers the greatest setVersion and electionId reported by a primary, -and distrusts primaries from older setVersions or from the same setVersion -but with lesser electionIds. -It compares setVersions as integer values. -It compares electionIds as 12-byte big-endian integers. -This prevents the client from oscillating -between the old and new primary during a split-brain period, -and helps provide read-your-writes consistency with write concern "majority" -and read preference "primary". - -Requirements for read-your-writes consistency -````````````````````````````````````````````` - -Using (setVersion, electionId) only provides read-your-writes consistency if: - -* The application uses the same MongoClient instance for write-concern - "majority" writes and read-preference "primary" reads, and -* All members use MongoDB 2.6.10+, 3.0.0+ or 3.2.0+ with replication protocol 0 - and clocks are *less* than 30 seconds skewed, or -* All members run MongoDB 3.2.0 and replication protocol 1 - and clocks are *less* skewed than the election timeout - (`electionTimeoutMillis`, which defaults to 10 seconds), or -* All members run MongoDB 3.2.1+ and replication protocol 1 - (in which case clocks need not be synchronized). - -Scenario -```````` - -Consider the following situation: - -1. Server A is primary. -2. A network partition isolates A from the set, but the client still sees it. -3. Server B is elected primary. -4. The client discovers that B is primary, does a write-concern "majority" - write operation on B and receives acknowledgment. -5. The client receives an ismaster response from A, claiming A is still primary. -6. If the client trusts that A is primary, the next read-preference "primary" - read sees stale data from A that may *not* include the write sent to B. - -See `SERVER-17975 `_, "Stale -reads with WriteConcern Majority and ReadPreference Primary." - -Detecting a stale primary -````````````````````````` - -To prevent this scenario, the client uses setVersion and electionId to -determine which primary was elected last. In this case, it would not consider -A primary, nor read from it, after receiving B's ismaster response with the -same setVersion and a greater electionId. - -Monotonicity -```````````` - -The electionId is an ObjectId compared bytewise in big-endian order. -In some server versions, it is monotonic with respect -to a particular servers' system clock, but is not globally monotonic across -a deployment. However, if inter-server clock skews are small, it can be -treated as a monotonic value. - -In MongoDB 2.6.10+ (which has `SERVER-13542 `_ backported), -MongoDB 3.0.0+ or MongoDB 3.2+ (under replication protocol version 0), -the electionId's leading bytes are a server timestamp. -As long as server clocks are skewed *less* than 30 seconds, -electionIds can be reliably compared. -(This is precise enough, because in replication protocol version 0, servers -are designed not to complete more than one election every 30 seconds. -Elections do not take 30 seconds--they are typically much faster than that--but -there is a 30-second cooldown before the next election can complete.) - -Beginning in MongoDB 3.2.0, under replication protocol version 1, -the electionId begins with a timestamp, but -the cooldown is shorter. As long as inter-server clock skew is *less* than -the configured election timeout (`electionTimeoutMillis`, which defaults to -10 seconds), then electionIds can be reliably compared. - -Beginning in MongoDB 3.2.1, under replication protocol version 1, -the electionId is guaranteed monotonic -without relying on any clock synchronization. - -Using me field to detect seed list members that do not match host names in the replica set configuration -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Removal from the topology of seed list members where the "me" property does not match the address used to connect -prevents clients from being able to select a server, only to fail to re-select that server once the primary has responded. - -This scenario illustrates the problems that arise if this is NOT done: - -* The client specifies a seed list of A, B, C -* Server A responds as a secondary with hosts D, E, F -* The client executes a query with read preference of secondary, and server A is selected -* Server B responds as a primary with hosts D, E, F. Servers A, B, C are removed, as they don't appear in the primary's hosts list -* The client iterates the cursor and attempts to execute a get-more against server A. -* Server selection fails because server A is no longer part of the topology. - -With checking for "me" in place, it looks like this instead: - -* The client specifies a seed list of A, B, C -* Server A responds as a secondary with hosts D, E, F, where "me" is D, and so the client adds D, E, F as type "Unknown" and starts - monitoring them, but removes A from the topology. -* The client executes a query with read preference of secondary, and goes in to the server selection loop -* Server D responds as a secondary where "me" is D -* Server selection completes by matching D -* The client iterates the cursor and attempts to execute a get-more against server D. -* Server selection completes by matching D. - -Ignore setVersion unless the server is primary -'''''''''''''''''''''''''''''''''''''''''''''' - -It was thought that if all replica set members report a setVersion, -and a secondary's response has a higher setVersion than any seen, -that the secondary's host list could be considered as authoritative -as the primary's. (See `Replica set monitoring with and without a primary`_.) - -This scenario illustrates the problem with setVersion: - -* We have a replica set with servers A, B, and C. -* Server A is the primary, with setVersion 4. -* An administrator runs replSetReconfig on A, - which increments its setVersion to 5. -* The client checks Server A and receives the new config. -* Server A crashes before any secondary receives the new config. -* Server B is elected primary. It has the old setVersion 4. -* The client ignores B's version of the config - because its setVersion is not greater than 5. - -The client may never correct its view of the topology. - -Even worse: - -* An administrator runs replSetReconfig - on Server B, which increments its setVersion to 5. -* Server A restarts. - This results in *two* versions of the config, - both claiming to be version 5. - -If the client trusted the setVersion in this scenario, -it would trust whichever config it received first. - -mongos 2.6 ignores setVersion and only trusts the primary. -This spec requires all clients to ignore setVersion from non-primaries. - -Retry ismaster calls once -''''''''''''''''''''''''' - -A monitor's connection to a server is long-lived -and used only for ismaster calls. -So if a server has responded in the past, -a network error on the monitor's connection likely means there was -a network glitch or a server restart since the last check, -rather than that the server is down. -Marking the server Unknown in this case costs unnecessary effort. - -However, -if the server still doesn't respond when the monitor attempts to reconnect, -then it is probably down. - -Use error messages to detect "not master" and "node is recovering" -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -When error codes are not available, error messages are checked for the -substrings "not master" and "node is recovering". This is because older server -versions returned unstable error codes or no error codes in many -circumstances. - -Other transient errors -'''''''''''''''''''''' - -There are other transient errors a server may return, e.g. retryable errors -listed in the retryable writes spec. SDAM does not consider these because they -do not imply the connected server should be marked as "Unknown". For example, -the following errors may be returned from a mongos when it cannot route to a -shard: - -.. list-table:: - :header-rows: 1 - - * - Error Name - - Error Code - * - HostNotFound - - 7 - * - HostUnreachable - - 6 - * - NetworkTimeout - - 89 - * - SocketException - - 9001 - -When these are returned, the mongos should *not* be marked as "Unknown", since -it is more likely an issue with the shard. - -Clients use the hostnames listed in the replica set config, not the seed list -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Very often users have DNS aliases they use in their `seed list`_ instead of -the hostnames in the replica set config. For example, the name "host_alias" -might refer to a server also known as "host1", and the URI is:: - - mongodb://host_alias/?replicaSet=rs - -When the client connects to "host_alias", its ismaster response includes the -list of hostnames from the replica set config, which does not include the seed:: - - { - hosts: ["host1:27017", "host2:27017"], - setName: "rs", - ... other ismaster response fields ... - } - -This spec requires clients to connect to the hostnames listed in the ismaster -response. Furthermore, if the response is from a primary, the client MUST -remove all hostnames not listed. In this case, the client disconnects from -"host_alias" and tries "host1" and "host2". (See `updateRSFromPrimary`_.) - -Thus, replica set members must be reachable from the client by the hostnames -listed in the replica set config. - -An alternative proposal is for clients to continue using the hostnames in the -seed list. It could add new hosts from the ismaster response, and where a host -is known by two names, the client can deduplicate them using the "me" field and -prefer the name in the seed list. - -This proposal was rejected because it does not support key features of replica -sets: failover and zero-downtime reconfiguration. - -In our example, if "host1" and "host2" are not reachable from the client, the -client continues to use "host_alias" only. If that server goes down or is -removed by a replica set reconfig, the client is suddenly unable to reach the -replica set at all: by allowing the client to use the alias, we have hidden the -fact that the replica set's failover feature will not work in a crisis or -during a reconfig. - -In conclusion, to support key features of replica sets, we require that the -hostnames used in a replica set config are reachable from the client. - -Backwards Compatibility ------------------------ - -The Java driver 2.12.1 has a "heartbeatConnectRetryFrequency". -Since this spec recommends the option be named "minHeartbeatFrequencyMS", -the Java driver must deprecate its old option -and rename it minHeartbeatFrequency (for consistency with its other options -which also lack the "MS" suffix). - -Reference Implementation ------------------------- - -* Java driver 3.x -* PyMongo 3.x -* Perl driver 1.0.0 (in progress) - -Future Work ------------ - -MongoDB is likely to add some of the following features, -which will require updates to this spec: - -* Eventually consistent collections (SERVER-2956) -* Mongos discovery (SERVER-1834) -* Put individual databases into maintenance mode, - instead of the whole server (SERVER-7826) -* Put setVersion in write-command responses (SERVER-13909) - -Questions and Answers ---------------------- - -When does a client see "not master" or "node is recovering"? -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -These errors indicate one of these: - -* A write was attempted on an unwritable server - (arbiter, secondary, slave, ghost, or recovering). -* A read was attempted on an unreadable server - (arbiter, ghost, or recovering) - or a read was attempted on a read-only server without the slaveOk bit set. - -In any case the error is a symptom that -a ServerDescription's type no longer reflects reality. - -A primary closes its connections when it steps down, -so in many cases the next operation causes a network error -rather than "not master". -The driver can see a "not master" error in the following scenario: - -#. The client discovers the primary. -#. The primary steps down. -#. Before the client checks the server and discovers the stepdown, - the application attempts an operation. -#. The client's connection pool is empty, - either because it has - never attempted an operation on this server, - or because all connections are in use by other threads. -#. The client creates a connection to the old primary. -#. The client attempts to write, or to read without the slaveOk bit, - and receives "not master". - -See `"not master" and "node is recovering"`_, -and the test scenario called -"parsing 'not master' and 'node is recovering' errors". - -What's the point of periodic monitoring? -'''''''''''''''''''''''''''''''''''''''' - -Why not just wait until a "not master" error or -"node is recovering" error informs the client that its -TopologyDescription is wrong? Or wait until server selection -fails to find a suitable server, and only scan all servers then? - -Periodic monitoring accomplishes three objectives: - -* Update each server's type, tags, and `round trip time`_. - Read preferences and the mongos selection algorithm - require this information remains up to date. -* Discover new secondaries so that secondary reads are evenly spread. -* Detect incremental changes to the replica set configuration, - so that the client remains connected to the set - even while it is migrated to a completely new set of hosts. - -If the application uses some servers very infrequently, -monitoring can also proactively detect state changes -(primary stepdown, server becoming unavailable) -that would otherwise cause future errors. - -Acknowledgments ---------------- - -Jeff Yemin's code for the Java driver 2.12, -and his patient explanation thereof, -is the major inspiration for this spec. -Mathias Stearn's beautiful design for replica set monitoring in mongos 2.6 -contributed as well. -Bernie Hackett gently oversaw the specification process. - -.. _connection string: http://docs.mongodb.org/manual/reference/connection-string/ - -Changes -------- - -2015-12-17: Require clients to compare (setVersion, electionId) tuples. - -2015-10-09: Specify electionID comparison method. - -2015-06-16: Added cooldownMS. - -2016-05-04: Added link to SDAM monitoring. - -2016-07-18: Replace mentions of the "Read Preferences Spec" with "Server Selection Spec", - and "secondaryAcceptableLatencyMS" with "localThresholdMS". - -.. [1] "localThresholdMS" was called "secondaryAcceptableLatencyMS" in the Read Preferences Spec, - before it was superseded by the Server Selection Spec. - -2016-07-21: Updated for Max Staleness support. - -2016-08-04: Explain better why clients use the hostnames in RS config, not URI. - -2016-08-31: Multi-threaded clients SHOULD use ismaster replies to update the topology - when they handshake application connections. - -2016-10-06: in updateRSWithoutPrimary the isMaster response's "primary" field - should be used to update the topology description, even if address != me. - -2016-10-29: Allow for idleWritePeriodMS to change someday. - -2016-11-01: "Unknown" is no longer the default TopologyType, the default is now - explicitly unspecified. Update instructions for setting the initial - TopologyType when running the spec tests. - -2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the -future. - -2017-02-28: Update "network error when reading or writing": timeout while -connecting does mark a server Unknown, unlike a timeout while reading or -writing. Justify the different behaviors, and also remove obsolete reference -to auto-retry. - -2017-06-13: Move socketCheckIntervalMS to Server Selection Spec. - -2017-08-01: Parse logicalSessionTimeoutMinutes from isMaster reply. - -2017-08-11: Clearer specification of "incompatible" logic. - -2017-09-01: Improved incompatibility error messages. - -2018-03-28: Specify that monitoring must not do mechanism negotiation or -authentication. + This specification has been converted to Markdown and renamed to + `server-discovery-and-monitoring.md `_. diff --git a/source/server-discovery-and-monitoring/server-monitoring.md b/source/server-discovery-and-monitoring/server-monitoring.md new file mode 100644 index 0000000000..8090f98ee9 --- /dev/null +++ b/source/server-discovery-and-monitoring/server-monitoring.md @@ -0,0 +1,1013 @@ +# Server Monitoring + +- Status: Accepted +- Minimum Server Version: 2.4 + +______________________________________________________________________ + +## Abstract + +This spec defines how a driver monitors a MongoDB server. In summary, the client monitors each server in the topology. +The scope of server monitoring is to provide the topology with updated ServerDescriptions based on hello or legacy hello +command responses. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +See the terms in the [main SDAM spec](server-discovery-and-monitoring.md). + +#### check + +The client checks a server by attempting to call hello or legacy hello on it, and recording the outcome. + +#### client + +A process that initiates a connection to a MongoDB server. This includes mongod and mongos processes in a replica set or +sharded cluster, as well as drivers, the shell, tools, etc. + +#### scan + +The process of checking all servers in the deployment. + +#### suitable + +A server is judged "suitable" for an operation if the client can use it for a particular operation. For example, a write +requires a standalone, primary, or mongos. Suitability is fully specified in the +[Server Selection Spec](../server-selection/server-selection.md). + +#### significant topology change + +A change in the server's state that is relevant to the client's view of the server, e.g. a change in the server's +replica set member state, or its replica set tags. In SDAM terms, a significant topology change on the server means the +client's ServerDescription is out of date. Standalones and mongos do not currently experience significant topology +changes but they may in the future. + +#### regular hello or legacy hello command + +A default `{hello: 1}` or legacy hello command where the server responds immediately. + +#### streamable hello or legacy hello command + +The hello or legacy hello command feature which allows the server to stream multiple replies back to the client. + +#### RTT + +Round trip time. The client's measurement of the duration of one hello or legacy hello call. The RTT is used to support +[localThresholdMS](../server-selection/server-selection.md#localthresholdms) from the Server Selection spec and +[timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms) from the +[Client Side Operations Timeout Spec](../client-side-operations-timeout/client-side-operations-timeout.md). + +#### FaaS + +A Function-as-a-Service (FaaS) environment like AWS Lambda. + + + +#### serverMonitoringMode + +The serverMonitoringMode option configures which server monitoring protocol to use. Valid modes are "stream", "poll", or +"auto". The default value MUST be "auto": + +- With "stream" mode, the client MUST use the streaming protocol when the server supports it or fall back to the polling + protocol otherwise. +- With "poll" mode, the client MUST use the polling protocol. +- With "auto" mode, the client MUST behave the same as "poll" mode when running on a FaaS platform or the same as + "stream" mode otherwise. The client detects that it's running on a FaaS platform via the same rules for generating + the `client.env` handshake metadata field in the + [MongoDB Handshake spec](../mongodb-handshake/handshake.md#client-env). + +Multi-threaded or asynchronous drivers MUST implement this option. See +[Why disable the streaming protocol on FaaS platforms like AWS Lambda?](#why-disable-the-streaming-protocol-on-faas-platforms-like-aws-lambda) +and [Why introduce a knob for serverMonitoringMode?](#why-introduce-a-knob-for-servermonitoringmode) + +### Monitoring + +The client monitors servers using the hello or legacy hello commands. In MongoDB 4.4+, a monitor uses the +[Streaming Protocol](#streaming-protocol) to continuously stream hello or legacy hello responses from the server. In +MongoDB \<= 4.2, a monitor uses the [Polling Protocol](#polling-protocol) pausing heartbeatFrequencyMS between +[checks](#check). Clients check servers sooner in response to certain events. + +If a [server API version](../versioned-api/versioned-api.md) is requested, then the driver must use hello for +monitoring. If a server API version is not requested, the initial handshake using the legacy hello command must include +`helloOk: true`. If the response contains `helloOk: true`, then the driver must use the `hello` command for monitoring. +If the response does not contain `helloOk: true`, then the driver must use the legacy hello command for monitoring. + +The socket used to check a server MUST use the same +[connectTimeoutMS](https://www.mongodb.com/docs/manual/reference/connection-string/) as regular sockets. Multi-threaded +clients SHOULD set monitoring sockets' socketTimeoutMS to the connectTimeoutMS. (See +[socket timeout for monitoring is connectTimeoutMS](#socket-timeout-for-monitoring-is-connecttimeoutms). Drivers MAY let +users configure the timeouts for monitoring sockets separately if necessary to preserve backwards compatibility.) + +The client begins monitoring a server when: + +- ... the client is initialized and begins monitoring each seed. See + [initial servers](server-discovery-and-monitoring.md#initial-servers). +- ... [updateRSWithoutPrimary](server-discovery-and-monitoring.md#updateRSWithoutPrimary) or + [updateRSFromPrimary](server-discovery-and-monitoring.md#updateRSFromPrimary) discovers new replica set members. + +The following subsections specify how monitoring works, first in multi-threaded or asynchronous clients, and second in +single-threaded clients. This spec provides detailed requirements for monitoring because it intends to make all drivers +behave consistently. + +#### Multi-threaded or asynchronous monitoring + +##### Servers are monitored in parallel + +All servers' monitors run independently, in parallel: If some monitors block calling hello or legacy hello over slow +connections, other monitors MUST proceed unimpeded. + +The natural implementation is a thread per server, but the decision is left to the implementer. (See +[thread per server](#thread-per-server).) + +##### Servers are monitored with dedicated sockets + +[A monitor SHOULD NOT use the client's regular connection pool](#a-monitor-should-not-use-the-clients-regular-connection-pool) +to acquire a socket; it uses a dedicated socket that does not count toward the pool's maximum size. + +Drivers MUST NOT authenticate on sockets used for monitoring nor include SCRAM mechanism negotiation (i.e. +`saslSupportedMechs`), as doing so would make monitoring checks more expensive for the server. + +##### Servers are checked periodically + +Each monitor [checks](#check) its server and notifies the client of the outcome so the client can update the +TopologyDescription. + +After each check, the next check SHOULD be scheduled [heartbeatFrequencyMS](#heartbeatfrequencyms) later; a check MUST +NOT run while a previous check is still in progress. + +##### Requesting an immediate check + +At any time, the client can request that a monitor check its server immediately. (For example, after a "not writable +primary" error. See [error handling](server-discovery-and-monitoring.md#error-handling).) If the monitor is sleeping +when this request arrives, it MUST wake and check as soon as possible. If a hello or legacy hello call is already in +progress, the request MUST be ignored. If the previous check ended less than +[minHeartbeatFrequencyMS](#minheartbeatfrequencyms) ago, the monitor MUST sleep until the minimum delay has passed, then +check the server. + +##### Application operations are unblocked when a server is found + +Each time a check completes, threads waiting for a [suitable](#suitable) server are unblocked. Each unblocked thread +MUST proceed if the new TopologyDescription now contains a suitable server. + +##### Clients update the topology from each handshake + +When a monitor check creates a new connection, the [connection handshake](../mongodb-handshake/handshake.md) response +MUST be used to satisfy the check and update the topology. + +When a client successfully calls hello or legacy hello to handshake a new connection for application operations, it +SHOULD use the hello or legacy hello reply to update the ServerDescription and TopologyDescription, the same as with a +hello or legacy hello reply on a monitoring socket. If the hello or legacy hello call fails, the client SHOULD mark the +server Unknown and update its TopologyDescription, the same as a failed server check on monitoring socket, unless the +connection pool has added the `SystemOverloadedError` label to the error. + +##### Clients use the streaming protocol when supported + +When a monitor discovers that the server supports the streamable hello or legacy hello command and the client does not +have [streaming disabled](#streaming-disabled), it MUST use the [streaming protocol](#streaming-protocol). + +#### Single-threaded monitoring + + + +##### cooldownMS + +After a single-threaded client gets a network error trying to [check](#check) a server, the client skips re-checking the +server until cooldownMS has passed. + +This avoids spending connectTimeoutMS on each unavailable server during each scan. + +This value MUST be 5000 ms, and it MUST NOT be configurable. + + + +##### Scanning + +Single-threaded clients MUST [scan](#scan) all servers synchronously, inline with regular application operations. Before +each operation, the client checks if [heartbeatFrequencyMS](#heartbeatfrequencyms) has passed since the previous scan +ended, or if the topology is marked "stale"; if so it scans all the servers before selecting a server and performing the +operation. + +Selection failure triggers an immediate scan. When a client that uses single-threaded monitoring fails to select a +suitable server for any operation, it [scans](#scan) the servers, then attempts selection again, to see if the scan +discovered suitable servers. It repeats, waiting [minHeartbeatFrequencyMS](#minheartbeatfrequencyms) after each scan, +until a timeout. + +##### Scanning order + +If the topology is a replica set, the client attempts to contact the primary as soon as possible to get an authoritative +list of members. Otherwise, the client attempts to check all members it knows of, in order from the least-recently to +the most-recently checked. + +When all servers have been checked the scan is complete. New servers discovered **during** the scan MUST be checked +before the scan is complete. Sometimes servers are removed during a scan so they are not checked, depending on the order +of events. + +The scanning order is expressed in this pseudocode: + +```text +scanStartTime = now() +# You'll likely need to convert units here. +beforeCoolDown = scanStartTime - cooldownMS + +while true: + serversToCheck = all servers with lastUpdateTime before scanStartTime + + remove from serversToCheck any Unknowns with lastUpdateTime > beforeCoolDown + + if no serversToCheck: + # This scan has completed. + break + + if a server in serversToCheck is RSPrimary: + check it + else if there is a PossiblePrimary: + check it + else if any servers are not of type Unknown or RSGhost: + check the one with the oldest lastUpdateTime + if several servers have the same lastUpdateTime, choose one at random + else: + check the Unknown or RSGhost server with the oldest lastUpdateTime + if several servers have the same lastUpdateTime, choose one at random +``` + +This algorithm might be better understood with an example: + +1. The client is configured with one seed and TopologyType Unknown. It begins a scan. +2. When it checks the seed, it discovers a secondary. +3. The secondary's hello or legacy hello response includes the "primary" field with the address of the server that the + secondary thinks is primary. +4. The client creates a ServerDescription with that address, type PossiblePrimary, and lastUpdateTime "infinity ago". + (See [updateRSWithoutPrimary](server-discovery-and-monitoring.md#updateRSWithoutPrimary).) +5. On the next iteration, there is still no RSPrimary, so the new PossiblePrimary is the top-priority server to check. +6. The PossiblePrimary is checked and replaced with an RSPrimary. The client has now acquired an authoritative host + list. Any new hosts in the list are added to the TopologyDescription with lastUpdateTime "infinity ago". (See + [updateRSFromPrimary](server-discovery-and-monitoring.md#updateRSFromPrimary).) +7. The client continues scanning until all known hosts have been checked. + +Another common case might be scanning a pool of mongoses. When the client first scans its seed list, they all have the +default lastUpdateTime "infinity ago", so it scans them in random order. This randomness provides some load-balancing if +many clients start at once. A client's subsequent scans of the mongoses are always in the same order, since their +lastUpdateTimes are always in the same order by the time a scan ends. + +##### Handling of backpressure labels + +Because the scan may occur on an authenticated connection in single-threaded monitors, the server may apply backpressure +by failing the command with a `SystemOverloadedError` label. The driver MUST not close the connection when this label is +encountered. + +#### minHeartbeatFrequencyMS + +If a client frequently rechecks a server, it MUST wait at least minHeartbeatFrequencyMS milliseconds since the previous +check ended, to avoid pointless effort. This value MUST be 500 ms, and it MUST NOT be configurable (no knobs). + +#### heartbeatFrequencyMS + +The interval between server [checks](#check), counted from the end of the previous check until the beginning of the next +one. + +For multi-threaded and asynchronous drivers it MUST default to 10 seconds and MUST be configurable. For single-threaded +drivers it MUST default to 60 seconds and MUST be configurable. It MUST be called heartbeatFrequencyMS unless this +breaks backwards compatibility. + +For both multi- and single-threaded drivers, the driver MUST NOT permit users to configure it less than +minHeartbeatFrequencyMS (500ms). + +(See [heartbeatFrequencyMS in the main SDAM spec](server-discovery-and-monitoring.md#heartbeatFrequencyMS).) + +### Awaitable hello or legacy hello Server Specification + +As of MongoDB 4.4 the hello or legacy hello command can wait to reply until there is a topology change or a maximum time +has elapsed. Clients opt in to this "awaitable hello" feature by passing new parameters "topologyVersion" and +"maxAwaitTimeMS" to the hello or legacy hello commands. Exhaust support has also been added, which clients can enable in +the usual manner by setting the [OP_MSG exhaustAllowed flag](../message/OP_MSG.md#exhaustAllowed). + +Clients use the awaitable hello feature as the basis of the streaming heartbeat protocol to learn much sooner about +stepdowns, elections, reconfigs, and other events. + +#### topologyVersion + +A server that supports awaitable hello or legacy hello includes a "topologyVersion" field in all hello or legacy hello +replies and State Change Error replies. The topologyVersion is a subdocument with two fields, "processId" and "counter": + +```typescript +{ + topologyVersion: {processId: , counter: }, + ( ... other fields ...) +} +``` + +##### processId + +An ObjectId maintained in memory by the server. It is reinitialized by the server using the standard ObjectId logic each +time this server process starts. + +##### counter + +An int64 State change counter, maintained in memory by the server. It begins at 0 when the server starts, and it is +incremented whenever there is a significant topology change. + +#### maxAwaitTimeMS + +To enable awaitable hello or legacy hello, the client includes a new int64 field "maxAwaitTimeMS" in the hello or legacy +hello request. This field determines the maximum duration in milliseconds a server will wait for a significant topology +change before replying. + +#### Feature Discovery + +To discover if the connected server supports awaitable hello or legacy hello, a client checks the most recent hello or +legacy hello command reply. If the reply includes "topologyVersion" then the server supports awaitable hello or legacy +hello. + +#### Awaitable hello or legacy hello Protocol + +To initiate an awaitable hello or legacy hello command, the client includes both maxAwaitTimeMS and topologyVersion in +the request, for example: + +```typescript +{ + hello: 1, + maxAwaitTimeMS: 10000, + topologyVersion: {processId: , counter: }, + ( ... other fields ...) +} +``` + +Clients MAY additionally set the [OP_MSG exhaustAllowed flag](../message/OP_MSG.md#exhaustAllowed) to enable streaming +hello or legacy hello. With streaming hello or legacy hello, the server MAY send multiple hello or legacy hello +responses without waiting for further requests. + +A server that implements the new protocol follows these rules: + +- Always include the server's topologyVersion in hello, legacy hello, and State Change Error replies. +- If the request includes topologyVersion without maxAwaitTimeMS or vice versa, return an error. +- If the request omits topologyVersion and maxAwaitTimeMS, reply immediately. +- If the request includes topologyVersion and maxAwaitTimeMS, then reply immediately if the server's + topologyVersion.processId does not match the request's, otherwise reply when the server's topologyVersion.counter is + greater than the request's, or maxAwaitTimeMS elapses, whichever comes first. +- Following the [OP_MSG spec](../message/OP_MSG.md), if the request omits the exhaustAllowed flag, the server MUST NOT + set the moreToCome flag on the reply. If the request's exhaustAllowed flag is set, the server MAY set the moreToCome + flag on the reply. If the server sets moreToCome, it MUST continue streaming replies without awaiting further + requests. Between replies it MUST wait until the server's topologyVersion.counter is incremented or maxAwaitTimeMS + elapses, whichever comes first. If the reply includes `ok: 0` the server MUST NOT set the moreToCome flag. +- On a topology change that changes the horizon parameters, the server will close all application connections. + +Example awaitable hello conversation: + +| Client | Server | +| ---------------------------------------------------------- | ------------------------------- | +| hello handshake -> | | +| | \<- reply with topologyVersion | +| hello as OP_MSG with maxAwaitTimeMS and topologyVersion -> | | +| | wait for change or timeout | +| | \<- OP_MSG with topologyVersion | +| ... | | + +Example streaming hello conversation (awaitable hello with exhaust): + +| Client | Server | +| --------------------------------------------------------------------------- | ---------------------------------------------- | +| hello handshake -> | | +| | \<- reply with topologyVersion | +| hello as OP_MSG with exhaustAllowed, maxAwaitTimeMS, and topologyVersion -> | | +| | wait for change or timeout | +| | \<- OP_MSG with moreToCome and topologyVersion | +| | wait for change or timeout | +| | \<- OP_MSG with moreToCome and topologyVersion | +| | ... | +| | \<- OP_MSG without moreToCome | +| ... | | + +### Streaming Protocol + +The streaming protocol is used to monitor MongoDB 4.4+ servers and optimally reduces the time it takes for a client to +discover server state changes. Multi-threaded or asynchronous drivers MUST use the streaming protocol when connected to +a server that supports the awaitable hello or legacy hello commands. This protocol requires an extra thread and an extra +socket for each monitor to perform RTT calculations. + +#### Streaming disabled + +The streaming protocol MUST be disabled when either: + +- the client is configured with serverMonitoringMode=poll, or +- the client is configured with serverMonitoringMode=auto and a FaaS platform is detected, or +- the server does not support streaming (eg MongoDB < 4.4). + +When the streaming protocol is disabled the client MUST use the [polling protocol](#polling-protocol) and MUST NOT start +an extra thread or connection for [Measuring RTT](#measuring-rtt). + +See +[Why disable the streaming protocol on FaaS platforms like AWS Lambda?](#why-disable-the-streaming-protocol-on-faas-platforms-like-aws-lambda). + +#### Streaming hello or legacy hello + +The streaming hello or legacy hello protocol uses awaitable hello or legacy hello with the OP_MSG exhaustAllowed flag to +continuously stream hello or legacy hello responses from the server. Drivers MUST set the OP_MSG exhaustAllowed flag +with the awaitable hello or legacy hello command and MUST process each hello or legacy hello response. (I.e., they MUST +process responses strictly in the order they were received.) + +A client follows these rules when processing the hello or legacy hello exhaust response: + +- If the response indicates a command error, or a network error or timeout occurs, the client MUST close the connection + and restart the monitoring protocol on a new connection. (See + [Network or command error during server check](#network-or-command-error-during-server-check).) +- If the response is successful (includes "ok:1") and includes the OP_MSG moreToCome flag, then the client begins + reading the next response. +- If the response is successful (includes "ok:1") and does not include the OP_MSG moreToCome flag, then the client + initiates a new awaitable hello or legacy hello with the topologyVersion field from the previous response. + +#### Socket timeout + +Clients MUST use connectTimeoutMS as the timeout for the connection handshake. When connectTimeoutMS=0, the timeout is +unlimited and MUST remain unlimited for awaitable hello and legacy hello replies. Otherwise, connectTimeoutMS is +non-zero and clients MUST use connectTimeoutMS + heartbeatFrequencyMS as the timeout for awaitable hello and legacy +hello replies. + +#### Measuring RTT + +When using the streaming protocol, clients MUST issue a hello or legacy hello command to each server to measure RTT +every heartbeatFrequencyMS. The RTT command MUST be run on a dedicated connection to each server. Clients MUST NOT use +dedicated connections to measure RTT when the streaming protocol is not used. (See +[Monitors MUST use a dedicated connection for RTT commands](#monitors-must-use-a-dedicated-connection-for-rtt-commands).) + +Clients MUST update the RTT from the hello or legacy hello duration of the initial connection handshake. Clients MUST +NOT update RTT based on streaming hello or legacy hello responses. + +Clients MUST ignore the response to the hello or legacy hello command when measuring RTT. Errors encountered when +running a hello or legacy hello command MUST NOT update the topology. (See +[Why don't clients mark a server unknown when an RTT command fails?](#why-dont-clients-mark-a-server-unknown-when-an-rtt-command-fails)) + +Clients MUST track the minimum RTT out of the (at most) last 10 samples. Clients MUST report the minimum RTT as 0 until +at least 2 samples have been gathered. + +When constructing a ServerDescription from a streaming hello or legacy hello response, clients MUST set the average and +minimum round trip times from the RTT task as the "roundTripTime" and "minRoundTripTime" fields, respectively. + +See the pseudocode in the [RTT thread](#rtt-thread) section for an example implementation. + +#### SDAM Monitoring + +Clients MUST publish a ServerHeartbeatStartedEvent before attempting to read the next hello or legacy hello exhaust +response. (See +[Why must streaming hello or legacy hello clients publish ServerHeartbeatStartedEvents?](#why-must-streaming-hello-or-legacy-hello-clients-publish-serverheartbeatstartedevents)) + +Clients MUST NOT publish any events when running an RTT command. (See +[Why don't streaming hello or legacy hello clients publish events for RTT commands?](#why-dont-streaming-hello-or-legacy-hello-clients-publish-events-for-rtt-commands)) + +#### Heartbeat frequency + +In the polling protocol, a client sleeps between each hello or legacy hello check (for at least minHeartbeatFrequencyMS +and up to heartbeatFrequencyMS). In the streaming protocol, after processing an "ok:1" hello or legacy hello response, +the client MUST NOT sleep and MUST begin the next check immediately. + +Clients MUST set [maxAwaitTimeMS](#maxawaittimems) to heartbeatFrequencyMS. + +#### hello or legacy hello Cancellation + +When a client is closed, clients MUST cancel all hello and legacy hello checks; a monitor blocked waiting for the next +streaming hello or legacy hello response MUST be interrupted such that threads may exit promptly without waiting +maxAwaitTimeMS. + +When a client marks a server Unknown from +[Network error when reading or writing](server-discovery-and-monitoring.md#network-error-when-reading-or-writing), +clients MUST cancel the hello or legacy hello check on that server and close the current monitoring connection. (See +[Drivers cancel in-progress monitor checks](#drivers-cancel-in-progress-monitor-checks).) + +### Polling Protocol + +The polling protocol is used to monitor MongoDB < 4.4 servers or when streaming is disabled. The client [checks](#check) +a server with a hello or legacy hello command and then sleeps for heartbeatFrequencyMS before running another check. + +### Marking the connection pool as ready (CMAP only) + +When a monitor completes a successful check against a server, it MUST mark the connection pool for that server as +"ready", and doing so MUST be synchronized with the update to the topology (e.g. by marking the pool as ready in +onServerDescriptionChanged). This is required to ensure a server does not get selected while its pool is still paused. +See the [Connection Pool](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool) +definition in the CMAP specification for more details on marking the pool as "ready". + +### Error handling + +#### Network or command error during server check + +When a server [check](#check) fails due to a network error (including a network timeout) or a command error (`ok: 0`), +the client MUST follow these steps: + +1. Close the current monitoring connection. + +2. Mark the server Unknown. + +3. Clear the connection pool for the server (See + [Clear the connection pool on both network and command errors](#clear-the-connection-pool-on-both-network-and-command-errors)). + For CMAP compliant drivers, clearing the pool MUST be synchronized with marking the server as Unknown (see + [Why synchronize clearing a server's pool with updating the topology?](server-discovery-and-monitoring.md#why-synchronize-clearing-a-servers-pool-with-updating-the-topology)). + If this was a network timeout error, then the pool MUST be cleared with interruptInUseConnections = true (see + [Why does the pool need to support closing in use connections as part of its clear logic?](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#why-does-the-pool-need-to-support-interrupting-in-use-connections-as-part-of-its-clear-logic)) + +4. If this was a network error and the server was in a known state before the error, the client MUST NOT sleep and MUST + begin the next check immediately. (See + [retry hello or legacy hello calls once](#retry-hello-or-legacy-hello-calls-once) and + [JAVA-1159](https://jira.mongodb.org/browse/JAVA-1159).) + +5. Otherwise, wait for heartbeatFrequencyMS (or minHeartbeatFrequencyMS if a check is requested) before restarting the + monitoring protocol on a new connection. + + - Note that even in the streaming protocol, a monitor in this state will wait for an application operation to request + an immediate check or for the heartbeatFrequencyMS timeout to expire before beginning the next check. + +See the pseudocode in the `Monitor thread` section. + +Note that this rule applies only to server checks during monitoring. It does *not* apply when multi-threaded +[clients update the topology from each handshake](#clients-update-the-topology-from-each-handshake). + +### Implementation notes + +This section intends to provide generous guidance to driver authors. It is complementary to the reference +implementations. Words like "should", "may", and so on are used more casually here. + +#### Monitor thread + +Most platforms can use an event object to control the monitor thread. The event API here is assumed to be like the +standard [Python Event](https://docs.python.org/2/library/threading.html#event-objects). +[heartbeatFrequencyMS](#heartbeatfrequencyms) is configurable, [minHeartbeatFrequencyMS](#minheartbeatfrequencyms) is +always 500 milliseconds: + +```python +class Monitor(Thread): + def __init__(): + # Monitor options: + serverAddress = serverAddress + connectTimeoutMS = connectTimeoutMS + heartbeatFrequencyMS = heartbeatFrequencyMS + minHeartbeatFrequencyMS = 500 + stableApi = stableApi + if serverMonitoringMode == "stream": + streamingEnabled = True + elif serverMonitoringMode == "poll": + streamingEnabled = False + else: # serverMonitoringMode == "auto" + streamingEnabled = not isFaas() + + # Internal Monitor state: + connection = Null + # Server API versioning implies that the server supports hello. + helloOk = stableApi != Null + description = default ServerDescription + lock = Mutex() + rttMonitor = RttMonitor(serverAddress, stableApi) + + def run(): + while this monitor is not stopped: + previousDescription = description + try: + description = checkServer(previousDescription) + except CheckCancelledError: + if this monitor is stopped: + # The client was closed. + return + # The client marked this server Unknown and cancelled this + # check during "Network error when reading or writing". + # Wait before running the next check. + wait() + continue + + with client.lock: + topology.onServerDescriptionChanged(description, connection pool for server) + if description.error != Null: + # Clear the connection pool only after the server description is set to Unknown. + clear(interruptInUseConnections: isNetworkTimeout(description.error)) connection pool for server + + # Immediately proceed to the next check if the previous response + # was successful and included the topologyVersion field, or the + # previous response included the moreToCome flag, or the server + # has just transitioned to Unknown from a network error. + serverSupportsStreaming = description.type != Unknown and description.topologyVersion != Null + connectionIsStreaming = connection != Null and connection.moreToCome + transitionedWithNetworkError = isNetworkError(description.error) and previousDescription.type != Unknown + if streamingEnabled and serverSupportsStreaming and not rttMonitor.started: + # Start the RttMonitor. + rttMonitor.run() + if (streamingEnabled and (serverSupportsStreaming or connectionIsStreaming)) or transitionedWithNetworkError: + continue + + wait() + + def setUpConnection(): + # Take the mutex to avoid a data race because this code writes to the connection field and a concurrent + # cancelCheck call could be reading from it. + with lock: + # Server API versioning implies that the server supports hello. + helloOk = stableApi != Null + connection = new Connection(serverAddress) + if connectTimeoutMS != 0: + set connection timeout to connectTimeoutMS + + # Do any potentially blocking operations after releasing the mutex. + create the socket and perform connection handshake + + def checkServer(previousDescription): + try: + # The connection is null if this is the first check. It's closed if there was an error during the previous + # check or the previous check was cancelled. + + if helloOk: + helloCommand = hello + else + helloCommand = legacy hello + + if not connection or connection.isClosed(): + setUpConnection() + rttMonitor.addSample(connection.handshakeDuration) + response = connection.handshakeResponse + elif connection.moreToCome: + response = read next helloCommand exhaust response + elif streamingEnabled and previousDescription.topologyVersion: + # Initiate streaming hello or legacy hello + if connectTimeoutMS != 0: + set connection timeout to connectTimeoutMS+heartbeatFrequencyMS + response = call {helloCommand: 1, helloOk: True, topologyVersion: previousDescription.topologyVersion, maxAwaitTimeMS: heartbeatFrequencyMS} + else: + # The server does not support topologyVersion or streamingEnabled=False. + response = call {helloCommand: 1, helloOk: True} + + # If the server supports hello, then response.helloOk will be true + # and hello will be used for subsequent monitoring commands. + # If the server does not support hello, then response.helloOk will be undefined + # and legacy hello will be used for subsequent monitoring commands. + helloOk = response.helloOk + + return ServerDescription(response, rtt=rttMonitor.average(), ninetiethPercentileRtt=rttMonitor.ninetiethPercentile()) + except Exception as exc: + close connection + rttMonitor.reset() + return ServerDescription(type=Unknown, error=exc) + + def wait(): + start = gettime() + + # Can be awakened by requestCheck(). + event.wait(heartbeatFrequencyMS) + event.clear() + + waitTime = gettime() - start + if waitTime < minHeartbeatFrequencyMS: + # Cannot be awakened. + sleep(minHeartbeatFrequencyMS - waitTime) +``` + +[Requesting an immediate check](#requesting-an-immediate-check): + +```python +def requestCheck(): + event.set() +``` + +[hello or legacy hello Cancellation](#hello-or-legacy-hello-cancellation): + +```python +def cancelCheck(): + # Take the mutex to avoid reading the connection value while setUpConnection is writing to it. + # Copy the connection value in the lock but do the actual cancellation outside. + with lock: + tempConnection = connection + + if tempConnection: + interrupt connection read + close tempConnection +``` + +#### RTT thread + +The requirements in the [Measuring RTT](#measuring-rtt) section can be satisfied with an additional thread that +periodically runs the hello or legacy hello command on a dedicated connection, for example: + +```python +class RttMonitor(Thread): + def __init__(): + # Options: + serverAddress = serverAddress + connectTimeoutMS = connectTimeoutMS + heartbeatFrequencyMS = heartbeatFrequencyMS + stableApi = stableApi + + # Internal state: + connection = Null + # Server API versioning implies that the server supports hello. + helloOk = stableApi != Null + lock = Mutex() + movingAverage = MovingAverage() + # Track the min RTT seen in the most recent 10 samples. + recentSamples = deque(maxlen=10) + + def reset(): + with lock: + movingAverage.reset() + recentSamples.clear() + + def addSample(rtt): + with lock: + movingAverage.update(rtt) + recentSamples.append(rtt) + + def average(): + with lock: + return movingAverage.get() + + def min(): + with lock: + # Need at least 2 RTT samples. + if len(recentSamples) < 2: + return 0 + return min(recentSamples) + + def run(): + while this monitor is not stopped: + try: + rtt = pingServer() + addSample(rtt) + except Exception as exc: + # Don't call reset() here. The Monitor thread is responsible + # for resetting the average RTT. + close connection + connection = Null + helloOk = stableApi != Null + + # Can be awakened when the client is closed. + event.wait(heartbeatFrequencyMS) + event.clear() + + def setUpConnection(): + # Server API versioning implies that the server supports hello. + helloOk = stableApi != Null + connection = new Connection(serverAddress) + if connectTimeoutMS != 0: + set connection timeout to connectTimeoutMS + perform connection handshake + + def pingServer(): + if helloOk: + helloCommand = hello + else + helloCommand = legacy hello + + if not connection: + setUpConnection() + return RTT of the connection handshake + + start = time() + response = call {helloCommand: 1, helloOk: True} + rtt = time() - start + helloOk = response.helloOk + return rtt +``` + +## Design Alternatives + +### Alternating hello or legacy hello to check servers and RTT without adding an extra connection + +The streaming hello or legacy hello protocol is optimal in terms of latency; clients are always blocked waiting for the +server to stream updated hello or legacy hello information, they learn of server state changes as soon as possible. +However, streaming hello or legacy hello has two downsides: + +1. Streaming hello or legacy hello requires a new connection to each server to calculate the RTT. +2. Streaming hello or legacy hello requires a new thread (or threads) to calculate the RTT of each server. + +To address these concerns we designed the alternating hello or legacy hello protocol. This protocol would have +alternated between awaitable hello or legacy hello and regular hello or legacy hello. The awaitable hello or legacy +hello replaces the polling protocol's client side sleep and allows the client to receive updated hello or legacy hello +responses sooner. The regular hello or legacy hello allows the client to maintain accurate RTT calculations without +requiring any extra threads or sockets. + +We reject this design because streaming hello or legacy hello is strictly better at reducing the client's +time-to-recovery. We determined that one extra connection per server per MongoClient is reasonable for all drivers. +Applications that upgrade may see a modest increase in connections and memory usage on the server. We don't expect this +increase to be problematic; however, we have several projects planned for future MongoDB releases to make the streaming +hello or legacy hello protocol cheaper server-side which should mitigate the cost of the extra monitoring connections. + +### Use TCP smoothed round-trip time instead of measuring RTT explicitly + +TCP sockets internally maintain a "smoothed round-trip time" or SRTT. Drivers could use this SRTT instead of measuring +RTT explicitly via hello or legacy hello commands. The server could even include this value on all hello or legacy hello +responses. We reject this idea for a few reasons: + +- Not all programming languages have an API to access the TCP socket's RTT. +- On Windows, RTT access requires Admin privileges. +- TCP's SRTT would likely differ substantially from RTT measurements in the current protocol. For example, the SRTT can + be reset on [retransmission timeouts](https://tools.ietf.org/html/rfc2988#section-5). + +## Rationale + +### Thread per server + +Mongos uses a monitor thread per replica set, rather than a thread per server. A thread per server is impractical if +mongos is monitoring a large number of replica sets. But a driver only monitors one. + +In mongos, threads trying to do reads and writes join the effort to scan the replica set. Such threads are more likely +to be abundant in mongos than in drivers, so mongos can rely on them to help with monitoring. + +In short: mongos has different scaling concerns than a multi-threaded or asynchronous driver, so it allocates threads +differently. + +### Socket timeout for monitoring is connectTimeoutMS + +When a client waits for a server to respond to a connection, the client does not know if the server will respond +eventually or if it is down. Users can help the client guess correctly by supplying a reasonable connectTimeoutMS for +their network: on some networks a server is probably down if it hasn't responded in 10 ms, on others a server might +still be up even if it hasn't responded in 10 seconds. + +The socketTimeoutMS, on the other hand, must account for both network latency and the operation's duration on the +server. Applications should typically set a very long or infinite socketTimeoutMS so they can wait for long-running +MongoDB operations. + +Multi-threaded clients use distinct sockets for monitoring and for application operations. A socket used for monitoring +does two things: it connects and calls hello or legacy hello. Both operations are fast on the server, so only network +latency matters. Thus both operations SHOULD use connectTimeoutMS, since that is the value users supply to help the +client guess if a server is down, based on users' knowledge of expected latencies on their networks. + +### A monitor SHOULD NOT use the client's regular connection pool + +If a multi-threaded driver's connection pool enforces a maximum size and monitors use sockets from the pool, there are +two bad options: either monitors compete with the application for sockets, or monitors have the exceptional ability to +create sockets even when the pool has reached its maximum size. The former risks starving the monitor. The latter is +more complex than it is worth. (A lesson learned from PyMongo 2.6's pool, which implemented this option.) + +Since this rule is justified for drivers that enforce a maximum pool size, this spec recommends that all drivers follow +the same rule for the sake of consistency. + +### Monitors MUST use a dedicated connection for RTT commands + +When using the streaming protocol, a monitor needs to maintain an extra dedicated connection to periodically update its +average round trip time in order to support [localThresholdMS](../server-selection/server-selection.md#localthresholdms) +from the Server Selection spec. + +It could pop a connection from its regular pool, but we rejected this option for a few reasons: + +- Under contention the RTT task may block application operations from completing in a timely manner. +- Under contention the application may block the RTT task from completing in a timely manner. +- Under contention the RTT task may often result in an extra connection anyway because the pool creates new connections + under contention up to maxPoolSize. +- This would be inconsistent with the rule that a monitor SHOULD NOT use the client's regular connection pool. + +The client could open and close a new connection for each RTT check. We rejected this design, because if we ping every +heartbeatFrequencyMS (default 10 seconds) then the cost to the client and the server of creating and destroying the +connection might exceed the cost of keeping a dedicated connection open. + +Instead, the client must use a dedicated connection reserved for RTT commands. Despite the cost of the additional +connection per server, we chose this option as the safest and least likely to result in surprising behavior under load. + +### Monitors MUST use the hello or legacy hello command to measure RTT + +In the streaming protocol, clients could use the "ping", "hello", or legacy hello commands to measure RTT. This spec +chooses "hello" or legacy hello for consistency with the polling protocol as well as consistency with the initial RTT +provided the connection handshake which also uses the hello or legacy hello commands. Additionally, mongocryptd does not +allow the ping command but does allow hello or legacy hello. + +### Why not use `awaitedTimeMS` in the server response to calculate RTT in the streaming protocol? + +One approach to calculating RTT in the streaming protocol would be to have the server return an `awaitedTimeMS` in its +`hello` or legacy hello response. A driver could then determine the RTT by calculating the difference between the +initial request, or last response, and the `awaitedTimeMS`. + +We rejected this design because of a number of issue with the unreliability of clocks in distributed systems. Clocks +skew between local and remote system clocks. This approach mixes two notions of time: the local clock times the whole +operation while the remote clock times the wait. This means that if these clocks tick at different rates, or there are +anomalies like clock changes, you will get bad results. To make matters worse, you will be comparing times from multiple +servers that could each have clocks ticking at different rates. This approach will bias toward servers with the fastest +ticking clock, since it will seem like it spends the least time on the wire. + +Additionally, systems using NTP will experience clock "slew". ntpd "slews" time by up to 500 parts-per-million to have +the local time gradually approach the "true" time without big jumps - over a 10 second window that means a 5ms +difference. If both sides are slewing in opposite directions, that can result in an effective difference of 10ms. Both +of these times are close enough to [localThresholdMS](../server-selection/server-selection.md#localthresholdms) to +significantly affect which servers are viable in NEAREST calculations. + +Ensuring that all measurements use the same clock obviates the need for a more complicated solution, and mitigates the +above mentioned concerns. + +### Why don't clients mark a server unknown when an RTT command fails? + +In the streaming protocol, clients use the hello or legacy hello command on a dedicated connection to measure a server's +RTT. However, errors encountered when running the RTT command MUST NOT mark a server Unknown. We reached this decision +because the dedicated RTT connection does not come from a connection pool and thus does not have a generation number +associated with it. Without a generation number we cannot handle errors from the RTT command without introducing race +conditions. Introducing such a generation number would add complexity to this design without much benefit. It is safe to +ignore these errors because the Monitor will soon discover the server's state regardless (either through an updated +streaming response, an error on the streaming connection, or by handling an error on an application connection). + +### Drivers cancel in-progress monitor checks + +When an application operation fails with a non-timeout network error, drivers cancel that monitor's in-progress check. + +We assume that a non-timeout network error on one application connection implies that all other connections to that +server are also bad. This means that it is redundant to continue reading on the current monitoring connection. Instead, +we cancel the current monitor check, close the monitoring connection, and start a new check soon. Note that we rely on +the connection/pool generation number checking to avoid races and ensure that the monitoring connection is only closed +once. + +This approach also handles the rare case where the client sees a network error on an application connection but the +monitoring connection is still healthy. If we did not cancel the monitor check in this scenario, then the server would +remain in the Unknown state until the next hello or legacy hello response (up to maxAwaitTimeMS). A potential real world +example of this behavior is when Azure closes an idle connection in the application pool. + +### Retry hello or legacy hello calls once + +A monitor's connection to a server is long-lived and used only for hello or legacy hello calls. So if a server has +responded in the past, a network error on the monitor's connection means that there was a network glitch, or a server +restart since the last check, or that the server is truly down. To handle the case that the server is truly down, the +monitor makes the server unselectable by marking it Unknown. To handle the case of a transient network glitch or +restart, the monitor immediately runs the next check without waiting. + +### Clear the connection pool on both network and command errors + +A monitor clears the connection pool when a server check fails with a network or command error +([Network or command error during server check](#network-or-command-error-during-server-check)). When the check fails +with a network error it is likely that all connections to that server are also closed. (See +[JAVA-1252](https://jira.mongodb.org/browse/JAVA-1252)). When the check fails with a network timeout error, a monitor +MUST set interruptInUseConnections to true. See, +[Why does the pool need to support closing in use connections as part of its clear logic?](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#why-does-the-pool-need-to-support-interrupting-in-use-connections-as-part-of-its-clear-logic). + +When the server is shutting down, it may respond to hello or legacy hello commands with ShutdownInProgress errors before +closing connections. In this case, the monitor clears the connection pool because all connections will be closed soon. +Other command errors are unexpected but are handled identically. + +### Why must streaming hello or legacy hello clients publish ServerHeartbeatStartedEvents? + +The [SDAM Monitoring spec](server-discovery-and-monitoring-logging-and-monitoring.md#heartbeats) guarantees that every +ServerHeartbeatStartedEvent has either a correlating ServerHeartbeatSucceededEvent or ServerHeartbeatFailedEvent. This +is consistent with Command Monitoring on exhaust cursors where the driver publishes a fake CommandStartedEvent before +reading the next getMore response. + +### Why don't streaming hello or legacy hello clients publish events for RTT commands? + +In the streaming protocol, clients MUST NOT publish any events (server, topology, command, CMAP, etc..) when running an +RTT command. We considered introducing new RTT events (ServerRTTStartedEvent, ServerRTTSucceededEvent, +ServerRTTFailedEvent) but it's not clear that there is a demand for this. Applications can still monitor changes to a +server's RTT by listening to TopologyDescriptionChangedEvents. + +### What is the purpose of the "awaited" field on server heartbeat events? + +ServerHeartbeatSucceededEvents published from awaitable hello or legacy hello responses will regularly have 10 second +durations. The spec introduces the "awaited" field on server heartbeat events so that applications can differentiate a +slow heartbeat in the polling protocol from a normal awaitable hello or legacy hello heartbeat in the new protocol. + +### Why disable the streaming protocol on FaaS platforms like AWS Lambda? + +The streaming protocol relies on the assumption that the client can read the server's heartbeat responses in a timely +manner, otherwise the client will be acting on stale information. In many FaaS platforms, like AWS Lambda, host +applications will be suspended and resumed many minutes later. This behavior causes a build up of heartbeat responses +and the client can end up spending a long time in a catch up phase processing outdated responses. This problem was +discovered in [DRIVERS-2246](https://jira.mongodb.org/browse/DRIVERS-2246). + +Additionally, the streaming protocol requires an extra connection and thread per monitored server which is expensive on +platforms like AWS Lambda. The extra connection is particularly inefficient when thousands of AWS instances and thus +thousands of clients are used. + +We decided to make polling the default behavior when running on FaaS platforms like AWS Lambda to improve scalability, +performance, and reliability. + +### Why introduce a knob for serverMonitoringMode? + +The serverMonitoringMode knob provides a workaround in cases where the polling protocol would be a better choice but the +driver is not running on a FaaS platform. It also provides a workaround in case the FaaS detection logic becomes +outdated or inaccurate. + +## Changelog + +- 2024-05-02: Migrated from reStructuredText to Markdown. + +- 2020-02-20: Extracted server monitoring from SDAM into this new spec. + +- 2020-03-09: A monitor check that creates a new connection MUST use the connection's handshake to update the topology. + +- 2020-04-20: Add streaming heartbeat protocol. + +- 2020-05-20: Include rationale for why we don't use `awaitedTimeMS` + +- 2020-06-11: Support connectTimeoutMS=0 in streaming heartbeat protocol. + +- 2020-12-17: Mark the pool for a server as "ready" after performing a successful check. Synchronize pool clearing with + SDAM updates. + +- 2021-06-21: Added support for hello/helloOk to handshake and monitoring. + +- 2021-06-24: Remove optimization mention that no longer applies + +- 2022-01-19: Add 90th percentile RTT tracking. + +- 2022-02-24: Rename Versioned API to Stable API + +- 2022-04-05: Preemptively cancel in progress operations when SDAM heartbeats timeout. + +- 2022-10-05: Remove spec front matter reformat changelog. + +- 2022-11-17: Add minimum RTT tracking and remove 90th percentile RTT. + +- 2023-10-05: Add serverMonitoringMode and default to the polling protocol on FaaS. Clients MUST NOT use dedicated + connections to measure RTT when using the polling protocol. + +______________________________________________________________________ diff --git a/source/server-discovery-and-monitoring/server-monitoring.rst b/source/server-discovery-and-monitoring/server-monitoring.rst new file mode 100644 index 0000000000..11355ed1dc --- /dev/null +++ b/source/server-discovery-and-monitoring/server-monitoring.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `server-monitoring.md `_. diff --git a/source/server-discovery-and-monitoring/tests/README.md b/source/server-discovery-and-monitoring/tests/README.md new file mode 100644 index 0000000000..e747fee538 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/README.md @@ -0,0 +1,239 @@ +# Server Discovery And Monitoring Tests + +______________________________________________________________________ + +The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their +conformance to the Server Discovery And Monitoring Spec. + +Additional prose tests, that cannot be represented as spec tests, are described and MUST be implemented. + +## Version + +Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +## Format + +Each YAML file has the following keys: + +- description: A textual description of the test. +- uri: A connection string. +- phases: An array of "phase" objects. A phase of the test optionally sends inputs to the client, then tests the + client's resulting TopologyDescription. + +Each phase object has the following keys: + +- description: (optional) A textual description of this phase. +- responses: (optional) An array of "response" objects. If not provided, the test runner should construct the client and + perform assertions specified in the outcome object without processing any responses. +- applicationErrors: (optional) An array of "applicationError" objects. +- outcome: An "outcome" object representing the TopologyDescription. + +A response is a pair of values: + +- The source, for example "a:27017". This is the address the client sent the "hello" or legacy hello command to. +- A hello or legacy hello response, for example `{ok: 1, helloOk: true, isWritablePrimary: true}`. If the response + includes an electionId it is shown in extended JSON like `{"$oid": "000000000000000000000002"}`. The empty response + `{}` indicates a network error when attempting to call "hello" or legacy hello. + +An "applicationError" object has the following keys: + +- address: The source address, for example "a:27017". +- generation: (optional) The error's generation number, for example `1`. When absent this value defaults to the pool's + current generation number. +- maxWireVersion: The `maxWireVersion` of the connection the error occurs on, for example `9`. Added to support testing + the behavior of "not writable primary" errors on \<4.2 and >=4.2 servers. +- when: A string describing when this mock error should occur. Supported values are: + - "beforeHandshakeCompletes": Simulate this mock error as if it occurred during a new connection's handshake for an + application operation. + - "afterHandshakeCompletes": Simulate this mock error as if it occurred on an established connection for an + application operation (i.e. after the connection pool check out succeeds). +- type: The type of error to mock. Supported values are: + - "command": A command error. Always accompanied with a "response". + - "network": A non-timeout network error. + - "timeout": A network timeout error. +- response: (optional) A command error response, for example `{ok: 0, errmsg: "not primary"}`. Present if and only if + `type` is "command". Note the server only returns "not primary" if the "hello" command has been run on this + connection. Otherwise the legacy error message is returned. + +In non-monitoring tests, an "outcome" represents the correct TopologyDescription that results from processing the +responses in the phases so far. It has the following keys: + +- topologyType: A string like "ReplicaSetNoPrimary". +- setName: A string with the expected replica set name, or null. +- servers: An object whose keys are addresses like "a:27017", and whose values are "server" objects. +- logicalSessionTimeoutMinutes: null or an integer. +- maxSetVersion: absent or an integer. +- maxElectionId: absent or a BSON ObjectId. +- compatible: absent or a bool. + +A "server" object represents a correct ServerDescription within the client's current TopologyDescription. It has the +following keys: + +- type: A ServerType name, like "RSSecondary". See [ServerType](../server-discovery-and-monitoring.md#servertype) for + details pertaining to async and multi-threaded drivers. +- error: An optional string that must be a substring of the message on the `ServerDescription.error` object +- setName: A string with the expected replica set name, or null. +- setVersion: absent or an integer. +- electionId: absent, null, or an ObjectId. +- logicalSessionTimeoutMinutes: absent, null, or an integer. +- minWireVersion: absent or an integer. +- maxWireVersion: absent or an integer. +- topologyVersion: absent, null, or a topologyVersion document. +- pool: (optional) A "pool" object. + +A "pool" object represents a correct connection pool for a given server. It has the following keys: + +- generation: This server's expected pool generation, like `0`. + +In monitoring tests, an "outcome" contains a list of SDAM events that should have been published by the client as a +result of processing hello or legacy hello responses in the current phase. Any SDAM events published by the client +during its construction (that is, prior to processing any of the responses) should be combined with the events published +during processing of hello or legacy hello responses of the first phase of the test. A test MAY explicitly verify events +published during client construction by providing an empty responses array for the first phase. + +## Use as unittests + +### Mocking + +Drivers should be able to test their server discovery and monitoring logic without any network I/O, by parsing hello (or +legacy hello) and application error from the test file and passing them into the driver code. Parts of the client and +monitoring code may need to be mocked or subclassed to achieve this. +[A reference implementation for PyMongo 3.10.1 is available here](https://github.com/mongodb/mongo-python-driver/blob/3.10.1/test/test_discovery_and_monitoring.py). + +### Initialization + +For each file, create a fresh client object initialized with the file's "uri". + +All files in the "single" directory include a connection string with one host and no "replicaSet" option. Set the +client's initial TopologyType to Single, however that is achieved using the client's API. (The spec says "The user MUST +be able to set the initial TopologyType to Single" without specifying how.) + +All files in the "sharded" directory include a connection string with multiple hosts and no "replicaSet" option. Set the +client's initial TopologyType to Unknown or Sharded, depending on the client's API. + +All files in the "rs" directory include a connection string with a "replicaSet" option. Set the client's initial +TopologyType to ReplicaSetNoPrimary. (For most clients, parsing a connection string with a "replicaSet" option +automatically sets the TopologyType to ReplicaSetNoPrimary.) Some of the files in "rs" are post-fixed with "pre-6.0". +These files test the `updateRSFromPrimary` behavior prior to maxWireVersion 17, there should be no special handling +required for these tests. + +Set up a listener to collect SDAM events published by the client, including events published during client construction. + +### Test Phases + +For each phase in the file: + +1. Parse the "responses" array. Pass in the responses in order to the driver code. If a response is the empty object + `{}`, simulate a network error. +2. Parse the "applicationErrors" array. For each element, simulate the given error as if it occurred while running an + application operation. Note that it is sufficient to construct a mock error and call the procedure which updates + the topology, e.g. `topology.handleApplicationError(address, generation, maxWireVersion, error)`. + +For non-monitoring tests, once all responses are processed, assert that the phase's "outcome" object is equivalent to +the driver's current TopologyDescription. + +For monitoring tests, once all responses are processed, assert that the events collected so far by the SDAM event +listener are equivalent to the events specified in the phase. + +Some fields such as "logicalSessionTimeoutMinutes", "compatible", and "topologyVersion" were added later and haven't +been added to all test files. If these fields are present, test that they are equivalent to the fields of the driver's +current TopologyDescription or ServerDescription. + +For monitoring tests, clear the list of events collected so far. + +Continue until all phases have been executed. + +## Integration Tests + +Integration tests are provided in the "unified" directory and are written in the +[Unified Test Format](../../unified-test-format/unified-test-format.md). + +## Prose Tests + +The following prose tests cannot be represented as spec tests and MUST be implemented. + +### Streaming protocol Tests + +Drivers that implement the streaming protocol (multi-threaded or asynchronous drivers) must implement the following +tests. Each test should be run against a standalone, replica set, and sharded cluster unless otherwise noted. + +Some of these cases should already be tested with the old protocol; in that case just verify the test cases succeed with +the new protocol. + +1. Configure the client with heartbeatFrequencyMS set to 500, overriding the default of 10000. Assert the client + processes hello and legacy hello replies more frequently (approximately every 500ms). + +### RTT Tests + +Run the following test(s) on MongoDB 4.4+. + +1. Test that RTT is continuously updated. + 1. Create a client with `heartbeatFrequencyMS=500`, `appName=streamingRttTest`, and subscribe to server events. + + 2. Run a find command to wait for the server to be discovered. + + 3. Sleep for 2 seconds. This must be long enough for multiple heartbeats to succeed. + + 4. Assert that each `ServerDescriptionChangedEvent` includes a non-zero RTT. + + 5. Configure the following failpoint to block hello or legacy hello commands for 250ms which should add extra latency + to each RTT check: + + ```javascript + db.adminCommand({ + configureFailPoint: "failCommand", + mode: {times: 1000}, + data: { + failCommands: ["hello"], // or the legacy hello command + blockConnection: true, + blockTimeMS: 500, + appName: "streamingRttTest", + }, + }); + ``` + + 6. Wait for the server's RTT to exceed 250ms. Eventually the average RTT should also exceed 500ms but we use 250ms to + speed up the test. Note that the + [Server Description Equality](../server-discovery-and-monitoring.md#server-description-equality) rule means that + ServerDescriptionChangedEvents will not be published. This test may need to use a driver specific helper to + obtain the latest RTT instead. If the RTT does not exceed 250ms after 10 seconds, consider the test failed. + + 7. Disable the failpoint: + + ```javascript + db.adminCommand({ + configureFailPoint: "failCommand", + mode: "off", + }); + ``` + +### Heartbeat Tests + +1. Test that `ServerHeartbeatStartedEvent` is emitted before the monitoring socket was created + 1. Create a mock TCP server (example shown below) that pushes a `client connected` event to a shared array when a + client connects and a `client hello received` event when the server receives the client hello and then closes + the connection: + + ```javascript + let events = []; + server = createServer(clientSocket => { + events.push('client connected'); + + clientSocket.on('data', () => { + events.push('client hello received'); + clientSocket.destroy(); + }); + }); + server.listen(9999); + ``` + + 2. Create a client with `serverSelectionTimeoutMS: 500` and listen to `ServerHeartbeatStartedEvent` and + `ServerHeartbeatFailedEvent`, pushing the event name to the same shared array as the mock TCP server + + 3. Attempt to connect client to previously created TCP server, catching the error when the client fails to connect + + 4. Assert that the first four elements in the array are: : + + ```javascript + ['serverHeartbeatStartedEvent', 'client connected', 'client hello received', 'serverHeartbeatFailedEvent'] + ``` diff --git a/source/server-discovery-and-monitoring/tests/README.rst b/source/server-discovery-and-monitoring/tests/README.rst deleted file mode 100644 index 768a1128ad..0000000000 --- a/source/server-discovery-and-monitoring/tests/README.rst +++ /dev/null @@ -1,109 +0,0 @@ -===================================== -Server Discovery And Monitoring Tests -===================================== - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the -Server Discovery And Monitoring Spec. - -Version -------- - -Files in the "specifications" repository have no version scheme. They are not -tied to a MongoDB server version. - -Format ------- - -Each YAML file has the following keys: - -- description: Some text. -- uri: A connection string. -- phases: An array of "phase" objects. - -A "phase" of the test sends inputs to the client, then tests the client's -resulting TopologyDescription. Each phase object has two keys: - -- responses: An array of "response" objects. -- outcome: An "outcome" object representing the TopologyDescription. - -A response is a pair of values: - -- The source, for example "a:27017". - This is the address the client sent the "ismaster" command to. -- An ismaster response, for example `{ok: 1, ismaster: true}`. - If the response includes an electionId it is shown in extended JSON like - `{"$oid": "000000000000000000000002"}`. - The empty response `{}` indicates a network error - when attempting to call "ismaster". - -An "outcome" represents the correct TopologyDescription that results from -processing the responses in the phases so far. It has the following keys: - -- topologyType: A string like "ReplicaSetNoPrimary". -- setName: A string with the expected replica set name, or null. -- servers: An object whose keys are addresses like "a:27017", and whose values - are "server" objects. -- logicalSessionTimeoutMinutes: null or an integer. -- compatible: absent or a bool. - -A "server" object represents a correct ServerDescription within the client's -current TopologyDescription. It has the following keys: - -- type: A ServerType name, like "RSSecondary". -- setName: A string with the expected replica set name, or null. -- setVersion: absent or an integer. -- electionId: absent, null, or an ObjectId. -- logicalSessionTimeoutMinutes: absent, null, or an integer. -- minWireVersion: absent or an integer. -- maxWireVersion: absent or an integer. - -Use as unittests ----------------- - -Mocking -~~~~~~~ - -Drivers should be able to test their server discovery and monitoring logic -without any network I/O, by parsing ismaster responses from the test file -and passing them into the driver code. Parts of the client and monitoring -code may need to be mocked or subclassed to achieve this. `A reference -implementation for PyMongo 3.x is available here -`_. - -Initialization -~~~~~~~~~~~~~~ - -For each file, create a fresh client object initialized with the file's "uri". - -All files in the "single" directory include a connection string with one host -and no "replicaSet" option. -Set the client's initial TopologyType to Single, however that is achieved using the client's API. -(The spec says "The user MUST be able to set the initial TopologyType to Single" -without specifying how.) - -All files in the "sharded" directory include a connection string with multiple hosts -and no "replicaSet" option. -Set the client's initial TopologyType to Unknown or Sharded, depending on the client's API. - -All files in the "rs" directory include a connection string with a "replicaSet" option. -Set the client's initial TopologyType to ReplicaSetNoPrimary. -(For most clients, parsing a connection string with a "replicaSet" option -automatically sets the TopologyType to ReplicaSetNoPrimary.) - -Test Phases -~~~~~~~~~~~ - -For each phase in the file, parse the "responses" array. -Pass in the responses in order to the driver code. -If a response is the empty object `{}`, simulate a network error. - -Once all responses are processed, assert that the phase's "outcome" object -is equivalent to the driver's current TopologyDescription. - -Some fields such as "logicalSessionTimeoutMinutes" or "compatible" were added -later and haven't been added to all test files. If these fields are present, -test that they are equivalent to the fields of the driver's current -TopologyDescription. - -Continue until all phases have been executed. diff --git a/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json b/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json new file mode 100644 index 0000000000..c60ee453dd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.json @@ -0,0 +1,121 @@ +{ + "description": "Network timeouts before and after the handshake completes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore network timeout application error (afterHandshakeCompletes)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "timeout" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore network timeout application error (beforeHandshakeCompletes)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "timeout" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml b/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml new file mode 100644 index 0000000000..42cfa60509 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/error_handling_handshake.yml @@ -0,0 +1,48 @@ +description: Network timeouts before and after the handshake completes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore network timeout application error (afterHandshakeCompletes) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: timeout + outcome: *outcome + +- description: Ignore network timeout application error (beforeHandshakeCompletes) + # The test runner may need to add the SystemOverloadedError label for the pre-handshake network timeout. + # The SystemOverloadedError label causes the error to be ignored. + applicationErrors: + - address: a:27017 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: timeout + outcome: *outcome \ No newline at end of file diff --git a/source/server-discovery-and-monitoring/tests/errors/generate-error-tests.py b/source/server-discovery-and-monitoring/tests/errors/generate-error-tests.py new file mode 100644 index 0000000000..c9323857ad --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/generate-error-tests.py @@ -0,0 +1,172 @@ +import itertools +import os +import subprocess +import sys + +# Require Python 3.7+ for ordered dictionaries so that the order of the +# generated tests remain the same. +if sys.version_info[:2] < (3, 7): + print('ERROR: This script requires Python >= 3.7, not:') + print(sys.version) + print('Usage: python3 %s' % (sys.argv[0])) + exit(1) + + +dirname = os.path.dirname +DIR = dirname(os.path.realpath(__file__)) +SOURCE = dirname(dirname(dirname(DIR))) + + +def template(filename): + fullpath = os.path.join(DIR, filename) + with open(fullpath, 'r') as f: + return f.read() + + +def write_test(filename, data): + fullpath = os.path.join(DIR, filename + '.yml') + with open(fullpath, 'w') as f: + f.write(data) + + print(f"Generated {fullpath}") + + +# Maps from error_name to (error_code,) +ERR_CODES = { + 'InterruptedAtShutdown': (11600,), + 'InterruptedDueToReplStateChange': (11602,), + 'NotPrimaryOrSecondary': (13436,), + 'PrimarySteppedDown': (189,), + 'ShutdownInProgress': (91,), + 'NotWritablePrimary': (10107,), + 'NotPrimaryNoSecondaryOk': (13435,), + 'LegacyNotPrimary': (10058,), +} + + +def create_stale_tests(): + tmp = template('stale-topologyVersion.yml.template') + for error_name in ERR_CODES: + test_name = f'stale-topologyVersion-{error_name}' + error_code, = ERR_CODES[error_name] + data = tmp.format(**locals()) + write_test(test_name, data) + +TV_GREATER = ''' + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2"''' +TV_GREATER_FINAL = ''' + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2"''' +TV_CHANGED = ''' + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1"''' +TV_CHANGED_FINAL = ''' + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1"''' + +# Maps non-stale error description to: +# (error_topology_version, final_topology_version) +NON_STALE_CASES = { + 'topologyVersion missing': ('', ' null'), + 'topologyVersion greater': (TV_GREATER, TV_GREATER_FINAL), + 'topologyVersion proccessId changed': (TV_CHANGED, TV_CHANGED_FINAL), +} + + +def create_non_stale_tests(): + tmp = template('non-stale-topologyVersion.yml.template') + for error_name, description in itertools.product( + ERR_CODES, NON_STALE_CASES): + test_name = f'non-stale-{description.replace(" ", "-")}-{error_name}' + error_code, = ERR_CODES[error_name] + error_topology_version, final_topology_version = NON_STALE_CASES[description] + # On 4.2+, only ShutdownInProgress and InterruptedAtShutdown will + # clear the pool. + if error_name in ("ShutdownInProgress", "InterruptedAtShutdown"): + final_pool_generation = 1 + else: + final_pool_generation = 0 + + data = tmp.format(**locals()) + write_test(test_name, data) + + +WHEN = ['beforeHandshakeCompletes', 'afterHandshakeCompletes'] +STALE_GENERATION_COMMAND_ERROR = ''' + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code} + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2"''' +STALE_GENERATION_NETWORK_ERROR = ''' + type: {network_error_type}''' + + +def create_stale_generation_tests(): + tmp = template('stale-generation.yml.template') + # Stale command errors + for error_name, when in itertools.product(ERR_CODES, WHEN): + test_name = f'stale-generation-{when}-{error_name}' + error_code, = ERR_CODES[error_name] + stale_error = STALE_GENERATION_COMMAND_ERROR.format(**locals()) + data = tmp.format(**locals()) + write_test(test_name, data) + # Stale network errors + for network_error_type, when in itertools.product( + ['network', 'timeout'], WHEN): + error_name = network_error_type + test_name = f'stale-generation-{when}-{network_error_type}' + stale_error = STALE_GENERATION_NETWORK_ERROR.format(**locals()) + data = tmp.format(**locals()) + write_test(test_name, data) + + +def create_pre_42_tests(): + tmp = template('pre-42.yml.template') + # All "not writable primary"/"node is recovering" clear the pool on <4.2 + for error_name in ERR_CODES: + test_name = f'pre-42-{error_name}' + error_code, = ERR_CODES[error_name] + data = tmp.format(**locals()) + write_test(test_name, data) + + +def create_post_42_tests(): + tmp = template('post-42.yml.template') + for error_name in ERR_CODES: + test_name = f'post-42-{error_name}' + error_code, = ERR_CODES[error_name] + # On 4.2+, only ShutdownInProgress and InterruptedAtShutdown will + # clear the pool. + if error_name in ("ShutdownInProgress", "InterruptedAtShutdown"): + final_pool_generation = 1 + else: + final_pool_generation = 0 + data = tmp.format(**locals()) + write_test(test_name, data) + + +create_stale_tests() +create_non_stale_tests() +create_stale_generation_tests() +create_pre_42_tests() +create_post_42_tests() + +print('Running make') +subprocess.run(f'cd {SOURCE} && make', shell=True, check=True) diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json new file mode 100644 index 0000000000..c22a47dc8a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.json @@ -0,0 +1,80 @@ +{ + "description": "Non-stale network error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale network error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml new file mode 100644 index 0000000000..3d29debefe --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-error.yml @@ -0,0 +1,47 @@ +description: Non-stale network error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale network error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json new file mode 100644 index 0000000000..03dc5b66c9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.json @@ -0,0 +1,88 @@ +{ + "description": "Non-stale network timeout error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale network timeout error does not mark server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "timeout" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml new file mode 100644 index 0000000000..0c27f52d2e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-network-timeout-error.yml @@ -0,0 +1,38 @@ +description: Non-stale network timeout error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale network timeout error does not mark server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: timeout + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json new file mode 100644 index 0000000000..777e703a3c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater InterruptedAtShutdown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..9672debceb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedAtShutdown.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater InterruptedAtShutdown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..c4aa7fb71b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater InterruptedDueToReplStateChange error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..87162a1595 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-InterruptedDueToReplStateChange.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater InterruptedDueToReplStateChange error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json new file mode 100644 index 0000000000..2a9bc8a5cf --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater LegacyNotPrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml new file mode 100644 index 0000000000..ca25a16512 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-LegacyNotPrimary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater LegacyNotPrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..638aa306cb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater NotPrimaryNoSecondaryOk error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..b5dfc46978 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater NotPrimaryNoSecondaryOk error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..f327954a9d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater NotPrimaryOrSecondary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..8583704c90 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotPrimaryOrSecondary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater NotPrimaryOrSecondary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json new file mode 100644 index 0000000000..0ac02fb19b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater NotWritablePrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml new file mode 100644 index 0000000000..6770de0519 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-NotWritablePrimary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater NotWritablePrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json new file mode 100644 index 0000000000..daf2a7e8e1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater PrimarySteppedDown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml new file mode 100644 index 0000000000..a302307d97 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-PrimarySteppedDown.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater PrimarySteppedDown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json new file mode 100644 index 0000000000..a7d9e1fe24 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion greater ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion greater ShutdownInProgress error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml new file mode 100644 index 0000000000..2d7a0c7d27 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-greater-ShutdownInProgress.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion greater ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion greater ShutdownInProgress error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json new file mode 100644 index 0000000000..2c59e785ab --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing InterruptedAtShutdown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..74e59d2056 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedAtShutdown.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing InterruptedAtShutdown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..f2cb834e83 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing InterruptedDueToReplStateChange error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..3eab8ad40f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-InterruptedDueToReplStateChange.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing InterruptedDueToReplStateChange error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json new file mode 100644 index 0000000000..095128d615 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing LegacyNotPrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml new file mode 100644 index 0000000000..a7d0113357 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-LegacyNotPrimary.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing LegacyNotPrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..3d7312d4a5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing NotPrimaryNoSecondaryOk error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..0f43246070 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing NotPrimaryNoSecondaryOk error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..a457ba3072 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing NotPrimaryOrSecondary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..fcc72bf794 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotPrimaryOrSecondary.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing NotPrimaryOrSecondary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json new file mode 100644 index 0000000000..b7427a3f3d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing NotWritablePrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml new file mode 100644 index 0000000000..4a9ef427fb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-NotWritablePrimary.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing NotWritablePrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json new file mode 100644 index 0000000000..8146a60d6e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing PrimarySteppedDown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml new file mode 100644 index 0000000000..8ca1251a2d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-PrimarySteppedDown.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing PrimarySteppedDown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json new file mode 100644 index 0000000000..c7597007d7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.json @@ -0,0 +1,85 @@ +{ + "description": "Non-stale topologyVersion missing ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion missing ShutdownInProgress error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml new file mode 100644 index 0000000000..cd8857008d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-missing-ShutdownInProgress.yml @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion missing ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion missing ShutdownInProgress error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json new file mode 100644 index 0000000000..8448c60599 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed InterruptedAtShutdown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..126ed23b44 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedAtShutdown.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed InterruptedAtShutdown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..9d601c4ede --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed InterruptedDueToReplStateChange error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..bcb69479d1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-InterruptedDueToReplStateChange.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed InterruptedDueToReplStateChange error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json new file mode 100644 index 0000000000..8be833f104 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed LegacyNotPrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml new file mode 100644 index 0000000000..172240cb78 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-LegacyNotPrimary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed LegacyNotPrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..f2f94c0d00 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed NotPrimaryNoSecondaryOk error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..b915fb97f3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed NotPrimaryNoSecondaryOk error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..6d3b397566 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed NotPrimaryOrSecondary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..dc104488cd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotPrimaryOrSecondary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed NotPrimaryOrSecondary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json new file mode 100644 index 0000000000..332ddf5ec1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed NotWritablePrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml new file mode 100644 index 0000000000..5146636b65 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-NotWritablePrimary.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed NotWritablePrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json new file mode 100644 index 0000000000..c22a537f58 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed PrimarySteppedDown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml new file mode 100644 index 0000000000..0c997a2a3e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-PrimarySteppedDown.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed PrimarySteppedDown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json new file mode 100644 index 0000000000..eaaab79273 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.json @@ -0,0 +1,100 @@ +{ + "description": "Non-stale topologyVersion proccessId changed ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale topologyVersion proccessId changed ShutdownInProgress error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml new file mode 100644 index 0000000000..973c56460b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion-proccessId-changed-ShutdownInProgress.yml @@ -0,0 +1,61 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale topologyVersion proccessId changed ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale topologyVersion proccessId changed ShutdownInProgress error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: + processId: + "$oid": '000000000000000000000002' + counter: + "$numberLong": "1" + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template new file mode 100644 index 0000000000..83cff68e27 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/non-stale-topologyVersion.yml.template @@ -0,0 +1,52 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Non-stale {description} {error_name} error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Non-stale {description} {error_name} error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code}{error_topology_version} + outcome: + servers: + a:27017: + type: Unknown + topologyVersion:{final_topology_version} + pool: + generation: {final_pool_generation} + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json new file mode 100644 index 0000000000..40c4ed6c80 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 InterruptedAtShutdown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..73a0f0d285 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedAtShutdown.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 InterruptedAtShutdown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..5c489f5ecb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 InterruptedDueToReplStateChange error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..ad333e311e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-InterruptedDueToReplStateChange.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 InterruptedDueToReplStateChange error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json new file mode 100644 index 0000000000..f0851b299e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 LegacyNotPrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml new file mode 100644 index 0000000000..9c7723bbf3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-LegacyNotPrimary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 LegacyNotPrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..a675f0ca54 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 NotPrimaryNoSecondaryOk error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..49304d6d63 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 NotPrimaryNoSecondaryOk error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..ea9bf1d16b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 NotPrimaryOrSecondary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..9174b31057 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotPrimaryOrSecondary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 NotPrimaryOrSecondary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json new file mode 100644 index 0000000000..10211fca70 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 NotWritablePrimary error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml new file mode 100644 index 0000000000..886b1f5b5b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-NotWritablePrimary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 NotWritablePrimary error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json new file mode 100644 index 0000000000..fa98d0bf06 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 PrimarySteppedDown error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml new file mode 100644 index 0000000000..6a42267996 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-PrimarySteppedDown.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 PrimarySteppedDown error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json new file mode 100644 index 0000000000..cd587205b6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.json @@ -0,0 +1,70 @@ +{ + "description": "Post-4.2 ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 8 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Post-4.2 ShutdownInProgress error marks server Unknown", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 8, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml new file mode 100644 index 0000000000..2ad90d9864 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42-ShutdownInProgress.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 ShutdownInProgress error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/post-42.yml.template b/source/server-discovery-and-monitoring/tests/errors/post-42.yml.template new file mode 100644 index 0000000000..8ca3c79ba4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/post-42.yml.template @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Post-4.2 {error_name} error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 8 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Post-4.2 {error_name} error marks server Unknown + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 8 + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code} + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: {final_pool_generation} + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json new file mode 100644 index 0000000000..9f6ea212e5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 InterruptedAtShutdown error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..4996e9f25f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedAtShutdown.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 InterruptedAtShutdown error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..7e5f235713 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 InterruptedDueToReplStateChange error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..ada5f1ff73 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-InterruptedDueToReplStateChange.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 InterruptedDueToReplStateChange error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json new file mode 100644 index 0000000000..1635f1a856 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 LegacyNotPrimary error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml new file mode 100644 index 0000000000..e2b37b4f5f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-LegacyNotPrimary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 LegacyNotPrimary error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..0e70ede02c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 NotPrimaryNoSecondaryOk error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..867b7d3dc1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 NotPrimaryNoSecondaryOk error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..3fefb21663 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 NotPrimaryOrSecondary error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..705e2339c0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotPrimaryOrSecondary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 NotPrimaryOrSecondary error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json new file mode 100644 index 0000000000..d010da0a5b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 NotWritablePrimary error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml new file mode 100644 index 0000000000..29669ff336 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-NotWritablePrimary.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 NotWritablePrimary error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json new file mode 100644 index 0000000000..02956d201d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 PrimarySteppedDown error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml new file mode 100644 index 0000000000..9176ee25c3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-PrimarySteppedDown.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 PrimarySteppedDown error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json new file mode 100644 index 0000000000..fc3a5aa6fe --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.json @@ -0,0 +1,70 @@ +{ + "description": "Pre-4.2 ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 7 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Pre-4.2 ShutdownInProgress error marks server Unknown and clears the pool", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 7, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml new file mode 100644 index 0000000000..5c64050e59 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42-ShutdownInProgress.yml @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 ShutdownInProgress error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/pre-42.yml.template b/source/server-discovery-and-monitoring/tests/errors/pre-42.yml.template new file mode 100644 index 0000000000..7449f68c16 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/pre-42.yml.template @@ -0,0 +1,47 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Pre-4.2 {error_name} error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 7 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: null + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Pre-4.2 {error_name} error marks server Unknown and clears the pool + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 7 + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code} + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.json b/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.json new file mode 100644 index 0000000000..eb00b69613 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.json @@ -0,0 +1,131 @@ +{ + "description": "Do not check errmsg when code exists", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "errmsg \"not master\" gets ignored when error code exists", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "not master", + "code": 1 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "errmsg \"node is recovering\" gets ignored when error code exists", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "node is recovering", + "code": 1 + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml b/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml new file mode 100644 index 0000000000..6bd98386bb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/prefer-error-code.yml @@ -0,0 +1,54 @@ +description: Do not check errmsg when code exists +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: errmsg "not master" gets ignored when error code exists + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: "not master" # NOTE: This needs to be "not master" and not "not writable primary". + code: 1 # Not a "not writable primary" error code. + outcome: *outcome + +- description: errmsg "node is recovering" gets ignored when error code exists + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: "node is recovering" + code: 1 # Not a "node is recovering" error code. + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json new file mode 100644 index 0000000000..2f7c7fd13b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedAtShutdown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..37dddcead5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedAtShutdown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedAtShutdown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..b0b51ef676 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedDueToReplStateChange error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..f83532e2c5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-InterruptedDueToReplStateChange.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedDueToReplStateChange error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..b68e23b7a7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryNoSecondaryOk error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..43c7f57742 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryNoSecondaryOk error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..d9b3562654 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryOrSecondary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..1dc6a2319e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotPrimaryOrSecondary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryOrSecondary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json new file mode 100644 index 0000000000..90889356dd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotWritablePrimary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml new file mode 100644 index 0000000000..488eaab4a5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-NotWritablePrimary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotWritablePrimary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json new file mode 100644 index 0000000000..0a707a1c07 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale PrimarySteppedDown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml new file mode 100644 index 0000000000..2b45081087 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-PrimarySteppedDown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale PrimarySteppedDown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json new file mode 100644 index 0000000000..5da3413d5b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale ShutdownInProgress error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml new file mode 100644 index 0000000000..b9bd8212d9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-ShutdownInProgress.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale ShutdownInProgress error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '2' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json new file mode 100644 index 0000000000..d29310fb61 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedAtShutdown error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedAtShutdown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..48dd257af9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedAtShutdown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedAtShutdown error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedAtShutdown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..376bb93770 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedDueToReplStateChange error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedDueToReplStateChange error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..5e7ddeaa36 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-InterruptedDueToReplStateChange.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedDueToReplStateChange error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedDueToReplStateChange error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json new file mode 100644 index 0000000000..990fc45e4e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation LegacyNotPrimary error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale LegacyNotPrimary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml new file mode 100644 index 0000000000..8701f366ab --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-LegacyNotPrimary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation LegacyNotPrimary error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale LegacyNotPrimary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..1744a82f77 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryNoSecondaryOk error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryNoSecondaryOk error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..70673e9fe6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryNoSecondaryOk error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryNoSecondaryOk error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..57ca1cf158 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryOrSecondary error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryOrSecondary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..4b036edd0d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotPrimaryOrSecondary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryOrSecondary error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryOrSecondary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json new file mode 100644 index 0000000000..995453c82b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotWritablePrimary error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotWritablePrimary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml new file mode 100644 index 0000000000..f9199ef48f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-NotWritablePrimary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotWritablePrimary error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotWritablePrimary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json new file mode 100644 index 0000000000..bf4c85d24f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation PrimarySteppedDown error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale PrimarySteppedDown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml new file mode 100644 index 0000000000..032993e749 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-PrimarySteppedDown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation PrimarySteppedDown error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale PrimarySteppedDown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json new file mode 100644 index 0000000000..9374900e06 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation ShutdownInProgress error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale ShutdownInProgress error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml new file mode 100644 index 0000000000..23659f729b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-ShutdownInProgress.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation ShutdownInProgress error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale ShutdownInProgress error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json new file mode 100644 index 0000000000..f5d01b6540 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.json @@ -0,0 +1,163 @@ +{ + "description": "Stale generation network error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale network error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml new file mode 100644 index 0000000000..98cb9a44e1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-network.yml @@ -0,0 +1,82 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation network error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale network error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json new file mode 100644 index 0000000000..fa84343b0b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.json @@ -0,0 +1,163 @@ +{ + "description": "Stale generation timeout error afterHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale timeout error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "timeout" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml new file mode 100644 index 0000000000..37b8f71d8d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-afterHandshakeCompletes-timeout.yml @@ -0,0 +1,82 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation timeout error afterHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale timeout error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: timeout + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json new file mode 100644 index 0000000000..72fac9a86e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedAtShutdown error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedAtShutdown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..c699c53ab1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedAtShutdown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedAtShutdown error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedAtShutdown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..3c713592a3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation InterruptedDueToReplStateChange error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedDueToReplStateChange error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..a9826eb01a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-InterruptedDueToReplStateChange.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation InterruptedDueToReplStateChange error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedDueToReplStateChange error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json new file mode 100644 index 0000000000..257b6ec6fb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation LegacyNotPrimary error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale LegacyNotPrimary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml new file mode 100644 index 0000000000..f26d4d963f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-LegacyNotPrimary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation LegacyNotPrimary error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale LegacyNotPrimary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..dcb5716f44 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryNoSecondaryOk error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryNoSecondaryOk error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..27187bae63 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryNoSecondaryOk error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryNoSecondaryOk error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..58cefafae9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotPrimaryOrSecondary error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryOrSecondary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..1b56fd2ab7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotPrimaryOrSecondary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotPrimaryOrSecondary error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryOrSecondary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json new file mode 100644 index 0000000000..c92b01e054 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation NotWritablePrimary error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotWritablePrimary error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml new file mode 100644 index 0000000000..725dc172de --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-NotWritablePrimary.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation NotWritablePrimary error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotWritablePrimary error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json new file mode 100644 index 0000000000..62759b6ad9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation PrimarySteppedDown error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale PrimarySteppedDown error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml new file mode 100644 index 0000000000..cac86fa7ee --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-PrimarySteppedDown.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation PrimarySteppedDown error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale PrimarySteppedDown error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json new file mode 100644 index 0000000000..4661632c4f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.json @@ -0,0 +1,176 @@ +{ + "description": "Stale generation ShutdownInProgress error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale ShutdownInProgress error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml new file mode 100644 index 0000000000..dd3ca671b3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-ShutdownInProgress.yml @@ -0,0 +1,91 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation ShutdownInProgress error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale ShutdownInProgress error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": "2" + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json new file mode 100644 index 0000000000..15b044fc73 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.json @@ -0,0 +1,163 @@ +{ + "description": "Stale generation network error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale network error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml new file mode 100644 index 0000000000..b678534826 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-network.yml @@ -0,0 +1,82 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation network error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale network error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json new file mode 100644 index 0000000000..acbb9e581e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.json @@ -0,0 +1,163 @@ +{ + "description": "Stale generation timeout error beforeHandshakeCompletes", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Non-stale application network error", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "network" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Primary A is rediscovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale timeout error (stale generation)", + "applicationErrors": [ + { + "address": "a:27017", + "generation": 0, + "when": "beforeHandshakeCompletes", + "maxWireVersion": 9, + "type": "timeout" + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 1 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml new file mode 100644 index 0000000000..d1ba24ba3b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation-beforeHandshakeCompletes-timeout.yml @@ -0,0 +1,82 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation timeout error beforeHandshakeCompletes +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale timeout error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: beforeHandshakeCompletes + maxWireVersion: 9 + type: timeout + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template b/source/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template new file mode 100644 index 0000000000..1519796575 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-generation.yml.template @@ -0,0 +1,81 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale generation {error_name} error {when} +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +# Process a network error which increments the pool generation. +- description: Non-stale application network error + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: network + outcome: + servers: + a:27017: + type: Unknown + topologyVersion: null + pool: + generation: 1 + topologyType: ReplicaSetNoPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Primary A is rediscovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: *topologyVersion_1_1 + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 1 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale {error_name} error (stale generation) + applicationErrors: + - address: a:27017 + generation: 0 + when: {when} + maxWireVersion: 9{stale_error} + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json new file mode 100644 index 0000000000..f2207a04d5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion InterruptedAtShutdown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedAtShutdown error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedAtShutdown error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedAtShutdown", + "code": 11600, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml new file mode 100644 index 0000000000..483c48044d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedAtShutdown.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion InterruptedAtShutdown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedAtShutdown error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale InterruptedAtShutdown error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedAtShutdown + code: 11600 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json new file mode 100644 index 0000000000..4387451ce6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion InterruptedDueToReplStateChange error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedDueToReplStateChange error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale InterruptedDueToReplStateChange error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "InterruptedDueToReplStateChange", + "code": 11602, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml new file mode 100644 index 0000000000..5bed83e3da --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-InterruptedDueToReplStateChange.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion InterruptedDueToReplStateChange error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale InterruptedDueToReplStateChange error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale InterruptedDueToReplStateChange error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: InterruptedDueToReplStateChange + code: 11602 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json new file mode 100644 index 0000000000..8c0cf00f22 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion LegacyNotPrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale LegacyNotPrimary error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale LegacyNotPrimary error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "LegacyNotPrimary", + "code": 10058, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml new file mode 100644 index 0000000000..483087a238 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-LegacyNotPrimary.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion LegacyNotPrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale LegacyNotPrimary error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale LegacyNotPrimary error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: LegacyNotPrimary + code: 10058 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json new file mode 100644 index 0000000000..99a828326c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion NotPrimaryNoSecondaryOk error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryNoSecondaryOk error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryNoSecondaryOk error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml new file mode 100644 index 0000000000..823bf3ee8e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryNoSecondaryOk.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion NotPrimaryNoSecondaryOk error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryNoSecondaryOk error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale NotPrimaryNoSecondaryOk error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryNoSecondaryOk + code: 13435 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json new file mode 100644 index 0000000000..ba2ea87106 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion NotPrimaryOrSecondary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryOrSecondary error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotPrimaryOrSecondary error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotPrimaryOrSecondary", + "code": 13436, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml new file mode 100644 index 0000000000..acee1ec566 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotPrimaryOrSecondary.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion NotPrimaryOrSecondary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotPrimaryOrSecondary error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale NotPrimaryOrSecondary error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotPrimaryOrSecondary + code: 13436 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json new file mode 100644 index 0000000000..8edd317a73 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion NotWritablePrimary error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotWritablePrimary error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale NotWritablePrimary error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "NotWritablePrimary", + "code": 10107, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml new file mode 100644 index 0000000000..8e126b0cb0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-NotWritablePrimary.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion NotWritablePrimary error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale NotWritablePrimary error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale NotWritablePrimary error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: NotWritablePrimary + code: 10107 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json new file mode 100644 index 0000000000..da8e4755eb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion PrimarySteppedDown error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale PrimarySteppedDown error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale PrimarySteppedDown error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "PrimarySteppedDown", + "code": 189, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml new file mode 100644 index 0000000000..c5e8a403b1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-PrimarySteppedDown.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion PrimarySteppedDown error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale PrimarySteppedDown error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale PrimarySteppedDown error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: PrimarySteppedDown + code: 189 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json new file mode 100644 index 0000000000..aa252e1dc4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.json @@ -0,0 +1,147 @@ +{ + "description": "Stale topologyVersion ShutdownInProgress error", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale ShutdownInProgress error (topologyVersion less)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore stale ShutdownInProgress error (topologyVersion equal)", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 0, + "errmsg": "ShutdownInProgress", + "code": 91, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml new file mode 100644 index 0000000000..6ad7677cf0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion-ShutdownInProgress.yml @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion ShutdownInProgress error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale ShutdownInProgress error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale ShutdownInProgress error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: ShutdownInProgress + code: 91 + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template new file mode 100644 index 0000000000..f69d440da0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/stale-topologyVersion.yml.template @@ -0,0 +1,65 @@ +# Autogenerated tests for SDAM error handling, see generate-error-tests.py +description: Stale topologyVersion {error_name} error +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore stale {error_name} error (topologyVersion less) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code} + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '0' + outcome: *outcome + +- description: Ignore stale {error_name} error (topologyVersion equal) + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 0 + errmsg: {error_name} + code: {error_code} + topologyVersion: + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json b/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json new file mode 100644 index 0000000000..b588807e08 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.json @@ -0,0 +1,98 @@ +{ + "description": "writeErrors field is ignored", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "description": "Primary A is discovered", + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "description": "Ignore command error with writeErrors field", + "applicationErrors": [ + { + "address": "a:27017", + "when": "afterHandshakeCompletes", + "maxWireVersion": 9, + "type": "command", + "response": { + "ok": 1, + "writeErrors": [ + { + "errmsg": "NotPrimaryNoSecondaryOk", + "code": 13435, + "index": 0 + } + ] + } + } + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + }, + "pool": { + "generation": 0 + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml b/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml new file mode 100644 index 0000000000..85f59d97d4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/errors/write_errors_ignored.yml @@ -0,0 +1,42 @@ +description: writeErrors field is ignored +uri: mongodb://a/?replicaSet=rs +phases: +- description: Primary A is discovered + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 9 + topologyVersion: &topologyVersion_1_1 + processId: + "$oid": '000000000000000000000001' + counter: + "$numberLong": '1' + outcome: &outcome + servers: + a:27017: + type: RSPrimary + setName: rs + topologyVersion: *topologyVersion_1_1 + pool: + generation: 0 + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + +- description: Ignore command error with writeErrors field + applicationErrors: + - address: a:27017 + when: afterHandshakeCompletes + maxWireVersion: 9 + type: command + response: + ok: 1 + writeErrors: + - { errmsg: NotPrimaryNoSecondaryOk, code: 13435, index: 0 } + outcome: *outcome diff --git a/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json b/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json new file mode 100644 index 0000000000..d2e34478e6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.json @@ -0,0 +1,28 @@ +{ + "description": "Load balancer can be discovered and only has the address property set", + "uri": "mongodb://a/?loadBalanced=true", + "phases": [ + { + "outcome": { + "servers": { + "a:27017": { + "type": "LoadBalancer", + "setName": null, + "setVersion": null, + "electionId": null, + "logicalSessionTimeoutMinutes": null, + "minWireVersion": null, + "maxWireVersion": null, + "topologyVersion": null + } + }, + "topologyType": "LoadBalanced", + "setName": null, + "logicalSessionTimeoutMinutes": null, + "maxSetVersion": null, + "maxElectionId": null, + "compatible": true + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml b/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml new file mode 100644 index 0000000000..20083d98be --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/load-balanced/discover_load_balancer.yml @@ -0,0 +1,25 @@ +description: "Load balancer can be discovered and only has the address property set" + +uri: "mongodb://a/?loadBalanced=true" + +phases: + + # There should be no monitoring in LoadBalanced mode, so no responses are necessary to get the topology into the + # correct state. + - outcome: + servers: + a:27017: + type: LoadBalancer + setName: null + setVersion: null + electionId: null + logicalSessionTimeoutMinutes: null + minWireVersion: null + maxWireVersion: null + topologyVersion: null + topologyType: LoadBalanced + setName: null + logicalSessionTimeoutMinutes: null + maxSetVersion: null + maxElectionId: null + compatible: true diff --git a/source/server-discovery-and-monitoring/tests/monitoring/README.md b/source/server-discovery-and-monitoring/tests/monitoring/README.md new file mode 100644 index 0000000000..fd463c48db --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/README.md @@ -0,0 +1,9 @@ +# SDAM Monitoring Tests + +The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their +conformance to the SDAM Monitoring spec. + +## Format + +The format of the tests follows the standard SDAM test and should be able to leverage the existing test runner in each +language for the SDAM tests. diff --git a/source/server-discovery-and-monitoring/tests/monitoring/README.rst b/source/server-discovery-and-monitoring/tests/monitoring/README.rst deleted file mode 100644 index 7c741544ec..0000000000 --- a/source/server-discovery-and-monitoring/tests/monitoring/README.rst +++ /dev/null @@ -1,12 +0,0 @@ -===================== -SDAM Monitoring Tests -===================== - -The YAML and JSON files in this directory tree are platform-independent tests -that drivers can use to prove their conformance to the SDAM Monitoring spec. - -Format ------- - -The format of the tests follows the standard SDAM test and should be able to leverage -the existing test runner in each language for the SDAM tests. diff --git a/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json b/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json new file mode 100644 index 0000000000..097203694e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.json @@ -0,0 +1,105 @@ +{ + "description": "Monitoring a discovered standalone connection", + "uri": "mongodb://a:27017/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "events": [ + { + "topology_opening_event": { + "topologyId": "42" + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "Unknown", + "servers": [] + }, + "newDescription": { + "topologyType": "Unknown", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + } + } + }, + { + "server_opening_event": { + "topologyId": "42", + "address": "a:27017" + } + }, + { + "server_description_changed_event": { + "topologyId": "42", + "address": "a:27017", + "previousDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + }, + "newDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Standalone" + } + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "Unknown", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + }, + "newDescription": { + "topologyType": "Single", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Standalone" + } + ] + } + } + } + ] + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml b/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml new file mode 100644 index 0000000000..1de96154cc --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/discovered_standalone.yml @@ -0,0 +1,70 @@ +description: "Monitoring a discovered standalone connection" +uri: "mongodb://a:27017/?directConnection=false" +phases: + - + responses: + - + - "a:27017" + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 21 } + + outcome: + events: + - + topology_opening_event: + topologyId: "42" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: [] + newDescription: + topologyType: "Unknown" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + server_opening_event: + topologyId: "42" + address: "a:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Standalone" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "Single" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Standalone" diff --git a/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.json b/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.json new file mode 100644 index 0000000000..09b1537193 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.json @@ -0,0 +1,93 @@ +{ + "description": "Monitoring a load balancer", + "uri": "mongodb://a:27017/?loadBalanced=true", + "phases": [ + { + "outcome": { + "events": [ + { + "topology_opening_event": { + "topologyId": "42" + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "Unknown", + "servers": [] + }, + "newDescription": { + "topologyType": "LoadBalanced", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + } + } + }, + { + "server_opening_event": { + "topologyId": "42", + "address": "a:27017" + } + }, + { + "server_description_changed_event": { + "topologyId": "42", + "address": "a:27017", + "previousDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + }, + "newDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "LoadBalancer" + } + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "LoadBalanced", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + }, + "newDescription": { + "topologyType": "LoadBalanced", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "LoadBalancer" + } + ] + } + } + } + ] + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml b/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml new file mode 100644 index 0000000000..b4eec2d2c6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/load_balancer.yml @@ -0,0 +1,65 @@ +description: "Monitoring a load balancer" +uri: "mongodb://a:27017/?loadBalanced=true" +phases: + - + outcome: + events: + - + topology_opening_event: + topologyId: "42" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: [] + newDescription: + topologyType: "LoadBalanced" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + server_opening_event: + topologyId: "42" + address: "a:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "LoadBalancer" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "LoadBalanced" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "LoadBalanced" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "LoadBalancer" diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json index 33010d49fb..41d048729d 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "setVersion": 1, @@ -18,7 +19,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml index ed992f849c..e5ca9cd90d 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_no_primary.yml @@ -1,112 +1,112 @@ description: "Monitoring a topology that is a replica set with no primary connected" uri: "mongodb://a,b" -phases: - - - responses: - - +phases: + - + responses: + - - "a:27017" - - + - ok: 1 - ismaster: false + helloOk: true + isWritablePrimary: false secondary: true setName: "rs" setVersion: 1 primary: "b:27017" - hosts: + hosts: - "a:27017" - "b:27017" minWireVersion: 0 - maxWireVersion: 4 - outcome: - events: - - - topology_opening_event: + maxWireVersion: 21 + outcome: + events: + - + topology_opening_event: topologyId: "42" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" servers: [] - newDescription: + newDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "a:27017" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "b:27017" - - - server_description_changed_event: + - + server_description_changed_event: topologyId: "42" address: "a:27017" - previousDescription: + previousDescription: address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "b:27017" setName: "rs" type: "RSSecondary" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: topologyType: "ReplicaSetNoPrimary" setName: "rs" - servers: - - + servers: + - address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "b:27017" setName: "rs" type: "RSSecondary" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "PossiblePrimary" - diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json index 04caeba652..3ccc127d1d 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "setVersion": 1, "primary": "a:27017", @@ -17,7 +18,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml index e42735d41c..256895bb28 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_primary.yml @@ -1,111 +1,111 @@ description: "Monitoring a topology that is a replica set with a primary connected" uri: "mongodb://a,b" -phases: - - - responses: - - +phases: + - + responses: + - - "a:27017" - - + - ok: 1 - ismaster: true + helloOk: true + isWritablePrimary: true setName: "rs" setVersion: 1 primary: "a:27017" - hosts: + hosts: - "a:27017" - "b:27017" minWireVersion: 0 - maxWireVersion: 4 - outcome: - events: - - - topology_opening_event: + maxWireVersion: 21 + outcome: + events: + - + topology_opening_event: topologyId: "42" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" servers: [] - newDescription: + newDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "a:27017" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "b:27017" - - - server_description_changed_event: + - + server_description_changed_event: topologyId: "42" address: "a:27017" - previousDescription: + previousDescription: address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "a:27017" setName: "rs" type: "RSPrimary" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: topologyType: "ReplicaSetWithPrimary" setName: "rs" - servers: - - + servers: + - address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "a:27017" setName: "rs" type: "RSPrimary" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json index a14456cdba..dc6fbe7e7d 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.json @@ -3,30 +3,7 @@ "uri": "mongodb://a,b/", "phases": [ { - "responses": [ - [ - "a:27017", - { - "ok": 1, - "ismaster": true, - "setName": "rs", - "setVersion": 1, - "primary": "a:27017", - "hosts": [ - "a:27017" - ], - "minWireVersion": 0, - "maxWireVersion": 4 - } - ], - [ - "b:27017", - { - "ok": 1, - "ismaster": true - } - ] - ], + "responses": [], "outcome": { "events": [ { @@ -73,7 +50,39 @@ "topologyId": "42", "address": "b:27017" } - }, + } + ] + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "setVersion": 1, + "primary": "a:27017", + "hosts": [ + "a:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true + } + ] + ], + "outcome": { + "events": [ { "server_description_changed_event": { "topologyId": "42", diff --git a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml index e156ec7100..25c006861a 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml +++ b/source/server-discovery-and-monitoring/tests/monitoring/replica_set_with_removal.yml @@ -2,22 +2,7 @@ description: "Monitoring a replica set with non member" uri: "mongodb://a,b/" phases: - - responses: - - - - "a:27017" - - { - ok: 1, - ismaster: true, - setName: "rs", - setVersion: 1.0, - primary: "a:27017", - hosts: [ "a:27017" ], - minWireVersion: 0, - maxWireVersion: 4 - } - - - - "b:27017" - - { ok: 1, ismaster: true } + responses: [] outcome: events: - @@ -52,6 +37,26 @@ phases: server_opening_event: topologyId: "42" address: "b:27017" + - + responses: + - + - "a:27017" + - { + ok: 1, + helloOk: true, + isWritablePrimary: true, + setName: "rs", + setVersion: 1.0, + primary: "a:27017", + hosts: [ "a:27017" ], + minWireVersion: 0, + maxWireVersion: 21 + } + - + - "b:27017" + - { ok: 1, helloOk: true, isWritablePrimary: true } + outcome: + events: - server_description_changed_event: topologyId: "42" diff --git a/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json b/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json index 5cc4fa818a..1f4e5c1d71 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json +++ b/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "setVersion": 1, "primary": "a:27017", @@ -17,7 +18,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 21 } ] ], @@ -37,6 +38,7 @@ }, "newDescription": { "topologyType": "ReplicaSetNoPrimary", + "setName": "rs", "servers": [ { "address": "a:27017", @@ -98,6 +100,7 @@ "topologyId": "42", "previousDescription": { "topologyType": "ReplicaSetNoPrimary", + "setName": "rs", "servers": [ { "address": "a:27017", diff --git a/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml b/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml index 84448d9fc9..69d0500de1 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml +++ b/source/server-discovery-and-monitoring/tests/monitoring/required_replica_set.yml @@ -7,13 +7,14 @@ phases: - "a:27017" - { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", setVersion: 1.0, primary: "a:27017", hosts: [ "a:27017", "b:27017" ], minWireVersion: 0, - maxWireVersion: 4 + maxWireVersion: 21 } outcome: events: @@ -28,6 +29,7 @@ phases: servers: [] newDescription: topologyType: "ReplicaSetNoPrimary" + setName: "rs" servers: - address: "a:27017" @@ -72,6 +74,7 @@ phases: topologyId: "42" previousDescription: topologyType: "ReplicaSetNoPrimary" + setName: "rs" servers: - address: "a:27017" diff --git a/source/server-discovery-and-monitoring/tests/monitoring/standalone.json b/source/server-discovery-and-monitoring/tests/monitoring/standalone.json index 5d40286c97..f375a383ca 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/standalone.json +++ b/source/server-discovery-and-monitoring/tests/monitoring/standalone.json @@ -1,6 +1,6 @@ { - "description": "Monitoring a standalone connection", - "uri": "mongodb://a:27017", + "description": "Monitoring a direct connection", + "uri": "mongodb://a:27017/?directConnection=true", "phases": [ { "responses": [ @@ -8,9 +8,10 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/monitoring/standalone.yml b/source/server-discovery-and-monitoring/tests/monitoring/standalone.yml index aff3f7322c..0c3ed64601 100644 --- a/source/server-discovery-and-monitoring/tests/monitoring/standalone.yml +++ b/source/server-discovery-and-monitoring/tests/monitoring/standalone.yml @@ -1,11 +1,11 @@ -description: "Monitoring a standalone connection" -uri: "mongodb://a:27017" +description: "Monitoring a direct connection" +uri: "mongodb://a:27017/?directConnection=true" phases: - responses: - - "a:27017" - - { ok: 1, ismaster: true, minWireVersion: 0, maxWireVersion: 4 } + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 21 } outcome: events: diff --git a/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json b/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json new file mode 100644 index 0000000000..4d046ff8ed --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.json @@ -0,0 +1,115 @@ +{ + "description": "Monitoring a direct connection - suppress update events for equal server descriptions", + "uri": "mongodb://a:27017/?directConnection=true", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "events": [ + { + "topology_opening_event": { + "topologyId": "42" + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "Unknown", + "servers": [] + }, + "newDescription": { + "topologyType": "Single", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + } + } + }, + { + "server_opening_event": { + "topologyId": "42", + "address": "a:27017" + } + }, + { + "server_description_changed_event": { + "topologyId": "42", + "address": "a:27017", + "previousDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + }, + "newDescription": { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Standalone" + } + } + }, + { + "topology_description_changed_event": { + "topologyId": "42", + "previousDescription": { + "topologyType": "Single", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Unknown" + } + ] + }, + "newDescription": { + "topologyType": "Single", + "servers": [ + { + "address": "a:27017", + "arbiters": [], + "hosts": [], + "passives": [], + "type": "Standalone" + } + ] + } + } + } + ] + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml b/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml new file mode 100644 index 0000000000..255ec2dd43 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/monitoring/standalone_suppress_equal_description_changes.yml @@ -0,0 +1,73 @@ +description: "Monitoring a direct connection - suppress update events for equal server descriptions" +uri: "mongodb://a:27017/?directConnection=true" +phases: + - + responses: + - + - "a:27017" + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 21 } + - + - "a:27017" + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 21 } + + outcome: + events: + - + topology_opening_event: + topologyId: "42" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Unknown" + servers: [] + newDescription: + topologyType: "Single" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + - + server_opening_event: + topologyId: "42" + address: "a:27017" + - + server_description_changed_event: + topologyId: "42" + address: "a:27017" + previousDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Standalone" + - + topology_description_changed_event: + topologyId: "42" + previousDescription: + topologyType: "Single" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Unknown" + newDescription: + topologyType: "Single" + servers: + - + address: "a:27017" + arbiters: [] + hosts: [] + passives: [] + type: "Standalone" diff --git a/source/server-discovery-and-monitoring/tests/rs/compatible.json b/source/server-discovery-and-monitoring/tests/rs/compatible.json index d670770f6d..dfd5d57dfa 100644 --- a/source/server-discovery-and-monitoring/tests/rs/compatible.json +++ b/source/server-discovery-and-monitoring/tests/rs/compatible.json @@ -8,21 +8,23 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ diff --git a/source/server-discovery-and-monitoring/tests/rs/compatible.yml b/source/server-discovery-and-monitoring/tests/rs/compatible.yml index 3e041d6030..1973f7dd40 100644 --- a/source/server-discovery-and-monitoring/tests/rs/compatible.yml +++ b/source/server-discovery-and-monitoring/tests/rs/compatible.yml @@ -1,19 +1,23 @@ description: "Replica set member with large maxWireVersion" + uri: "mongodb://a,b/?replicaSet=rs" + phases: [ { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], @@ -38,4 +42,4 @@ phases: [ compatible: true } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.json b/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.json new file mode 100644 index 0000000000..95e03ea958 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.json @@ -0,0 +1,40 @@ +{ + "description": "Replica set member and an unknown server", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "setName": "rs", + "logicalSessionTimeoutMinutes": null, + "compatible": true + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml b/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml new file mode 100644 index 0000000000..6cd208bff6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/compatible_unknown.yml @@ -0,0 +1,34 @@ +description: "Replica set member and an unknown server" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: [ + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + setName: "rs", + hosts: ["a:27017", "b:27017"], + minWireVersion: 0, + maxWireVersion: 21 + }], + ], + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs" + }, + "b:27017": { + type: "Unknown", + } + }, + topologyType: "ReplicaSetWithPrimary", + setName: "rs", + logicalSessionTimeoutMinutes: null, + compatible: true + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.json b/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.json new file mode 100644 index 0000000000..c8b41d30ca --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.json @@ -0,0 +1,167 @@ +{ + "description": "Static setVersion (DSC) is compatible with both pre and post DRIVERS-2412", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000005" + } + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000006" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000006" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000006" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000006" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000006" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.yml b/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.yml new file mode 100644 index 0000000000..c20b0027ab --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/disaggregated_storage_setversion.yml @@ -0,0 +1,118 @@ +# Disaggregated storage clusters use a constant setVersion (typically 1) instead of +# incrementing it on each reconfig. This test validates that primary staleness detection +# works correctly using electionId when setVersion doesn't change. +description: Static setVersion (DSC) is compatible with both pre and post DRIVERS-2412 +uri: "mongodb://a/?replicaSet=rs" +phases: + # Phase 1: Initial state - A is primary with electionId=5, setVersion=1 + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + minWireVersion: 0 + maxWireVersion: 17 + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000005"} + # Phase 2: B becomes primary with newer electionId=6, but setVersion stays at 1 + # The driver should accept B as the new primary and mark A as Unknown + - responses: + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000006"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + setVersion: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000006"} + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000006"} + # Phase 3: Old primary A tries to come back with stale electionId=5 + # Even though setVersion is still 1 (unchanged), the driver should reject A + # because its electionId is older than the current maxElectionId=6 + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + setVersion: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000006"} + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000006"} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.json b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.json index ced7baeb65..803462b156 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.json +++ b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.json @@ -1,6 +1,6 @@ { - "description": "Discover arbiters", - "uri": "mongodb://a/?replicaSet=rs", + "description": "Discover arbiters with directConnection URI option", + "uri": "mongodb://a/?directConnection=false", "phases": [ { "responses": [ @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], @@ -17,7 +18,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml index 831f9da0ed..6e0e8743fa 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml +++ b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters.yml @@ -1,6 +1,6 @@ -description: "Discover arbiters" +description: "Discover arbiters with directConnection URI option" -uri: "mongodb://a/?replicaSet=rs" +uri: "mongodb://a/?directConnection=false" phases: [ @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], arbiters: ["b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -40,4 +41,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json new file mode 100644 index 0000000000..e58d7c7fb4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.json @@ -0,0 +1,42 @@ +{ + "description": "Discover arbiters with replicaSet URI option", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "arbiters": [ + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml new file mode 100644 index 0000000000..8f9b53a12e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_arbiters_replicaset.yml @@ -0,0 +1,44 @@ +description: "Discover arbiters with replicaSet URI option" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + arbiters: ["b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_ghost.json b/source/server-discovery-and-monitoring/tests/rs/discover_ghost.json new file mode 100644 index 0000000000..3b7fc836ec --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_ghost.json @@ -0,0 +1,32 @@ +{ + "description": "Discover ghost with directConnection URI option", + "uri": "mongodb://b/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "isreplicaset": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "b:27017": { + "type": "RSGhost", + "setName": null + } + }, + "topologyType": "Unknown", + "logicalSessionTimeoutMinutes": null, + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_ghost.yml b/source/server-discovery-and-monitoring/tests/rs/discover_ghost.yml new file mode 100644 index 0000000000..cb48115f13 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_ghost.yml @@ -0,0 +1,36 @@ +description: "Discover ghost with directConnection URI option" + +uri: "mongodb://b/?directConnection=false" + +phases: [ + + { + responses: [ + + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + isreplicaset: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "b:27017": { + + type: "RSGhost", + setName: + } + }, + topologyType: "Unknown", + logicalSessionTimeoutMinutes: null, + setName: + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json new file mode 100644 index 0000000000..1a8457983b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.json @@ -0,0 +1,36 @@ +{ + "description": "Discover ghost with replicaSet URI option", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "isreplicaset": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null + }, + "b:27017": { + "type": "RSGhost", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml new file mode 100644 index 0000000000..7c02dcc252 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_ghost_replicaset.yml @@ -0,0 +1,42 @@ +description: "Discover ghost with replicaSet URI option" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + isreplicaset: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Unknown", + setName: + }, + + "b:27017": { + + type: "RSGhost", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_hidden.json b/source/server-discovery-and-monitoring/tests/rs/discover_hidden.json new file mode 100644 index 0000000000..10bd51edeb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_hidden.json @@ -0,0 +1,46 @@ +{ + "description": "Discover hidden with directConnection URI option", + "uri": "mongodb://a/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hidden": true, + "hosts": [ + "c:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "c:27017": { + "type": "Unknown", + "setName": null + }, + "d:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_hidden.yml b/source/server-discovery-and-monitoring/tests/rs/discover_hidden.yml new file mode 100644 index 0000000000..b8c13ca85e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_hidden.yml @@ -0,0 +1,51 @@ +description: "Discover hidden with directConnection URI option" + +uri: "mongodb://a/?directConnection=false" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + hidden: true, + hosts: ["c:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }], + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSOther", + setName: "rs" + }, + + "c:27017": { + + type: "Unknown", + setName: + }, + + "d:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json new file mode 100644 index 0000000000..63cf558675 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.json @@ -0,0 +1,46 @@ +{ + "description": "Discover hidden with replicaSet URI option", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hidden": true, + "hosts": [ + "c:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "c:27017": { + "type": "Unknown", + "setName": null + }, + "d:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml new file mode 100644 index 0000000000..a85271ae76 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_hidden_replicaset.yml @@ -0,0 +1,51 @@ +description: "Discover hidden with replicaSet URI option" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + hidden: true, + hosts: ["c:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }], + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSOther", + setName: "rs" + }, + + "c:27017": { + + type: "Unknown", + setName: + }, + + "d:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_passives.json b/source/server-discovery-and-monitoring/tests/rs/discover_passives.json index e46249d668..0a292c675c 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_passives.json +++ b/source/server-discovery-and-monitoring/tests/rs/discover_passives.json @@ -1,6 +1,6 @@ { - "description": "Discover passives", - "uri": "mongodb://a/?replicaSet=rs", + "description": "Discover passives with directConnection URI option", + "uri": "mongodb://a/?directConnection=false", "phases": [ { "responses": [ @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], @@ -17,7 +18,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -43,7 +44,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "passive": true, "hosts": [ @@ -54,7 +56,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_passives.yml b/source/server-discovery-and-monitoring/tests/rs/discover_passives.yml index 14ca42e454..12b303f86f 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_passives.yml +++ b/source/server-discovery-and-monitoring/tests/rs/discover_passives.yml @@ -1,6 +1,6 @@ -description: "Discover passives" +description: "Discover passives with directConnection URI option" -uri: "mongodb://a/?replicaSet=rs" +uri: "mongodb://a/?directConnection=false" phases: [ @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], passives: ["b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -46,14 +47,15 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, passive: true, hosts: ["a:27017"], passives: ["b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -78,4 +80,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json new file mode 100644 index 0000000000..c48fd47625 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.json @@ -0,0 +1,80 @@ +{ + "description": "Discover passives with replicaSet URI option", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "passives": [ + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "passive": true, + "hosts": [ + "a:27017" + ], + "passives": [ + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml new file mode 100644 index 0000000000..580ad8c038 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_passives_replicaset.yml @@ -0,0 +1,83 @@ +description: "Discover passives with replicaSet URI option" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + passives: ["b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + }, + { + responses: [ + + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + passive: true, + hosts: ["a:27017"], + passives: ["b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "RSSecondary", + setName: "rs" + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_primary.json b/source/server-discovery-and-monitoring/tests/rs/discover_primary.json index ea2cce9b72..04e7a4984c 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_primary.json +++ b/source/server-discovery-and-monitoring/tests/rs/discover_primary.json @@ -1,6 +1,6 @@ { - "description": "Replica set discovery from primary", - "uri": "mongodb://a/?replicaSet=rs", + "description": "Discover primary with directConnection URI option", + "uri": "mongodb://a/?directConnection=false", "phases": [ { "responses": [ @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_primary.yml b/source/server-discovery-and-monitoring/tests/rs/discover_primary.yml index 8eb078d958..bd263cc7e6 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_primary.yml +++ b/source/server-discovery-and-monitoring/tests/rs/discover_primary.yml @@ -1,6 +1,6 @@ -description: "Replica set discovery from primary" +description: "Discover primary with directConnection URI option" -uri: "mongodb://a/?replicaSet=rs" +uri: "mongodb://a/?directConnection=false" phases: [ @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -39,4 +40,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json new file mode 100644 index 0000000000..3cdcfdcee2 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.json @@ -0,0 +1,40 @@ +{ + "description": "Discover primary with replicaSet URI option", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml new file mode 100644 index 0000000000..7c7ad5ada9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_primary_replicaset.yml @@ -0,0 +1,43 @@ +description: "Discover primary with replicaSet URI option" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + setName: "rs", + hosts: ["a:27017", "b:27017"], + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_rsother.json b/source/server-discovery-and-monitoring/tests/rs/discover_rsother.json new file mode 100644 index 0000000000..9c3b8d8b7d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_rsother.json @@ -0,0 +1,45 @@ +{ + "description": "Discover RSOther with directConnection URI option", + "uri": "mongodb://b/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": false, + "hosts": [ + "c:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "b:27017": { + "type": "RSOther", + "setName": "rs" + }, + "c:27017": { + "type": "Unknown", + "setName": null + }, + "d:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_rsother.yml b/source/server-discovery-and-monitoring/tests/rs/discover_rsother.yml new file mode 100644 index 0000000000..20953c1fb0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_rsother.yml @@ -0,0 +1,50 @@ +description: "Discover RSOther with directConnection URI option" + +uri: "mongodb://b/?directConnection=false" + +phases: [ + + { + responses: [ + + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: false, + hosts: ["c:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "b:27017": { + + type: "RSOther", + setName: "rs" + }, + + "c:27017": { + + type: "Unknown", + setName: + }, + + "d:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json new file mode 100644 index 0000000000..3da9efb066 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.json @@ -0,0 +1,66 @@ +{ + "description": "Discover RSOther with replicaSet URI option", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hidden": true, + "hosts": [ + "c:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": false, + "hosts": [ + "c:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "b:27017": { + "type": "RSOther", + "setName": "rs" + }, + "c:27017": { + "type": "Unknown", + "setName": null + }, + "d:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml new file mode 100644 index 0000000000..34cfb743ff --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_rsother_replicaset.yml @@ -0,0 +1,68 @@ +description: "Discover RSOther with replicaSet URI option" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + hidden: true, + hosts: ["c:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }], + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: false, + hosts: ["c:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSOther", + setName: "rs" + }, + + "b:27017": { + + type: "RSOther", + setName: "rs" + }, + + "c:27017": { + + type: "Unknown", + setName: + }, + + "d:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_secondary.json b/source/server-discovery-and-monitoring/tests/rs/discover_secondary.json index 7210b3845c..64a1ce31e3 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_secondary.json +++ b/source/server-discovery-and-monitoring/tests/rs/discover_secondary.json @@ -1,6 +1,6 @@ { - "description": "Replica set discovery from secondary", - "uri": "mongodb://b/?replicaSet=rs", + "description": "Discover secondary with directConnection URI option", + "uri": "mongodb://b/?directConnection=false", "phases": [ { "responses": [ @@ -8,7 +8,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -16,7 +17,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_secondary.yml b/source/server-discovery-and-monitoring/tests/rs/discover_secondary.yml index 221e3693bd..4b3b089ce9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discover_secondary.yml +++ b/source/server-discovery-and-monitoring/tests/rs/discover_secondary.yml @@ -1,6 +1,6 @@ -description: "Replica set discovery from secondary" +description: "Discover secondary with directConnection URI option" -uri: "mongodb://b/?replicaSet=rs" +uri: "mongodb://b/?directConnection=false" phases: [ @@ -10,12 +10,13 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -40,4 +41,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json b/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json new file mode 100644 index 0000000000..d230f976a2 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.json @@ -0,0 +1,41 @@ +{ + "description": "Discover secondary with replicaSet URI option", + "uri": "mongodb://b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml b/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml new file mode 100644 index 0000000000..84fd2c8dcd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/discover_secondary_replicaset.yml @@ -0,0 +1,44 @@ +description: "Discover secondary with replicaSet URI option" + +uri: "mongodb://b/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + setName: "rs", + hosts: ["a:27017", "b:27017"], + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Unknown", + setName: + }, + + "b:27017": { + + type: "RSSecondary", + setName: "rs" + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/discovery.json b/source/server-discovery-and-monitoring/tests/rs/discovery.json index 57ed568e3b..e9deaa7587 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discovery.json +++ b/source/server-discovery-and-monitoring/tests/rs/discovery.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -17,7 +18,7 @@ "c:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -47,7 +48,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "primary": "d:27017", @@ -57,7 +59,7 @@ "d:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -91,7 +93,8 @@ "d:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "b:27017", @@ -100,7 +103,7 @@ "e:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -134,7 +137,8 @@ "c:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -143,7 +147,7 @@ "c:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/discovery.yml b/source/server-discovery-and-monitoring/tests/rs/discovery.yml index fc666a05f3..03c4780261 100644 --- a/source/server-discovery-and-monitoring/tests/rs/discovery.yml +++ b/source/server-discovery-and-monitoring/tests/rs/discovery.yml @@ -11,12 +11,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017", "c:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -55,13 +56,14 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", primary: "d:27017", hosts: ["b:27017", "c:27017", "d:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -106,11 +108,12 @@ phases: [ ["d:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["b:27017", "c:27017", "d:27017", "e:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -156,12 +159,13 @@ phases: [ ["c:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017", "c:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -200,4 +204,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.json b/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.json new file mode 100644 index 0000000000..2fcea2bf66 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.json @@ -0,0 +1,92 @@ +{ + "description": "ElectionId is considered higher precedence than setVersion", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.yml b/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.yml new file mode 100644 index 0000000000..4b9563fcfd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/electionId_precedence_setVersion.yml @@ -0,0 +1,62 @@ +description: ElectionId is considered higher precedence than setVersion +uri: "mongodb://a/?replicaSet=rs" +phases: + - responses: + - - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: + $oid: "000000000000000000000001" + minWireVersion: 0 + maxWireVersion: 17 + - - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 2 # Even though "B" reports the newer setVersion, "A" will report the newer electionId which should allow it to remain the primary + electionId: + $oid: "000000000000000000000001" + minWireVersion: 0 + maxWireVersion: 17 + - - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: + $oid: "000000000000000000000002" + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + "a:27017": + type: RSPrimary + setName: rs + setVersion: 1 + electionId: + $oid: "000000000000000000000002" + "b:27017": + type: Unknown + setName: null + setVersion: null + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: + $oid: "000000000000000000000002" diff --git a/source/server-discovery-and-monitoring/tests/rs/equal_electionids.json b/source/server-discovery-and-monitoring/tests/rs/equal_electionids.json index 8a5aa8cd67..f1deedf9f4 100644 --- a/source/server-discovery-and-monitoring/tests/rs/equal_electionids.json +++ b/source/server-discovery-and-monitoring/tests/rs/equal_electionids.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,14 +20,15 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -37,7 +39,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -60,7 +62,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/equal_electionids.yml b/source/server-discovery-and-monitoring/tests/rs/equal_electionids.yml index 1e6294bdfd..7c4c4e6515 100644 --- a/source/server-discovery-and-monitoring/tests/rs/equal_electionids.yml +++ b/source/server-discovery-and-monitoring/tests/rs/equal_electionids.yml @@ -9,23 +9,25 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -48,6 +50,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.json b/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.json deleted file mode 100644 index bf22cbb0eb..0000000000 --- a/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "description": "Ghost discovered", - "uri": "mongodb://a,b/?replicaSet=rs", - "phases": [ - { - "responses": [ - [ - "b:27017", - { - "ok": 1, - "ismaster": false, - "isreplicaset": true, - "minWireVersion": 0, - "maxWireVersion": 6 - } - ] - ], - "outcome": { - "servers": { - "a:27017": { - "type": "Unknown", - "setName": null - }, - "b:27017": { - "type": "RSGhost", - "setName": null - } - }, - "topologyType": "ReplicaSetNoPrimary", - "logicalSessionTimeoutMinutes": null, - "setName": "rs" - } - } - ] -} diff --git a/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.yml b/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.yml deleted file mode 100644 index 97cc92b9ed..0000000000 --- a/source/server-discovery-and-monitoring/tests/rs/ghost_discovered.yml +++ /dev/null @@ -1,41 +0,0 @@ -description: "Ghost discovered" - -uri: "mongodb://a,b/?replicaSet=rs" - -phases: [ - - { - responses: [ - - ["b:27017", { - - ok: 1, - ismaster: false, - isreplicaset: true, - minWireVersion: 0, - maxWireVersion: 6 - }] - ], - - outcome: { - - servers: { - - "a:27017": { - - type: "Unknown", - setName: - }, - - "b:27017": { - - type: "RSGhost", - setName: - } - }, - topologyType: "ReplicaSetNoPrimary", - logicalSessionTimeoutMinutes: null, - setName: "rs" - } - } -] diff --git a/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json b/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json index a67db57d0c..085e81e266 100644 --- a/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json +++ b/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml b/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml index 557286e0b8..c8bd27f8b9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml +++ b/source/server-discovery-and-monitoring/tests/rs/hosts_differ_from_seeds.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -33,4 +34,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json b/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json new file mode 100644 index 0000000000..bda18d9f6f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.json @@ -0,0 +1,56 @@ +{ + "description": "Incompatible arbiter", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "arbiterOnly": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 1 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "RSArbiter", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "setName": "rs", + "logicalSessionTimeoutMinutes": null, + "compatible": false + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml b/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml new file mode 100644 index 0000000000..2e46ff2951 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_arbiter.yml @@ -0,0 +1,36 @@ +description: "Incompatible arbiter" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + setName: "rs" + hosts: ["a:27017", "b:27017"] + minWireVersion: 0 + maxWireVersion: 21 + - + - "b:27017" + - ok: 1 + helloOk: true + arbiterOnly: true + setName: "rs" + hosts: ["a:27017", "b:27017"] + minWireVersion: 0 + maxWireVersion: 1 + outcome: + servers: + "a:27017": + type: "RSPrimary" + setName: "rs" + "b:27017": + type: "RSArbiter" + setName: "rs" + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + logicalSessionTimeoutMinutes: ~ + compatible: false diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json b/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json new file mode 100644 index 0000000000..9d82e31682 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.json @@ -0,0 +1,51 @@ +{ + "description": "Incompatible ghost", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isreplicaset": true, + "minWireVersion": 0, + "maxWireVersion": 1 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "RSGhost", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "setName": "rs", + "logicalSessionTimeoutMinutes": null, + "compatible": false + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml b/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml new file mode 100644 index 0000000000..c99badf4a3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_ghost.yml @@ -0,0 +1,34 @@ +description: "Incompatible ghost" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + setName: "rs" + hosts: ["a:27017", "b:27017"] + minWireVersion: 0 + maxWireVersion: 21 + - + - "b:27017" + - ok: 1 + helloOk: true + isreplicaset: true + minWireVersion: 0 + maxWireVersion: 1 + outcome: + servers: + "a:27017": + type: "RSPrimary" + setName: "rs" + "b:27017": + type: "RSGhost" + setName: + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + logicalSessionTimeoutMinutes: ~ + compatible: false diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_other.json b/source/server-discovery-and-monitoring/tests/rs/incompatible_other.json new file mode 100644 index 0000000000..149ba01142 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_other.json @@ -0,0 +1,56 @@ +{ + "description": "Incompatible other", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "hidden": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 1 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "RSOther", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "setName": "rs", + "logicalSessionTimeoutMinutes": null, + "compatible": false + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/incompatible_other.yml b/source/server-discovery-and-monitoring/tests/rs/incompatible_other.yml new file mode 100644 index 0000000000..16452e7b02 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/incompatible_other.yml @@ -0,0 +1,36 @@ +description: "Incompatible other" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + setName: "rs" + hosts: ["a:27017", "b:27017"] + minWireVersion: 0 + maxWireVersion: 21 + - + - "b:27017" + - ok: 1 + helloOk: true + hidden: true + setName: "rs" + hosts: ["a:27017", "b:27017"] + minWireVersion: 0 + maxWireVersion: 1 + outcome: + servers: + "a:27017": + type: "RSPrimary" + setName: "rs" + "b:27017": + type: "RSOther" + setName: "rs" + topologyType: "ReplicaSetWithPrimary" + setName: "rs" + logicalSessionTimeoutMinutes: ~ + compatible: false diff --git a/source/server-discovery-and-monitoring/tests/rs/ls_timeout.json b/source/server-discovery-and-monitoring/tests/rs/ls_timeout.json index 6860742c9e..c68790ddfd 100644 --- a/source/server-discovery-and-monitoring/tests/rs/ls_timeout.json +++ b/source/server-discovery-and-monitoring/tests/rs/ls_timeout.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017", @@ -19,7 +20,7 @@ "setName": "rs", "logicalSessionTimeoutMinutes": 3, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -53,10 +54,11 @@ "d:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "isreplicaset": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -90,7 +92,8 @@ "e:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "hosts": [ "a:27017", "b:27017", @@ -101,7 +104,7 @@ "setName": "rs", "arbiterOnly": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -136,7 +139,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -148,7 +152,7 @@ "setName": "rs", "logicalSessionTimeoutMinutes": 2, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -184,12 +188,13 @@ "c:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "setName": "rs", "hidden": true, "logicalSessionTimeoutMinutes": 1, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -226,7 +231,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -238,7 +244,7 @@ "setName": "rs", "logicalSessionTimeoutMinutes": null, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/ls_timeout.yml b/source/server-discovery-and-monitoring/tests/rs/ls_timeout.yml index 7690c83a49..8df7699532 100644 --- a/source/server-discovery-and-monitoring/tests/rs/ls_timeout.yml +++ b/source/server-discovery-and-monitoring/tests/rs/ls_timeout.yml @@ -8,12 +8,13 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017", "c:27017", "d:27017", "e:27017"], setName: "rs", logicalSessionTimeoutMinutes: 3, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ], outcome: { @@ -46,10 +47,11 @@ phases: [ responses: [ ["d:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, isreplicaset: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ], outcome: { @@ -82,12 +84,13 @@ phases: [ responses: [ ["e:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, hosts: ["a:27017", "b:27017", "c:27017", "d:27017", "e:27017"], setName: "rs", arbiterOnly: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -121,13 +124,14 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017", "c:27017", "d:27017", "e:27017"], setName: "rs", logicalSessionTimeoutMinutes: 2, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ], outcome: { @@ -162,12 +166,13 @@ phases: [ responses: [ ["c:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, setName: "rs", hidden: true, logicalSessionTimeoutMinutes: 1, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ], outcome: { @@ -202,13 +207,14 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017", "c:27017", "d:27017", "e:27017"], setName: "rs", logicalSessionTimeoutMinutes: null, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -240,4 +246,4 @@ phases: [ setName: "rs", } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.json b/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.json new file mode 100644 index 0000000000..0045591db9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.json @@ -0,0 +1,227 @@ +{ + "description": "Member list is updated when setVersion and electionId remain the same", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "c:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "c:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "c:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.yml new file mode 100644 index 0000000000..7920c7e06b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/member_list_update_with_unchanged_setversion_and_electionid.yml @@ -0,0 +1,161 @@ +# This test validates that the member list (hosts) is updated even when +# setVersion and electionId remain unchanged. +description: Member list is updated when setVersion and electionId remain the same +uri: "mongodb://a/?replicaSet=rs" +phases: + # Phase 1: Initial state - A is primary with two members (a, b) + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + minWireVersion: 0 + maxWireVersion: 17 + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000001"} + # Phase 2: Primary reports a new member c has been added, but setVersion and + # electionId remain unchanged. The driver should add c to the topology. + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + - "c:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + c:27017: + type: Unknown + setName: null + setVersion: null + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000001"} + # Phase 3: c responds and becomes a secondary + - responses: + - + - "c:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hosts: + - "a:27017" + - "b:27017" + - "c:27017" + setName: rs + setVersion: 1 + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + c:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000001"} + # Phase 4: Primary reports c has been removed, but setVersion and + # electionId remain unchanged. The driver should remove c from the topology. + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000001"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000001"} + diff --git a/source/server-discovery-and-monitoring/tests/rs/member_reconfig.json b/source/server-discovery-and-monitoring/tests/rs/member_reconfig.json index 336acff023..a05fed0efb 100644 --- a/source/server-discovery-and-monitoring/tests/rs/member_reconfig.json +++ b/source/server-discovery-and-monitoring/tests/rs/member_reconfig.json @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,13 +42,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/member_reconfig.yml b/source/server-discovery-and-monitoring/tests/rs/member_reconfig.yml index a525bd1e66..6ed5f80986 100644 --- a/source/server-discovery-and-monitoring/tests/rs/member_reconfig.yml +++ b/source/server-discovery-and-monitoring/tests/rs/member_reconfig.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -46,11 +47,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -69,4 +71,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/member_standalone.json b/source/server-discovery-and-monitoring/tests/rs/member_standalone.json index a97dfabf52..db100db9f3 100644 --- a/source/server-discovery-and-monitoring/tests/rs/member_standalone.json +++ b/source/server-discovery-and-monitoring/tests/rs/member_standalone.json @@ -8,9 +8,10 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -32,13 +33,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/member_standalone.yml b/source/server-discovery-and-monitoring/tests/rs/member_standalone.yml index 8850b9f9b6..8b98383c0e 100644 --- a/source/server-discovery-and-monitoring/tests/rs/member_standalone.yml +++ b/source/server-discovery-and-monitoring/tests/rs/member_standalone.yml @@ -10,9 +10,10 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -38,11 +39,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -61,4 +63,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.json b/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.json new file mode 100644 index 0000000000..c5109026bc --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.json @@ -0,0 +1,167 @@ +{ + "description": "DSC to ASC reverse migration - ASC primary with higher setVersion is accepted", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000005" + } + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1000, + "electionId": { + "$oid": "000000000000000000000006" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1000, + "electionId": { + "$oid": "000000000000000000000006" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1000, + "maxElectionId": { + "$oid": "000000000000000000000006" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000005" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "setVersion": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1000, + "electionId": { + "$oid": "000000000000000000000006" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1000, + "maxElectionId": { + "$oid": "000000000000000000000006" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.yml b/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.yml new file mode 100644 index 0000000000..6bb88f0fc1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/migration_from_disaggregated_storage.yml @@ -0,0 +1,119 @@ +# When migrating from disaggregated storage (constant setVersion) back to a +# traditional replica set (incrementing setVersion), a force reconfig is used +# with a high setVersion value to ensure the driver accepts the new primary. +description: DSC to ASC reverse migration - ASC primary with higher setVersion is accepted +uri: "mongodb://a/?replicaSet=rs" +phases: + # Phase 1: Disaggregated storage cluster with constant setVersion=1 + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + minWireVersion: 0 + maxWireVersion: 17 + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 1 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: {"$oid": "000000000000000000000005"} + # Phase 2: After migration to traditional replica set, B becomes primary with + # setVersion=1000 (force reconfig with high value) and newer electionId=6 + # The driver should accept B as the new primary due to higher setVersion + - responses: + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1000 + electionId: {"$oid": "000000000000000000000006"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + setVersion: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1000 + electionId: {"$oid": "000000000000000000000006"} + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1000 + maxElectionId: {"$oid": "000000000000000000000006"} + # Phase 3: Old disaggregated storage primary A tries to come back + # The driver should reject it because both setVersion (1 < 1000) and + # electionId (5 < 6) are stale + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 1 + electionId: {"$oid": "000000000000000000000005"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + setVersion: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1000 + electionId: {"$oid": "000000000000000000000006"} + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1000 + maxElectionId: {"$oid": "000000000000000000000006"} diff --git a/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.json b/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.json new file mode 100644 index 0000000000..57f39c93b2 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.json @@ -0,0 +1,119 @@ +{ + "description": "ASC to DSC forward migration - DSC uses setVersionASC + 1 to prevent false stale detection", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 10, + "electionId": { + "$oid": "000000000000000000000005" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 10, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 10, + "electionId": { + "$oid": "000000000000000000000005" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 10, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 10, + "maxElectionId": { + "$oid": "000000000000000000000005" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 11, + "electionId": { + "$oid": "000000000000000000000006" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 11, + "electionId": { + "$oid": "000000000000000000000006" + } + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs", + "setVersion": 10, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 11, + "maxElectionId": { + "$oid": "000000000000000000000006" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.yml b/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.yml new file mode 100644 index 0000000000..a798392dc9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/migration_to_disaggregated_storage.yml @@ -0,0 +1,85 @@ +# When migrating from a traditional replica set (incrementing setVersion) to +# disaggregated storage (constant setVersion), the migration uses setVersionASC + 1 +# to prevent the driver from incorrectly marking the new primary as stale. +description: ASC to DSC forward migration - DSC uses setVersionASC + 1 to prevent false stale detection +uri: "mongodb://a/?replicaSet=rs" +phases: + # Phase 1: Traditional replica set with incrementing setVersion=10 + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 10 + electionId: {"$oid": "000000000000000000000005"} + minWireVersion: 0 + maxWireVersion: 17 + - + - "b:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 10 + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 10 + electionId: {"$oid": "000000000000000000000005"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 10 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 10 + maxElectionId: {"$oid": "000000000000000000000005"} + # Phase 2: After migration to disaggregated storage, setVersion becomes 11 (10 + 1) + # This ensures the driver sees a newer setVersion and accepts the new primary, + # even though future reconfigs will keep setVersion constant at 11 + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - "a:27017" + - "b:27017" + setName: rs + setVersion: 11 + electionId: {"$oid": "000000000000000000000006"} + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 11 + electionId: {"$oid": "000000000000000000000006"} + b:27017: + type: RSSecondary + setName: rs + setVersion: 10 + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 11 + maxElectionId: {"$oid": "000000000000000000000006"} diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary.json b/source/server-discovery-and-monitoring/tests/rs/new_primary.json index eb73b304bd..69b07516b9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary.json +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary.json @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,14 +42,15 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -56,7 +58,8 @@ "servers": { "a:27017": { "type": "Unknown", - "setName": null + "setName": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary.yml index b7cfd16014..46480f6b37 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -46,11 +47,12 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -61,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", - setName: + setName:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { @@ -75,4 +78,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json index cd6c37cef7..90ef0ce8dc 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,7 +20,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,7 +42,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -50,7 +55,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -61,7 +67,7 @@ "$oid": "000000000000000000000002" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -70,7 +76,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -83,7 +90,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -92,7 +103,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -103,7 +115,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -112,7 +124,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", @@ -125,7 +138,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml index 93bcba7c31..fac0f8a3a1 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_electionid.yml @@ -9,13 +9,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -36,6 +37,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -44,13 +47,14 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000002"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -59,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId: , + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -71,6 +76,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -79,13 +86,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -93,7 +101,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { type: "RSPrimary", @@ -105,6 +114,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json index c5828171d4..9c1e2d4bdd 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,7 +20,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,7 +42,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -50,7 +55,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -61,7 +67,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -70,7 +76,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -83,7 +90,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -92,7 +103,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -103,7 +115,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -112,7 +124,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { "type": "RSPrimary", @@ -125,7 +138,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml index 9112e7578f..44fe058a30 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_new_setversion.yml @@ -9,13 +9,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -36,6 +37,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -44,13 +47,14 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 2, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -59,7 +63,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -71,6 +76,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -79,13 +86,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -93,7 +101,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" }, "b:27017": { type: "RSPrimary", @@ -105,6 +114,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json b/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json index 7be79d2d3c..774b3a5736 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.json @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,13 +42,14 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml index adca8a2d50..abcfc8f829 100644 --- a/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/new_primary_wrong_set_name.yml @@ -11,11 +11,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -49,11 +50,12 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -72,4 +74,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/non_rs_member.json b/source/server-discovery-and-monitoring/tests/rs/non_rs_member.json index 907c1651e0..6bf10bd628 100644 --- a/source/server-discovery-and-monitoring/tests/rs/non_rs_member.json +++ b/source/server-discovery-and-monitoring/tests/rs/non_rs_member.json @@ -8,8 +8,9 @@ "b:27017", { "ok": 1, + "helloOk": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/non_rs_member.yml b/source/server-discovery-and-monitoring/tests/rs/non_rs_member.yml index 7fc4dddf26..eb294d4307 100644 --- a/source/server-discovery-and-monitoring/tests/rs/non_rs_member.yml +++ b/source/server-discovery-and-monitoring/tests/rs/non_rs_member.yml @@ -9,8 +9,9 @@ phases: [ ["b:27017", { ok: 1, + helloOk: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -29,4 +30,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/normalize_case.json b/source/server-discovery-and-monitoring/tests/rs/normalize_case.json index 4d0b0ae629..62915495e0 100644 --- a/source/server-discovery-and-monitoring/tests/rs/normalize_case.json +++ b/source/server-discovery-and-monitoring/tests/rs/normalize_case.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "A:27017" @@ -20,7 +21,7 @@ "C:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/normalize_case.yml b/source/server-discovery-and-monitoring/tests/rs/normalize_case.yml index f1fdd78830..0f0dcb39b0 100644 --- a/source/server-discovery-and-monitoring/tests/rs/normalize_case.yml +++ b/source/server-discovery-and-monitoring/tests/rs/normalize_case.yml @@ -10,13 +10,14 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["A:27017"], passives: ["B:27017"], arbiters: ["C:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -48,4 +49,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.json b/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.json index e854e7fb43..0d9ba6213e 100644 --- a/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.json +++ b/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "me": "A:27017", "hosts": [ @@ -21,7 +22,7 @@ "C:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -51,7 +52,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "me": "B:27017", @@ -65,7 +67,7 @@ "C:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml b/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml index 51700b96a9..4d35e4067f 100644 --- a/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml +++ b/source/server-discovery-and-monitoring/tests/rs/normalize_case_me.yml @@ -10,14 +10,15 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", me: "A:27017", hosts: ["A:27017"], passives: ["B:27017"], arbiters: ["C:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -56,7 +57,8 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", me: "B:27017", @@ -64,7 +66,7 @@ phases: [ passives: ["B:27017"], arbiters: ["C:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -97,4 +99,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.json new file mode 100644 index 0000000000..8a77f31c50 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.json @@ -0,0 +1,203 @@ +{ + "description": "Pre 6.0 Primaries with and without electionIds", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setVersion": 1, + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "c:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1 + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + }, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + }, + "c:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setVersion": 1, + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "c:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + }, + { + "responses": [ + [ + "c:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017", + "c:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "c:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.yml new file mode 100644 index 0000000000..aef05dbe92 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/null_election_id-pre-6.0.yml @@ -0,0 +1,175 @@ +description: "Pre 6.0 Primaries with and without electionIds" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A has no electionId. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017", "c:27017"], + setVersion: 1, + setName: "rs", + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + }, + "c:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + } + }, + + # B is elected, it has an electionId. + { + responses: [ + ["b:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017", "c:27017"], + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000002"}, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId: + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000002"} + }, + "c:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, + } + }, + + # A still claims to be primary, no electionId, we have to trust it. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017", "c:27017"], + setVersion: 1, + setName: "rs", + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + }, + "c:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, + } + }, + + # But we remember B's electionId, so when we finally hear from C + # claiming it is primary, we ignore it due to its outdated electionId + { + responses: [ + ["c:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017", "c:27017"], + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000001"}, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + outcome: { + servers: { + # Still primary. + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + }, + "c:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/null_election_id.json b/source/server-discovery-and-monitoring/tests/rs/null_election_id.json index d4348df442..8a99a78475 100644 --- a/source/server-discovery-and-monitoring/tests/rs/null_election_id.json +++ b/source/server-discovery-and-monitoring/tests/rs/null_election_id.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017", @@ -17,7 +18,7 @@ "setVersion": 1, "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], @@ -42,7 +43,8 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1 } }, { @@ -51,7 +53,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017", @@ -63,7 +66,7 @@ "$oid": "000000000000000000000002" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], @@ -90,7 +93,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -99,7 +106,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017", @@ -108,23 +116,26 @@ "setVersion": 1, "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], "outcome": { "servers": { "a:27017": { - "type": "RSPrimary", - "setName": "rs", - "setVersion": 1, - "electionId": null - }, - "b:27017": { "type": "Unknown", "setName": null, + "setVersion": null, "electionId": null }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + }, "c:27017": { "type": "Unknown", "setName": null, @@ -133,7 +144,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -142,7 +157,8 @@ "c:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017", @@ -154,23 +170,26 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], "outcome": { "servers": { "a:27017": { - "type": "RSPrimary", - "setName": "rs", - "setVersion": 1, - "electionId": null - }, - "b:27017": { "type": "Unknown", "setName": null, + "setVersion": null, "electionId": null }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + }, "c:27017": { "type": "Unknown", "setName": null, @@ -179,7 +198,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/null_election_id.yml b/source/server-discovery-and-monitoring/tests/rs/null_election_id.yml index 713e74d536..f6b69d1287 100644 --- a/source/server-discovery-and-monitoring/tests/rs/null_election_id.yml +++ b/source/server-discovery-and-monitoring/tests/rs/null_election_id.yml @@ -9,12 +9,13 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017", "c:27017"], setVersion: 1, setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], @@ -40,6 +41,7 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, } }, @@ -48,13 +50,14 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017", "c:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000002"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], @@ -80,35 +83,40 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, - # A still claims to be primary, no electionId, we have to trust it. + # A still claims to be primary, no electionId, we don't trust it. { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017", "c:27017"], setVersion: 1, setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], outcome: { servers: { + # A ignored for missing electionId "a:27017": { - type: "RSPrimary", - setName: "rs", - setVersion: 1, - electionId: - }, - "b:27017": { type: "Unknown", setName: , + setVersion: , electionId: }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: { "$oid": "000000000000000000000002" } + }, "c:27017": { type: "Unknown", setName: , @@ -118,6 +126,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -127,29 +137,30 @@ phases: [ responses: [ ["c:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017", "c:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], outcome: { servers: { - # Still primary. "a:27017": { - type: "RSPrimary", - setName: "rs", - setVersion: 1, - electionId: - }, - "b:27017": { type: "Unknown", setName: , + setVersion: , electionId: }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: { "$oid": "000000000000000000000002" } + }, "c:27017": { type: "Unknown", setName: , @@ -159,6 +170,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json new file mode 100644 index 0000000000..e34280e88c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.json @@ -0,0 +1,61 @@ +{ + "description": "Primary becomes ghost", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "isreplicaset": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSGhost", + "setName": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml new file mode 100644 index 0000000000..8e11776feb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_ghost.yml @@ -0,0 +1,65 @@ +description: "Primary becomes ghost" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + }, + + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: false, + isreplicaset: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSGhost", + setName: + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json new file mode 100644 index 0000000000..79510d9399 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.json @@ -0,0 +1,56 @@ +{ + "description": "Primary becomes mongos", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "msg": "isdbgrid", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": {}, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml new file mode 100644 index 0000000000..d8b1b85b3f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_mongos.yml @@ -0,0 +1,58 @@ +description: "Primary becomes mongos" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + }, + + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + msg: "isdbgrid", + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: {}, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json index e35c75f4bc..abcc1e2d01 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -37,7 +38,7 @@ { "ok": 1, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml index 2f7db8054f..6555e95afa 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_becomes_standalone.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -39,7 +40,7 @@ phases: [ ["a:27017", { ok: 1, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -51,4 +52,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json b/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json index d008326123..3b564d2c93 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -36,13 +37,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml index 67a24ec5ba..e598e58c37 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_changes_set_name.yml @@ -11,11 +11,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -42,11 +43,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -58,4 +60,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.json b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.json index 271ca5874e..73a01a82a9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml index 421e1f0ae4..cd840297b1 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -55,4 +56,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json index e81f299086..b030bd2c53 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,14 +20,15 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -37,7 +39,7 @@ "$oid": "000000000000000000000002" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -46,7 +48,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -59,7 +62,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -84,7 +91,11 @@ }, "topologyType": "ReplicaSetNoPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -93,7 +104,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -104,7 +116,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -113,6 +125,7 @@ "a:27017": { "type": "Unknown", "setName": null, + "error": "primary marked stale due to electionId/setVersion mismatch", "electionId": null }, "b:27017": { @@ -123,7 +136,11 @@ }, "topologyType": "ReplicaSetNoPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -132,7 +149,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -143,7 +161,7 @@ "$oid": "000000000000000000000003" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -165,7 +183,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000003" + } } }, { @@ -174,7 +196,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -182,7 +205,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -203,7 +226,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000003" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml index fac9ba10de..0717f04275 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_electionid.yml @@ -9,23 +9,25 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000002"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -34,7 +36,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -46,6 +49,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -70,6 +75,8 @@ phases: [ topologyType: "ReplicaSetNoPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -78,13 +85,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -92,6 +100,7 @@ phases: [ "a:27017": { type: "Unknown", setName: , + error: "primary marked stale due to electionId/setVersion mismatch", electionId: }, "b:27017": { @@ -103,6 +112,8 @@ phases: [ topologyType: "ReplicaSetNoPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -111,13 +122,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000003"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -137,6 +149,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000003"}, } }, @@ -145,12 +159,13 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -169,6 +184,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000003"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json index d0e55c545a..653a5f29e8 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,14 +20,15 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -37,7 +39,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -46,7 +48,8 @@ "a:27017": { "type": "Unknown", "setName": null, - "electionId": null + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" }, "b:27017": { "type": "RSPrimary", @@ -59,7 +62,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -84,7 +91,11 @@ }, "topologyType": "ReplicaSetNoPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -93,7 +104,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -104,7 +116,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -113,6 +125,7 @@ "a:27017": { "type": "Unknown", "setName": null, + "error": "primary marked stale due to electionId/setVersion mismatch", "electionId": null }, "b:27017": { @@ -123,7 +136,11 @@ }, "topologyType": "ReplicaSetNoPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -132,7 +149,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -143,7 +161,7 @@ "$oid": "000000000000000000000002" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -165,7 +183,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } }, { @@ -174,7 +196,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -182,7 +205,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -203,7 +226,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml index a1e7801a4a..0e2cc328fd 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_disconnect_setversion.yml @@ -9,23 +9,25 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 2, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -34,7 +36,8 @@ phases: [ "a:27017": { type: "Unknown", setName: , - electionId: + electionId:, + error: "primary marked stale due to discovery of newer primary" }, "b:27017": { type: "RSPrimary", @@ -46,6 +49,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -70,6 +75,8 @@ phases: [ topologyType: "ReplicaSetNoPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -78,13 +85,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -92,6 +100,7 @@ phases: [ "a:27017": { type: "Unknown", setName: , + error: "primary marked stale due to electionId/setVersion mismatch", electionId: }, "b:27017": { @@ -103,6 +112,8 @@ phases: [ topologyType: "ReplicaSetNoPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, @@ -111,13 +122,14 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 2, electionId: {"$oid": "000000000000000000000002"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -137,6 +149,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000002"}, } }, @@ -145,12 +159,13 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -169,6 +184,8 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000002"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json b/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json index 806fda37c3..1ca72225a2 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "me": "c:27017", "hosts": [ @@ -17,7 +18,7 @@ "setName": "rs", "primary": "b:27017", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -39,14 +40,15 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "me": "b:27017", "hosts": [ "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml b/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml index 9ffa677327..01ebde9daa 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_hint_from_secondary_with_mismatched_me.yml @@ -9,14 +9,15 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, me: "c:27017", hosts: ["b:27017"], setName: "rs", primary: "b:27017", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -38,12 +39,13 @@ phases: [ responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, me: "b:27017", hosts: ["b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -59,4 +61,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json index 8d18a6971f..6bb6226f8a 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.json @@ -26,11 +26,12 @@ "a:27017", "b:27017" ], - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "ok": 1, "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml index 7a4b89fae5..25ca1722e0 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me.yml @@ -17,10 +17,11 @@ phases: hosts: - 'a:27017' - 'b:27017' - ismaster: true + helloOk: true + isWritablePrimary: true ok: 1 setName: rs minWireVersion: 0 - maxWireVersion: 6 + maxWireVersion: 21 uri: 'mongodb://localhost:27017/?replicaSet=rs' diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json new file mode 100644 index 0000000000..a55dcfc6d4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.json @@ -0,0 +1,79 @@ +{ + "description": "Primary mismatched me is not removed", + "uri": "mongodb://localhost:27017,localhost:27018/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "localhost:27017", + { + "ok": 1, + "hosts": [ + "localhost:27017", + "localhost:27018" + ], + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "primary": "localhost:27017", + "me": "a:27017", + "minWireVersion": 0, + "maxWireVersion": 25 + } + ] + ], + "outcome": { + "servers": { + "localhost:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "localhost:27018": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "localhost:27018", + { + "ok": 1, + "hosts": [ + "localhost:27017", + "localhost:27018" + ], + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "setName": "rs", + "primary": "localhost:27017", + "me": "localhost:27018", + "minWireVersion": 0, + "maxWireVersion": 25 + } + ] + ], + "outcome": { + "servers": { + "localhost:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "localhost:27018": { + "type": "RSSecondary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml new file mode 100644 index 0000000000..98384869b4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/primary_mismatched_me_not_removed.yml @@ -0,0 +1,75 @@ +description: Primary mismatched me is not removed +uri: mongodb://localhost:27017,localhost:27018/?replicaSet=rs + +phases: [ + { + responses: [ + ["localhost:27017", { + ok: 1, + hosts: [ + "localhost:27017", + "localhost:27018" + ], + helloOk: true, + isWritablePrimary: true, + setName: "rs", + primary: "localhost:27017", + # me does not match the primary responder's address, but the server + # is still added because we don't me mismatch check the primary and all + # servers from a primary isWritablePrimary are added to the working server set + me: "a:27017", + minWireVersion: 0, + maxWireVersion: 25 + }] + ], + outcome: { + servers: { + "localhost:27017": { + type: "RSPrimary", + setName: "rs" + }, + "localhost:27018": { + type: "Unknown", + setName: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + }, + { + responses: [ + ["localhost:27018", { + ok: 1, + hosts: [ + "localhost:27017", + "localhost:27018" + ], + helloOk: true, + isWritablePrimary: false, + secondary: true, + setName: "rs", + primary: "localhost:27017", + me: "localhost:27018", + minWireVersion: 0, + maxWireVersion: 25 + }] + ], + outcome: { + servers: { + "localhost:27017": { + type: "RSPrimary", + setName: "rs" + }, + "localhost:27018": { + type: "RSSecondary", + setName: "rs" + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json b/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json index 6ed55ab3d1..ed28c48c87 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -16,7 +17,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -42,14 +43,15 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -75,7 +77,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", @@ -83,7 +86,7 @@ "c:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -113,7 +116,8 @@ "c:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "primary": "b:27017", @@ -123,7 +127,7 @@ "c:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml b/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml index 719c444782..09b2916b48 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_reports_new_member.yml @@ -11,12 +11,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -49,11 +50,12 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -86,11 +88,12 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017", "c:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -130,13 +133,14 @@ phases: [ ["c:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", primary: "b:27017", hosts: ["a:27017", "b:27017", "c:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -168,4 +172,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json b/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json index fdb250ffef..798a648d19 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -16,7 +17,7 @@ "me": "a:27017", "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -42,7 +43,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "c:27017", "d:27017" @@ -50,7 +52,7 @@ "me": "c:27017", "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml b/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml index 0b2d82a05f..a94da152b4 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_to_no_primary_mismatched_me.yml @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], me: "a:27017", setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -46,12 +47,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["c:27017", "d:27017"], me : "c:27017", setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -76,4 +78,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json b/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json index eda4787173..1366e38996 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml index 4a06ce63c2..db50ee831b 100644 --- a/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/primary_wrong_set_name.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -26,4 +27,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/repeated.json b/source/server-discovery-and-monitoring/tests/rs/repeated.json new file mode 100644 index 0000000000..3ce0948ab8 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/repeated.json @@ -0,0 +1,144 @@ +{ + "description": "Repeated isWritablePrimary response must be processed", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hidden": true, + "hosts": [ + "a:27017", + "c:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown" + }, + "c:27017": { + "type": "Unknown" + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "c:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown" + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "hidden": true, + "hosts": [ + "a:27017", + "c:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown" + }, + "c:27017": { + "type": "Unknown" + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "c:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "c:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSOther", + "setName": "rs" + }, + "c:27017": { + "type": "RSPrimary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/repeated.yml b/source/server-discovery-and-monitoring/tests/rs/repeated.yml new file mode 100644 index 0000000000..b9e14ed982 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/repeated.yml @@ -0,0 +1,105 @@ +description: Repeated isWritablePrimary response must be processed + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: + # Phase 1 - a says it's not primary and suggests c may be the primary + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hidden: true + hosts: ["a:27017", "c:27017"] + setName: "rs" + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + "a:27017": + type: "RSOther" + setName: "rs" + + "b:27017": + type: Unknown + + "c:27017": + type: Unknown + topologyType: "ReplicaSetNoPrimary" + logicalSessionTimeoutMinutes: ~ + setName: "rs" + + # Phase 2 - c says it's a standalone, is removed + - responses: + - + - "c:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + "a:27017": + type: "RSOther" + setName: "rs" + + "b:27017": + type: Unknown + topologyType: "ReplicaSetNoPrimary" + logicalSessionTimeoutMinutes: ~ + setName: "rs" + + # Phase 3 - response from a is repeated, and must be processed; c added again + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + hidden: true + hosts: ["a:27017", "c:27017"] + setName: "rs" + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + "a:27017": + type: "RSOther" + setName: "rs" + + "b:27017": + type: Unknown + + "c:27017": + type: Unknown + topologyType: "ReplicaSetNoPrimary" + logicalSessionTimeoutMinutes: ~ + setName: "rs" + + # Phase 4 - c is now a primary + - responses: + - + - "c:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: ["a:27017", "c:27017"] + setName: "rs" + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + "a:27017": + type: "RSOther" + setName: "rs" + + "c:27017": + type: RSPrimary + setName: rs + topologyType: "ReplicaSetWithPrimary" + logicalSessionTimeoutMinutes: ~ + setName: "rs" diff --git a/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json b/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json new file mode 100644 index 0000000000..1cd732b82f --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.json @@ -0,0 +1,26 @@ +{ + "description": "replicaSet URI option causes starting topology to be RSNP", + "uri": "mongodb://a/?replicaSet=rs&directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": {}, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml b/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml new file mode 100644 index 0000000000..6bdf8cbe2d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/replicaset_rsnp.yml @@ -0,0 +1,21 @@ +description: replicaSet URI option causes starting topology to be RSNP + +uri: "mongodb://a/?replicaSet=rs&directConnection=false" + +phases: + # We are connecting to a standalone + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + minWireVersion: 0 + maxWireVersion: 21 + outcome: + # Server is removed because it's a standalone and the driver + # started in RSNP topology + servers: {} + topologyType: "ReplicaSetNoPrimary" + logicalSessionTimeoutMinutes: ~ + setName: "rs" diff --git a/source/server-discovery-and-monitoring/tests/rs/response_from_removed.json b/source/server-discovery-and-monitoring/tests/rs/response_from_removed.json index dd3562d7fc..fa46a14ceb 100644 --- a/source/server-discovery-and-monitoring/tests/rs/response_from_removed.json +++ b/source/server-discovery-and-monitoring/tests/rs/response_from_removed.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -36,7 +37,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -44,7 +46,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/response_from_removed.yml b/source/server-discovery-and-monitoring/tests/rs/response_from_removed.yml index 061a839379..836a73fbaf 100644 --- a/source/server-discovery-and-monitoring/tests/rs/response_from_removed.yml +++ b/source/server-discovery-and-monitoring/tests/rs/response_from_removed.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -40,12 +41,13 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -64,4 +66,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.json b/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.json deleted file mode 100644 index c575501d80..0000000000 --- a/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "description": "RSOther discovered", - "uri": "mongodb://a,b/?replicaSet=rs", - "phases": [ - { - "responses": [ - [ - "a:27017", - { - "ok": 1, - "ismaster": false, - "secondary": true, - "hidden": true, - "hosts": [ - "c:27017", - "d:27017" - ], - "setName": "rs", - "minWireVersion": 0, - "maxWireVersion": 6 - } - ], - [ - "b:27017", - { - "ok": 1, - "ismaster": false, - "secondary": false, - "hosts": [ - "c:27017", - "d:27017" - ], - "setName": "rs", - "minWireVersion": 0, - "maxWireVersion": 6 - } - ] - ], - "outcome": { - "servers": { - "a:27017": { - "type": "RSOther", - "setName": "rs" - }, - "b:27017": { - "type": "RSOther", - "setName": "rs" - }, - "c:27017": { - "type": "Unknown", - "setName": null - }, - "d:27017": { - "type": "Unknown", - "setName": null - } - }, - "topologyType": "ReplicaSetNoPrimary", - "logicalSessionTimeoutMinutes": null, - "setName": "rs" - } - } - ] -} diff --git a/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.yml b/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.yml deleted file mode 100644 index b84cd00986..0000000000 --- a/source/server-discovery-and-monitoring/tests/rs/rsother_discovered.yml +++ /dev/null @@ -1,66 +0,0 @@ -description: "RSOther discovered" - -uri: "mongodb://a,b/?replicaSet=rs" - -phases: [ - - { - responses: [ - - ["a:27017", { - - ok: 1, - ismaster: false, - secondary: true, - hidden: true, - hosts: ["c:27017", "d:27017"], - setName: "rs", - minWireVersion: 0, - maxWireVersion: 6 - }], - ["b:27017", { - - ok: 1, - ismaster: false, - secondary: false, - hosts: ["c:27017", "d:27017"], - setName: "rs", - minWireVersion: 0, - maxWireVersion: 6 - }] - ], - - outcome: { - - servers: { - - "a:27017": { - - type: "RSOther", - setName: "rs" - }, - - "b:27017": { - - type: "RSOther", - setName: "rs" - }, - - "c:27017": { - - type: "Unknown", - setName: - }, - - "d:27017": { - - type: "Unknown", - setName: - } - }, - topologyType: "ReplicaSetNoPrimary", - logicalSessionTimeoutMinutes: null, - setName: "rs" - } - } -] diff --git a/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.json b/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.json index 7d5e700035..ccbe7a08af 100644 --- a/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.json +++ b/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.json @@ -8,21 +8,23 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -30,7 +32,7 @@ "c:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml b/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml index 5caca0b9de..f214e5cd17 100644 --- a/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml +++ b/source/server-discovery-and-monitoring/tests/rs/sec_not_auth.yml @@ -10,22 +10,24 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["b:27017", "c:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -50,4 +52,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.json new file mode 100644 index 0000000000..f27060533c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.json @@ -0,0 +1,83 @@ +{ + "description": "Pre 6.0 New primary", + "uri": "mongodb://a,b/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ], + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "setName": "rs", + "hosts": [ + "a:27017", + "b:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "RSSecondary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 0, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + }, + "b:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.yml new file mode 100644 index 0000000000..bba5a3b09a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0-pre-6.0.yml @@ -0,0 +1,87 @@ +description: "Pre 6.0 New primary" + +uri: "mongodb://a,b/?replicaSet=rs" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + setName: "rs", + hosts: ["a:27017", "b:27017"], + minWireVersion: 0, + maxWireVersion: 21 + }], + ["b:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: false, + secondary: true, + setName: "rs", + hosts: ["a:27017", "b:27017"], + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "RSSecondary", + setName: "rs" + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + }, + + { + responses: [ + + ["b:27017", { + + ok: 0, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + "a:27017": { + + type: "RSPrimary", + setName: "rs" + }, + + "b:27017": { + + type: "Unknown", + setName: + } + + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs" + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json index 6d3033eeee..9ffff58ef0 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.json @@ -1,5 +1,5 @@ { - "description": "New primary", + "description": "Secondary ignored when ok is zero", "uri": "mongodb://a,b/?replicaSet=rs", "phases": [ { @@ -8,21 +8,23 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ @@ -30,7 +32,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -57,7 +59,7 @@ { "ok": 0, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml index a77166927f..70997cec04 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ignore_ok_0.yml @@ -1,4 +1,4 @@ -description: "New primary" +description: "Secondary ignored when ok is zero" uri: "mongodb://a,b/?replicaSet=rs" @@ -10,21 +10,23 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -57,7 +59,7 @@ phases: [ ok: 0, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -82,4 +84,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.json b/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.json new file mode 100644 index 0000000000..c23d8dc4c9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.json @@ -0,0 +1,38 @@ +{ + "description": "Secondary with IPv6 literal", + "uri": "mongodb://[::1]/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "[::1]:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": false, + "secondary": true, + "setName": "rs", + "me": "[::1]:27017", + "hosts": [ + "[::1]:27017" + ], + "minWireVersion": 0, + "maxWireVersion": 26 + } + ] + ], + "outcome": { + "servers": { + "[::1]:27017": { + "type": "RSSecondary", + "setName": "rs" + } + }, + "topologyType": "ReplicaSetNoPrimary", + "setName": "rs", + "logicalSessionTimeoutMinutes": null, + "compatible": true + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.yml new file mode 100644 index 0000000000..bac3496968 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_ipv6_literal.yml @@ -0,0 +1,25 @@ +# Regression test for bug discovered in HELP-68823. +description: Secondary with IPv6 literal +uri: mongodb://[::1]/?replicaSet=rs +phases: +- responses: + - - "[::1]:27017" + - ok: 1 + helloOk: true + isWritablePrimary: false + secondary: true + setName: rs + me: "[::1]:27017" + hosts: + - "[::1]:27017" + minWireVersion: 0 + maxWireVersion: 26 + outcome: + servers: + "[::1]:27017": + type: RSSecondary + setName: rs + topologyType: ReplicaSetNoPrimary + setName: rs + logicalSessionTimeoutMinutes: null + compatible: true diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json b/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json index d2a70f6788..790e4bfca8 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.json @@ -1,5 +1,6 @@ { "description": "Secondary mismatched me", + "uri": "mongodb://localhost:27017/?replicaSet=rs", "phases": [ { "outcome": { @@ -26,15 +27,15 @@ "a:27017", "b:27017" ], - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "ok": 1, "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ] } - ], - "uri": "mongodb://localhost:27017/?replicaSet=rs" + ] } diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml index 7a093c8dcc..d359609d83 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_mismatched_me.yml @@ -1,4 +1,7 @@ description: Secondary mismatched me + +uri: 'mongodb://localhost:27017/?replicaSet=rs' + phases: - outcome: servers: @@ -17,10 +20,9 @@ phases: hosts: - 'a:27017' - 'b:27017' - ismaster: false + helloOk: true + isWritablePrimary: false ok: 1 setName: rs minWireVersion: 0 - maxWireVersion: 6 -uri: 'mongodb://localhost:27017/?replicaSet=rs' - + maxWireVersion: 21 diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json index 4c132b633e..1f86b50543 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.json @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017" ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml index 10ae3674f0..18c24cdd17 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name.yml @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -27,4 +28,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json index 73cbab7c5d..6b89914151 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.json @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -41,7 +42,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -49,7 +51,7 @@ ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml index 667d93e38a..073e18dda9 100644 --- a/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml +++ b/source/server-discovery-and-monitoring/tests/rs/secondary_wrong_set_name_with_primary.yml @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -46,12 +47,13 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -70,4 +72,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.json b/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.json new file mode 100644 index 0000000000..1cc608a344 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.json @@ -0,0 +1,147 @@ +{ + "description": "Set version rolls back after new primary with higher election Id", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.yml b/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.yml new file mode 100644 index 0000000000..c30a8660ec --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/set_version_can_rollback.yml @@ -0,0 +1,101 @@ +description: Set version rolls back after new primary with higher election Id +uri: mongodb://a/?replicaSet=rs +phases: + - responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + - b:27017 + setName: rs + setVersion: 2 + electionId: + $oid: '000000000000000000000001' + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + setVersion: 2 + electionId: + $oid: '000000000000000000000001' + b:27017: + type: Unknown + setName: null + electionId: null + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 2 + maxElectionId: + $oid: '000000000000000000000001' + - # Response from new primary with newer election Id + responses: + - - b:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + - b:27017 + setName: rs + setVersion: 1 + electionId: + $oid: '000000000000000000000002' + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: + $oid: '000000000000000000000002' + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: + $oid: '000000000000000000000002' + - # Response from stale primary + responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + - b:27017 + setName: rs + setVersion: 2 + electionId: + $oid: '000000000000000000000001' + minWireVersion: 0 + maxWireVersion: 17 + outcome: + servers: + a:27017: + type: Unknown + setName: null + electionId: null + b:27017: + type: RSPrimary + setName: rs + setVersion: 1 + electionId: + $oid: '000000000000000000000002' + topologyType: ReplicaSetWithPrimary + logicalSessionTimeoutMinutes: null + setName: rs + maxSetVersion: 1 + maxElectionId: + $oid: '000000000000000000000002' diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.json new file mode 100644 index 0000000000..3669511c5a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.json @@ -0,0 +1,84 @@ +{ + "description": "setVersion version that is equal is treated the same as greater than if there is no electionId", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1 + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1 + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.yml new file mode 100644 index 0000000000..7eb2429fc4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_equal_max_without_electionid.yml @@ -0,0 +1,78 @@ +description: "setVersion version that is equal is treated the same as greater than if there is no electionId" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered and tells us about B. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + minWireVersion: 0, + maxWireVersion: 17 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + } + }, + + # B is elected, its setVersion is older so it is stale + { + responses: [ + ["b:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + minWireVersion: 0, + maxWireVersion: 17 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId: + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, # Max is still 1, there wasn't an actual larger setVersion seen + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json new file mode 100644 index 0000000000..06c89609f5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.json @@ -0,0 +1,85 @@ +{ + "description": "setVersion that is greater than maxSetVersion is used if there is no electionId", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1 + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "minWireVersion": 0, + "maxWireVersion": 17 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2 + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml new file mode 100644 index 0000000000..5b7439df6d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_greaterthan_max_without_electionid.yml @@ -0,0 +1,80 @@ +description: "setVersion that is greater than maxSetVersion is used if there is no electionId" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered and tells us about B. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + minWireVersion: 0, + maxWireVersion: 17 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + } + }, + + # B is elected, its setVersion is greater than our current maxSetVersion + # B is primary, A is marked Unknown + { + responses: [ + ["b:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 2, + minWireVersion: 0, + maxWireVersion: 17 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 2, + electionId: + }, + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 2, + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json new file mode 100644 index 0000000000..9a1ee61399 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.json @@ -0,0 +1,85 @@ +{ + "description": "Pre 6.0 setVersion is ignored if there is no electionId", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2, + "electionId": null + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2 + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2 + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml new file mode 100644 index 0000000000..f0e5e5b951 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid-pre-6.0.yml @@ -0,0 +1,80 @@ +description: "Pre 6.0 setVersion is ignored if there is no electionId" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered and tells us about B. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 2, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 2 , + electionId: + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 2, + } + }, + + # B is elected, its setVersion is older but we believe it anyway, because + # setVersion is only used in conjunction with electionId. + { + responses: [ + ["b:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 2, + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json index dbd9765d2f..256fafe108 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.json @@ -1,5 +1,5 @@ { - "description": "setVersion is ignored if there is no electionId", + "description": "setVersion that is less than maxSetVersion is ignored if there is no electionId", "uri": "mongodb://a/?replicaSet=rs", "phases": [ { @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -16,7 +17,7 @@ "setName": "rs", "setVersion": 2, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], @@ -36,7 +37,8 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2 } }, { @@ -45,7 +47,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -53,27 +56,28 @@ "setName": "rs", "setVersion": 1, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], "outcome": { "servers": { "a:27017": { - "type": "Unknown", - "setName": null, + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2, "electionId": null }, "b:27017": { - "type": "RSPrimary", - "setName": "rs", - "setVersion": 1, + "type": "Unknown", + "setName": null, "electionId": null } }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 2 } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml index 326fd04c62..ac71c2e9ec 100644 --- a/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/setversion_without_electionid.yml @@ -1,4 +1,4 @@ -description: "setVersion is ignored if there is no electionId" +description: "setVersion that is less than maxSetVersion is ignored if there is no electionId" uri: "mongodb://a/?replicaSet=rs" @@ -9,12 +9,13 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 2, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], @@ -35,41 +36,43 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, } }, - # B is elected, its setVersion is older but we believe it anyway, because - # setVersion is only used in conjunction with electionId. + # B is elected, its setVersion is older so it is stale { responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], outcome: { servers: { "a:27017": { - type: "Unknown", - setName: , + type: "RSPrimary", + setName: "rs", + setVersion: 2 , electionId: }, "b:27017": { - type: "RSPrimary", - setName: "rs", - setVersion: 1, + type: "Unknown", + setName: , electionId: } }, topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 2, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json b/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json index 39a4f532dd..6de995518d 100644 --- a/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.json @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -36,14 +37,15 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017" ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml index 0d38f33e94..18c7696ff7 100644 --- a/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/stepdown_change_set_name.yml @@ -11,11 +11,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -43,12 +44,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -60,4 +62,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/too_new.json b/source/server-discovery-and-monitoring/tests/rs/too_new.json index 945145af88..696246f8e1 100644 --- a/source/server-discovery-and-monitoring/tests/rs/too_new.json +++ b/source/server-discovery-and-monitoring/tests/rs/too_new.json @@ -8,21 +8,23 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ diff --git a/source/server-discovery-and-monitoring/tests/rs/too_new.yml b/source/server-discovery-and-monitoring/tests/rs/too_new.yml index a47527af4f..520d395fca 100644 --- a/source/server-discovery-and-monitoring/tests/rs/too_new.yml +++ b/source/server-discovery-and-monitoring/tests/rs/too_new.yml @@ -1,19 +1,23 @@ description: "Replica set member with large minWireVersion" + uri: "mongodb://a,b/?replicaSet=rs" + phases: [ { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", hosts: ["a:27017", "b:27017"], @@ -38,4 +42,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/too_old.json b/source/server-discovery-and-monitoring/tests/rs/too_old.json index 3f9eadc4bc..dc8a5b2b9c 100644 --- a/source/server-discovery-and-monitoring/tests/rs/too_old.json +++ b/source/server-discovery-and-monitoring/tests/rs/too_old.json @@ -8,27 +8,31 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "setName": "rs", "hosts": [ "a:27017", "b:27017" - ] + ], + "minWireVersion": 999, + "maxWireVersion": 1000 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/too_old.yml b/source/server-discovery-and-monitoring/tests/rs/too_old.yml index faa29ba596..410e886694 100644 --- a/source/server-discovery-and-monitoring/tests/rs/too_old.yml +++ b/source/server-discovery-and-monitoring/tests/rs/too_old.yml @@ -5,18 +5,22 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, setName: "rs", hosts: ["a:27017", "b:27017"], minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, setName: "rs", - hosts: ["a:27017", "b:27017"] + hosts: ["a:27017", "b:27017"], + minWireVersion: 999, + maxWireVersion: 1000 }] ], outcome: { @@ -36,4 +40,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.json b/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.json new file mode 100644 index 0000000000..d3baa13479 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.json @@ -0,0 +1,101 @@ +{ + "description": "Primary with equal topologyVersion", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + }, + "b:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml b/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml new file mode 100644 index 0000000000..8557606fbe --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_equal.yml @@ -0,0 +1,68 @@ +description: "Primary with equal topologyVersion" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with an equal topologyVersion, we should process the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + }, + "b:27017": { + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.json b/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.json new file mode 100644 index 0000000000..f296ccee62 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.json @@ -0,0 +1,259 @@ +{ + "description": "Primary with newer topologyVersion", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "2" + } + } + }, + "b:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "c:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "0" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000002" + }, + "counter": { + "$numberLong": "0" + } + } + }, + "c:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "d:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": null + }, + "d:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "e:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000003" + }, + "counter": { + "$numberLong": "0" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000003" + }, + "counter": { + "$numberLong": "0" + } + } + }, + "e:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + {} + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "topologyVersion": null + }, + "e:27017": { + "type": "Unknown", + "topologyVersion": null + } + }, + "topologyType": "ReplicaSetNoPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml b/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml new file mode 100644 index 0000000000..ab5b142013 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_greater.yml @@ -0,0 +1,194 @@ +description: "Primary with newer topologyVersion" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with a greater topologyVersion counter, we should process the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "2"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "2"}} + }, + "b:27017": { + + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with a different topologyVersion processId, we should process the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "c:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000002"}, "counter": {"$numberLong": "0"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000002"}, "counter": {"$numberLong": "0"}} + }, + "c:27017": { + + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds without a topologyVersion, we should process the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "d:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: null + }, + "d:27017": { + + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with a topologyVersion again, we should process the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "e:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000003"}, "counter": {"$numberLong": "0"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000003"}, "counter": {"$numberLong": "0"}} + }, + "e:27017": { + + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with a network error, we should process the response. + { + responses: [ + ["a:27017", {}] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + topologyVersion: null + }, + "e:27017": { + + type: "Unknown", + topologyVersion: null + } + }, + topologyType: "ReplicaSetNoPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_less.json b/source/server-discovery-and-monitoring/tests/rs/topology_version_less.json new file mode 100644 index 0000000000..435337ff25 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_less.json @@ -0,0 +1,97 @@ +{ + "description": "Primary with older topologyVersion", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 9, + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "0" + } + } + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "topologyVersion": { + "processId": { + "$oid": "000000000000000000000001" + }, + "counter": { + "$numberLong": "1" + } + } + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/topology_version_less.yml b/source/server-discovery-and-monitoring/tests/rs/topology_version_less.yml new file mode 100644 index 0000000000..fe3fceecef --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/topology_version_less.yml @@ -0,0 +1,64 @@ +description: "Primary with older topologyVersion" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A is discovered + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + }, + + # A responds with an older topologyVersion, we should ignore the response. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + minWireVersion: 0, + maxWireVersion: 9, + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "0"}} + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + topologyVersion: {'processId': {"$oid": "000000000000000000000001"}, "counter": {"$numberLong": "1"}} + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json b/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json index 95c7aa9dce..c6ffb321ca 100644 --- a/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json +++ b/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.json @@ -8,10 +8,11 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml b/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml index 37d2f09223..dda13ab983 100644 --- a/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml +++ b/source/server-discovery-and-monitoring/tests/rs/unexpected_mongos.yml @@ -10,10 +10,11 @@ phases: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -25,4 +26,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json new file mode 100644 index 0000000000..03195aacde --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.json @@ -0,0 +1,140 @@ +{ + "description": "Pre 6.0 Record max setVersion, even from primary without electionId", + "uri": "mongodb://a/?replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + }, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "b:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 2, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2 + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + }, + "minWireVersion": 0, + "maxWireVersion": 16 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" + }, + "b:27017": { + "type": "RSPrimary", + "setName": "rs", + "setVersion": 2 + } + }, + "topologyType": "ReplicaSetWithPrimary", + "logicalSessionTimeoutMinutes": null, + "setName": "rs", + "maxSetVersion": 2, + "maxElectionId": { + "$oid": "000000000000000000000001" + } + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml new file mode 100644 index 0000000000..182957e78b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid-pre-6.0.yml @@ -0,0 +1,119 @@ +description: "Pre 6.0 Record max setVersion, even from primary without electionId" + +uri: "mongodb://a/?replicaSet=rs" + +phases: [ + + # Primary A has setVersion and electionId, tells us about B. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000001"}, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000001"} + }, + "b:27017": { + type: "Unknown", + setName: , + electionId: + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, + } + }, + + # Reconfig the set and elect B, it has a new setVersion but no electionId. + { + responses: [ + ["b:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 2, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to discovery of newer primary" + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 2 + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, + } + }, + + # Delayed response from A, reporting its reelection. Its setVersion shows + # the election preceded B's so we ignore it. + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + hosts: ["a:27017", "b:27017"], + setName: "rs", + setVersion: 1, + electionId: {"$oid": "000000000000000000000002"}, + minWireVersion: 0, + maxWireVersion: 16 + }] + ], + outcome: { + servers: { + "a:27017": { + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" + }, + "b:27017": { + type: "RSPrimary", + setName: "rs", + setVersion: 2 + } + }, + topologyType: "ReplicaSetWithPrimary", + logicalSessionTimeoutMinutes: null, + setName: "rs", + maxSetVersion: 2, + maxElectionId: {"$oid": "000000000000000000000001"}, + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json index 19e1727bf3..eaf586d728 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -19,7 +20,7 @@ "$oid": "000000000000000000000001" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], @@ -41,7 +42,11 @@ }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -50,7 +55,8 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -58,26 +64,34 @@ "setName": "rs", "setVersion": 2, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], "outcome": { "servers": { "a:27017": { - "type": "Unknown", - "setName": null, - "electionId": null - }, - "b:27017": { "type": "RSPrimary", "setName": "rs", - "setVersion": 2 + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000001" + } + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" } }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000001" + } } }, { @@ -86,7 +100,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" @@ -97,26 +112,34 @@ "$oid": "000000000000000000000002" }, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 17 } ] ], "outcome": { "servers": { "a:27017": { - "type": "Unknown", - "setName": null, - "electionId": null - }, - "b:27017": { "type": "RSPrimary", "setName": "rs", - "setVersion": 2 + "setVersion": 1, + "electionId": { + "$oid": "000000000000000000000002" + } + }, + "b:27017": { + "type": "Unknown", + "setName": null, + "electionId": null, + "error": "primary marked stale due to electionId/setVersion mismatch" } }, "topologyType": "ReplicaSetWithPrimary", "logicalSessionTimeoutMinutes": null, - "setName": "rs" + "setName": "rs", + "maxSetVersion": 1, + "maxElectionId": { + "$oid": "000000000000000000000002" + } } } ] diff --git a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml index c4d2e7e6e9..d41a263cb5 100644 --- a/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml +++ b/source/server-discovery-and-monitoring/tests/rs/use_setversion_without_electionid.yml @@ -4,18 +4,19 @@ uri: "mongodb://a/?replicaSet=rs" phases: [ - # Primary A has setVersion and electionId, tells us about B. + # Primary A has electionId and setVersion, tells us about B. { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000001"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], @@ -36,73 +37,85 @@ phases: [ topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, - # Reconfig the set and elect B, it has a new setVersion but no electionId. + # Reconfig, B reports as primary, B is missing the electionId but reports setVersion { responses: [ ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 2, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], outcome: { servers: { - "a:27017": { - type: "Unknown", - setName: , - electionId: - }, - "b:27017": { + "a:27017": { type: "RSPrimary", setName: "rs", - setVersion: 2 + setVersion: 1, + electionId: { "$oid": "000000000000000000000001" } + }, + "b:27017": { + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" } }, topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000001"}, } }, - # Delayed response from A, reporting its reelection. Its setVersion shows - # the election preceded B's so we ignore it. + # A reports as primary, A has been reelection (electionId greater than our recorded maxElectionId). + # A's setVersion is less than our maxSetVersion, but electionId takes precedence so B's primary claim is ignored { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", setVersion: 1, electionId: {"$oid": "000000000000000000000002"}, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 17 }] ], outcome: { servers: { "a:27017": { - type: "Unknown", - setName: , - electionId: - }, - "b:27017": { type: "RSPrimary", setName: "rs", - setVersion: 2 + setVersion: 1, + electionId: {"$oid": "000000000000000000000002"} + }, + "b:27017":{ + type: "Unknown", + setName: , + electionId:, + error: "primary marked stale due to electionId/setVersion mismatch" } }, topologyType: "ReplicaSetWithPrimary", logicalSessionTimeoutMinutes: null, setName: "rs", + maxSetVersion: 1, + maxElectionId: {"$oid": "000000000000000000000002"}, } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.json b/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.json index 45be2f502b..d0764d24dc 100644 --- a/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.json +++ b/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.json @@ -8,7 +8,8 @@ "b:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "b:27017", @@ -16,7 +17,7 @@ ], "setName": "wrong", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml b/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml index 93004e8783..c9adb113d8 100644 --- a/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml +++ b/source/server-discovery-and-monitoring/tests/rs/wrong_set_name.yml @@ -10,12 +10,13 @@ phases: [ ["b:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["b:27017", "c:27017"], setName: "wrong", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -34,4 +35,4 @@ phases: [ setName: "rs" } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/compatible.json b/source/server-discovery-and-monitoring/tests/sharded/compatible.json index 3dae1f7ea1..ceb0ec24c4 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/compatible.json +++ b/source/server-discovery-and-monitoring/tests/sharded/compatible.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, "maxWireVersion": 1000 @@ -18,10 +19,11 @@ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/compatible.yml b/source/server-discovery-and-monitoring/tests/sharded/compatible.yml index 4e05a0e0ae..29c2f184ae 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/compatible.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/compatible.yml @@ -5,17 +5,19 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, maxWireVersion: 1000 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -35,4 +37,4 @@ phases: [ compatible: true } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json b/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json new file mode 100644 index 0000000000..bf7e57521c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.json @@ -0,0 +1,31 @@ +{ + "description": "Discover single mongos", + "uri": "mongodb://a/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "msg": "isdbgrid", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Mongos", + "setName": null + } + }, + "topologyType": "Sharded", + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml b/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml new file mode 100644 index 0000000000..1e8adc6c5c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/sharded/discover_single_mongos.yml @@ -0,0 +1,24 @@ +description: "Discover single mongos" + +uri: "mongodb://a/?directConnection=false" + +phases: + + - responses: + - + - "a:27017" + - + ok: 1 + helloOk: true + isWritablePrimary: true + msg: "isdbgrid" + minWireVersion: 0 + maxWireVersion: 21 + + outcome: + servers: + "a:27017": + type: "Mongos" + setName: + topologyType: "Sharded" + setName: diff --git a/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json b/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json index 96f8dec17a..3da0f84ca2 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json +++ b/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.json @@ -8,22 +8,24 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "logicalSessionTimeoutMinutes": 1, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "logicalSessionTimeoutMinutes": 2, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -49,21 +51,23 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "logicalSessionTimeoutMinutes": 1, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml b/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml index 8da08da783..c336f22848 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/ls_timeout_mongos.yml @@ -10,21 +10,23 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", logicalSessionTimeoutMinutes: 1, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", logicalSessionTimeoutMinutes: 2, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -49,27 +51,29 @@ phases: [ setName: } }, - # Now an ismaster response with no logicalSessionTimeoutMinutes + # Now an isWritablePrimary response with no logicalSessionTimeoutMinutes { responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", logicalSessionTimeoutMinutes: 1, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -94,4 +98,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json b/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json index 04015694a8..29b3351869 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json +++ b/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.json @@ -8,20 +8,22 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], @@ -70,10 +72,11 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml b/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml index bb9890b8fe..2ee364961d 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/mongos_disconnect.yml @@ -10,19 +10,21 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -79,10 +81,11 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ], @@ -107,4 +110,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json b/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json index 6e60fd05c7..ae0c2d9cde 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json +++ b/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.json @@ -8,20 +8,22 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml b/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml index 462478c0bd..1aea49138f 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/multiple_mongoses.yml @@ -10,19 +10,21 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -47,4 +49,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json b/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json index 58cf7c07d7..4698f576d5 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json +++ b/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.json @@ -8,23 +8,25 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml b/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml index 476c799705..6afb88e25e 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/non_mongos_removed.yml @@ -10,20 +10,22 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -42,4 +44,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml b/source/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml index 9a144b3907..e955b9b315 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/normalize_uri_case.yml @@ -29,4 +29,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/too_new.json b/source/server-discovery-and-monitoring/tests/sharded/too_new.json index 9521e11789..c4e984ddec 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/too_new.json +++ b/source/server-discovery-and-monitoring/tests/sharded/too_new.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 999, "maxWireVersion": 1000 @@ -18,8 +19,11 @@ "b:27017", { "ok": 1, - "ismaster": true, - "msg": "isdbgrid" + "helloOk": true, + "isWritablePrimary": true, + "msg": "isdbgrid", + "minWireVersion": 7, + "maxWireVersion": 900 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/sharded/too_new.yml b/source/server-discovery-and-monitoring/tests/sharded/too_new.yml index 4e03636879..f04be4a088 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/too_new.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/too_new.yml @@ -5,15 +5,19 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 999, maxWireVersion: 1000 }], ["b:27017", { ok: 1, - ismaster: true, - msg: "isdbgrid" + helloOk: true, + isWritablePrimary: true, + msg: "isdbgrid", + minWireVersion: 7, + maxWireVersion: 900 }] ], outcome: { @@ -33,4 +37,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/too_old.json b/source/server-discovery-and-monitoring/tests/sharded/too_old.json index 6bd187f61d..b918715ada 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/too_old.json +++ b/source/server-discovery-and-monitoring/tests/sharded/too_old.json @@ -8,17 +8,19 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 2, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "b:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid" } ] diff --git a/source/server-discovery-and-monitoring/tests/sharded/too_old.yml b/source/server-discovery-and-monitoring/tests/sharded/too_old.yml index 2870057028..1e791569b8 100644 --- a/source/server-discovery-and-monitoring/tests/sharded/too_old.yml +++ b/source/server-discovery-and-monitoring/tests/sharded/too_old.yml @@ -5,14 +5,16 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 2, - maxWireVersion: 6 + maxWireVersion: 21 }], ["b:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid" }] ], @@ -33,4 +35,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/compatible.json b/source/server-discovery-and-monitoring/tests/single/compatible.json index ee6b847ade..493d9b748e 100644 --- a/source/server-discovery-and-monitoring/tests/single/compatible.json +++ b/source/server-discovery-and-monitoring/tests/single/compatible.json @@ -8,9 +8,10 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/compatible.yml b/source/server-discovery-and-monitoring/tests/single/compatible.yml index eff9b73cdd..92c5180f89 100644 --- a/source/server-discovery-and-monitoring/tests/single/compatible.yml +++ b/source/server-discovery-and-monitoring/tests/single/compatible.yml @@ -5,9 +5,10 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], outcome: { @@ -23,4 +24,4 @@ phases: [ compatible: true } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json index 4458150186..1461b4c469 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.json @@ -1,6 +1,6 @@ { "description": "Direct connection to RSPrimary via external IP", - "uri": "mongodb://a", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,13 +8,14 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml index bf9b68aeb7..9d631c3b0b 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_external_ip.yml @@ -1,6 +1,6 @@ description: "Direct connection to RSPrimary via external IP" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["b:27017"], # Internal IP. setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -33,4 +34,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json index a7fa079490..72be020862 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.json @@ -1,6 +1,6 @@ { - "description": "Connect to mongos", - "uri": "mongodb://a", + "description": "Direct connection to mongos", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,10 +8,11 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "msg": "isdbgrid", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml index e039723019..c23ce0556d 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_mongos.yml @@ -1,6 +1,6 @@ -description: "Connect to mongos" +description: "Direct connection to mongos" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,10 +10,11 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, msg: "isdbgrid", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -32,4 +33,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json new file mode 100644 index 0000000000..82a51d390e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.json @@ -0,0 +1,32 @@ +{ + "description": "Direct connection with replicaSet URI option", + "uri": "mongodb://a/?replicaSet=rs&directConnection=true", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml new file mode 100644 index 0000000000..9a0f3996ca --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_replicaset.yml @@ -0,0 +1,23 @@ +description: Direct connection with replicaSet URI option + +uri: "mongodb://a/?replicaSet=rs&directConnection=true" + +phases: + # We are connecting to a replica set member + - responses: + - + - "a:27017" + - ok: 1 + helloOk: true + isWritablePrimary: true + setName: rs + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + "a:27017": + type: "RSPrimary" + setName: "rs" + topologyType: "Single" + logicalSessionTimeoutMinutes: + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json index 3ef374d6f1..e06d284364 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.json @@ -1,6 +1,6 @@ { - "description": "Connect to RSArbiter", - "uri": "mongodb://a", + "description": "Direct connection to RSArbiter", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "arbiterOnly": true, "hosts": [ "a:27017", @@ -16,7 +17,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml index 4d03d0baab..cb8d054c5e 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsarbiter.yml @@ -1,6 +1,6 @@ -description: "Connect to RSArbiter" +description: "Direct connection to RSArbiter" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, arbiterOnly: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -34,4 +35,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json index bd5aaf7f04..45eb1602fb 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.json @@ -1,6 +1,6 @@ { - "description": "Connect to RSPrimary", - "uri": "mongodb://a", + "description": "Direct connection to RSPrimary", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,14 +8,15 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "hosts": [ "a:27017", "b:27017" ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml index c4b2fa0b3a..40d96a5aa9 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rsprimary.yml @@ -1,6 +1,6 @@ -description: "Connect to RSPrimary" +description: "Direct connection to RSPrimary" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,11 +10,12 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -33,4 +34,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json index 3b4f3c8c5a..b1bef8a49f 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.json @@ -1,6 +1,6 @@ { - "description": "Connect to RSSecondary", - "uri": "mongodb://a", + "description": "Direct connection to RSSecondary", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": false, + "helloOk": true, + "isWritablePrimary": false, "secondary": true, "hosts": [ "a:27017", @@ -16,7 +17,7 @@ ], "setName": "rs", "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml index a57332f253..766789d5d9 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_rssecondary.yml @@ -1,6 +1,6 @@ -description: "Connect to RSSecondary" +description: "Direct connection to RSSecondary" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,12 +10,13 @@ phases: [ ["a:27017", { ok: 1, - ismaster: false, + helloOk: true, + isWritablePrimary: false, secondary: true, hosts: ["a:27017", "b:27017"], setName: "rs", minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -34,4 +35,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.json deleted file mode 100644 index a40debd183..0000000000 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "description": "Direct connection to slave", - "uri": "mongodb://a", - "phases": [ - { - "responses": [ - [ - "a:27017", - { - "ok": 1, - "ismaster": false, - "minWireVersion": 0, - "maxWireVersion": 6 - } - ] - ], - "outcome": { - "servers": { - "a:27017": { - "type": "Standalone", - "setName": null - } - }, - "topologyType": "Single", - "logicalSessionTimeoutMinutes": null, - "setName": null - } - } - ] -} diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.yml deleted file mode 100644 index 166470cae5..0000000000 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_slave.yml +++ /dev/null @@ -1,34 +0,0 @@ -description: "Direct connection to slave" - -uri: "mongodb://a" - -phases: [ - - { - responses: [ - - ["a:27017", { - - ok: 1, - ismaster: false, - minWireVersion: 0, - maxWireVersion: 6 - }] - ], - - outcome: { - - servers: { - - "a:27017": { - - type: "Standalone", - setName: - } - }, - topologyType: "Single", - logicalSessionTimeoutMinutes: null, - setName: - } - } -] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json index 2ecff9b9ae..e71ba07e74 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.json @@ -1,6 +1,6 @@ { - "description": "Connect to standalone", - "uri": "mongodb://a", + "description": "Direct connection to standalone", + "uri": "mongodb://a/?directConnection=true", "phases": [ { "responses": [ @@ -8,9 +8,10 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml index 8c5bcdef44..4398a05d47 100644 --- a/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_standalone.yml @@ -1,6 +1,6 @@ -description: "Connect to standalone" +description: "Direct connection to standalone" -uri: "mongodb://a" +uri: "mongodb://a/?directConnection=true" phases: [ @@ -10,9 +10,10 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -31,4 +32,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json new file mode 100644 index 0000000000..16f2735da5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.json @@ -0,0 +1,25 @@ +{ + "description": "Direct connection to unavailable seed", + "uri": "mongodb://a/?directConnection=true", + "phases": [ + { + "responses": [ + [ + "a:27017", + {} + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml new file mode 100644 index 0000000000..d62e26213c --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_unavailable_seed.yml @@ -0,0 +1,28 @@ +description: "Direct connection to unavailable seed" + +uri: "mongodb://a/?directConnection=true" + +phases: [ + + { + responses: [ + + ["a:27017", {}] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "Single", + logicalSessionTimeoutMinutes: null, + setName: + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json b/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json new file mode 100644 index 0000000000..8014a0a533 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.json @@ -0,0 +1,65 @@ +{ + "description": "Direct connection to RSPrimary with wrong set name", + "uri": "mongodb://a/?directConnection=true&replicaSet=rs", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "wrong", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown" + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "hosts": [ + "a:27017", + "b:27017" + ], + "setName": "rs", + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "RSPrimary", + "setName": "rs" + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": "rs" + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml b/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml new file mode 100644 index 0000000000..46b476d049 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/direct_connection_wrong_set_name.yml @@ -0,0 +1,40 @@ +description: Direct connection to RSPrimary with wrong set name +uri: mongodb://a/?directConnection=true&replicaSet=rs +phases: +- responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + - b:27017 + setName: wrong + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + a:27017: + type: Unknown + topologyType: Single + logicalSessionTimeoutMinutes: + setName: rs +- responses: + - - a:27017 + - ok: 1 + helloOk: true + isWritablePrimary: true + hosts: + - a:27017 + - b:27017 + setName: rs + minWireVersion: 0 + maxWireVersion: 21 + outcome: + servers: + a:27017: + type: RSPrimary + setName: rs + topologyType: Single + logicalSessionTimeoutMinutes: + setName: rs diff --git a/source/server-discovery-and-monitoring/tests/single/discover_standalone.json b/source/server-discovery-and-monitoring/tests/single/discover_standalone.json new file mode 100644 index 0000000000..d78c81654b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/discover_standalone.json @@ -0,0 +1,31 @@ +{ + "description": "Discover standalone", + "uri": "mongodb://a/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Standalone", + "setName": null + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/discover_standalone.yml b/source/server-discovery-and-monitoring/tests/single/discover_standalone.yml new file mode 100644 index 0000000000..1412bf62ac --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/discover_standalone.yml @@ -0,0 +1,35 @@ +description: "Discover standalone" + +uri: "mongodb://a/?directConnection=false" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + helloOk: true, + isWritablePrimary: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Standalone", + setName: + } + }, + topologyType: "Single", + logicalSessionTimeoutMinutes: null, + setName: + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json b/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json new file mode 100644 index 0000000000..b1f306c2be --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.json @@ -0,0 +1,25 @@ +{ + "description": "Discover unavailable seed", + "uri": "mongodb://a/?directConnection=false", + "phases": [ + { + "responses": [ + [ + "a:27017", + {} + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Unknown", + "setName": null + } + }, + "topologyType": "Unknown", + "logicalSessionTimeoutMinutes": null, + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml b/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml new file mode 100644 index 0000000000..6d9bd98b4b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/discover_unavailable_seed.yml @@ -0,0 +1,28 @@ +description: "Discover unavailable seed" + +uri: "mongodb://a/?directConnection=false" + +phases: [ + + { + responses: [ + + ["a:27017", {}] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Unknown", + setName: + } + }, + topologyType: "Unknown", + logicalSessionTimeoutMinutes: null, + setName: + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json b/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json index ae6c8ba11b..236eabe00a 100644 --- a/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json +++ b/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.json @@ -8,10 +8,11 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "logicalSessionTimeoutMinutes": 7, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml b/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml index d0273db6cd..216431bd6b 100644 --- a/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml +++ b/source/server-discovery-and-monitoring/tests/single/ls_timeout_standalone.yml @@ -10,10 +10,11 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, logicalSessionTimeoutMinutes: 7, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -32,4 +33,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/not_ok_response.json b/source/server-discovery-and-monitoring/tests/single/not_ok_response.json index 06f71305dc..cfaac3564a 100644 --- a/source/server-discovery-and-monitoring/tests/single/not_ok_response.json +++ b/source/server-discovery-and-monitoring/tests/single/not_ok_response.json @@ -1,5 +1,5 @@ { - "description": "Handle a not-ok ismaster response", + "description": "Handle a not-ok isWritablePrimary response", "uri": "mongodb://a", "phases": [ { @@ -8,18 +8,20 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ], [ "a:27017", { "ok": 0, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/not_ok_response.yml b/source/server-discovery-and-monitoring/tests/single/not_ok_response.yml index 6d49cca9c2..c668219a6d 100644 --- a/source/server-discovery-and-monitoring/tests/single/not_ok_response.yml +++ b/source/server-discovery-and-monitoring/tests/single/not_ok_response.yml @@ -1,4 +1,4 @@ -description: "Handle a not-ok ismaster response" +description: "Handle a not-ok isWritablePrimary response" uri: "mongodb://a" @@ -10,17 +10,19 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }], ["a:27017", { ok: 0, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -39,4 +41,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/standalone_removed.json b/source/server-discovery-and-monitoring/tests/single/standalone_removed.json index 4c363ffffb..675cdbb008 100644 --- a/source/server-discovery-and-monitoring/tests/single/standalone_removed.json +++ b/source/server-discovery-and-monitoring/tests/single/standalone_removed.json @@ -8,9 +8,10 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 6 + "maxWireVersion": 21 } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/standalone_removed.yml b/source/server-discovery-and-monitoring/tests/single/standalone_removed.yml index ff8c64f533..82a63e7cee 100644 --- a/source/server-discovery-and-monitoring/tests/single/standalone_removed.yml +++ b/source/server-discovery-and-monitoring/tests/single/standalone_removed.yml @@ -10,9 +10,10 @@ phases: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 0, - maxWireVersion: 6 + maxWireVersion: 21 }] ], @@ -31,4 +32,4 @@ phases: [ setName: } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json b/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json new file mode 100644 index 0000000000..488cac4918 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.json @@ -0,0 +1,30 @@ +{ + "description": "Connect to standalone using legacy hello", + "uri": "mongodb://a", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "ismaster": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Standalone", + "setName": null + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": null + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml b/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml new file mode 100644 index 0000000000..22265d7409 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/standalone_using_legacy_hello.yml @@ -0,0 +1,34 @@ +description: "Connect to standalone using legacy hello" + +uri: "mongodb://a" + +phases: [ + + { + responses: [ + + ["a:27017", { + + ok: 1, + ismaster: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + + outcome: { + + servers: { + + "a:27017": { + + type: "Standalone", + setName: + } + }, + topologyType: "Single", + logicalSessionTimeoutMinutes: null, + setName: + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/single/too_new.json b/source/server-discovery-and-monitoring/tests/single/too_new.json index 38e4621d60..8dd57d3348 100644 --- a/source/server-discovery-and-monitoring/tests/single/too_new.json +++ b/source/server-discovery-and-monitoring/tests/single/too_new.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true, + "helloOk": true, + "isWritablePrimary": true, "minWireVersion": 999, "maxWireVersion": 1000 } diff --git a/source/server-discovery-and-monitoring/tests/single/too_new.yml b/source/server-discovery-and-monitoring/tests/single/too_new.yml index 2c295c5406..b4c11adf27 100644 --- a/source/server-discovery-and-monitoring/tests/single/too_new.yml +++ b/source/server-discovery-and-monitoring/tests/single/too_new.yml @@ -5,7 +5,8 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true, + helloOk: true, + isWritablePrimary: true, minWireVersion: 999, maxWireVersion: 1000 }] @@ -23,4 +24,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/too_old.json b/source/server-discovery-and-monitoring/tests/single/too_old.json index fbf68262c0..8c027e01db 100644 --- a/source/server-discovery-and-monitoring/tests/single/too_old.json +++ b/source/server-discovery-and-monitoring/tests/single/too_old.json @@ -8,7 +8,8 @@ "a:27017", { "ok": 1, - "ismaster": true + "helloOk": true, + "isWritablePrimary": true } ] ], diff --git a/source/server-discovery-and-monitoring/tests/single/too_old.yml b/source/server-discovery-and-monitoring/tests/single/too_old.yml index 0035f5b06d..4931e0c2d3 100644 --- a/source/server-discovery-and-monitoring/tests/single/too_old.yml +++ b/source/server-discovery-and-monitoring/tests/single/too_old.yml @@ -5,7 +5,8 @@ phases: [ responses: [ ["a:27017", { ok: 1, - ismaster: true + helloOk: true, + isWritablePrimary: true }] ], outcome: { @@ -21,4 +22,4 @@ phases: [ compatible: false } } -] + ] diff --git a/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json b/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json new file mode 100644 index 0000000000..c3dd98cf62 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.json @@ -0,0 +1,56 @@ +{ + "description": "Standalone with default maxWireVersion of 0 is upgraded to one with maxWireVersion 21", + "uri": "mongodb://a", + "phases": [ + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Standalone", + "setName": null + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": null, + "compatible": false + } + }, + { + "responses": [ + [ + "a:27017", + { + "ok": 1, + "helloOk": true, + "isWritablePrimary": true, + "minWireVersion": 0, + "maxWireVersion": 21 + } + ] + ], + "outcome": { + "servers": { + "a:27017": { + "type": "Standalone", + "setName": null + } + }, + "topologyType": "Single", + "logicalSessionTimeoutMinutes": null, + "setName": null, + "compatible": true + } + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml b/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml new file mode 100644 index 0000000000..25f99f5b9d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/single/too_old_then_upgraded.yml @@ -0,0 +1,48 @@ +description: "Standalone with default maxWireVersion of 0 is upgraded to one with maxWireVersion 21" +uri: "mongodb://a" +phases: [ + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true + }] + ], + outcome: { + servers: { + "a:27017": { + type: "Standalone", + setName: + } + }, + topologyType: "Single", + logicalSessionTimeoutMinutes: null, + setName: , + compatible: false + } + }, + { + responses: [ + ["a:27017", { + ok: 1, + helloOk: true, + isWritablePrimary: true, + minWireVersion: 0, + maxWireVersion: 21 + }] + ], + outcome: { + servers: { + "a:27017": { + type: "Standalone", + setName: + } + }, + topologyType: "Single", + logicalSessionTimeoutMinutes: null, + setName: , + compatible: true + } + } + ] diff --git a/source/server-discovery-and-monitoring/tests/single/unavailable_seed.json b/source/server-discovery-and-monitoring/tests/single/unavailable_seed.json deleted file mode 100644 index e9cce02ebf..0000000000 --- a/source/server-discovery-and-monitoring/tests/single/unavailable_seed.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "description": "Unavailable seed", - "uri": "mongodb://a", - "phases": [ - { - "responses": [ - [ - "a:27017", - {} - ] - ], - "outcome": { - "servers": { - "a:27017": { - "type": "Unknown", - "setName": null - } - }, - "topologyType": "Single", - "logicalSessionTimeoutMinutes": null, - "setName": null - } - } - ] -} diff --git a/source/server-discovery-and-monitoring/tests/single/unavailable_seed.yml b/source/server-discovery-and-monitoring/tests/single/unavailable_seed.yml deleted file mode 100644 index c4cfbf76cc..0000000000 --- a/source/server-discovery-and-monitoring/tests/single/unavailable_seed.yml +++ /dev/null @@ -1,28 +0,0 @@ -description: "Unavailable seed" - -uri: "mongodb://a" - -phases: [ - - { - responses: [ - - ["a:27017", {}] - ], - - outcome: { - - servers: { - - "a:27017": { - - type: "Unknown", - setName: - } - }, - topologyType: "Single", - logicalSessionTimeoutMinutes: null, - setName: - } - } -] diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-error.json b/source/server-discovery-and-monitoring/tests/unified/auth-error.json new file mode 100644 index 0000000000..62d26494c7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-error.json @@ -0,0 +1,230 @@ +{ + "description": "auth-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "auth": true, + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "auth-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after AuthenticationFailure error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "authErrorTest", + "errorCode": 18 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "auth-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "auth-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "auth-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-error.yml b/source/server-discovery-and-monitoring/tests/unified/auth-error.yml new file mode 100644 index 0000000000..febf4a46dc --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-error.yml @@ -0,0 +1,130 @@ +description: auth-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + auth: true + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName auth-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: Reset server and pool after AuthenticationFailure error + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + appName: authErrorTest + errorCode: 18 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: authErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + # Note: The first insert command is never attempted because connection + # checkout fails. + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: auth-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.json b/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.json new file mode 100644 index 0000000000..fd62fe604e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.json @@ -0,0 +1,230 @@ +{ + "description": "auth-misc-command-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "auth": true, + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "auth-misc-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after misc command error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "authMiscErrorTest", + "errorCode": 1 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authMiscErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "auth-misc-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "auth-misc-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "auth-misc-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.yml b/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.yml new file mode 100644 index 0000000000..9969ca92d4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-misc-command-error.yml @@ -0,0 +1,132 @@ +--- +description: auth-misc-command-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + auth: true + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName auth-misc-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after misc command error + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + appName: authMiscErrorTest + errorCode: 1 # InternalError + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: authMiscErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + # Note: The first insert command is never attempted because connection + # checkout fails. + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: auth-misc-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-network-error.json b/source/server-discovery-and-monitoring/tests/unified/auth-network-error.json new file mode 100644 index 0000000000..84763af32e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-network-error.json @@ -0,0 +1,230 @@ +{ + "description": "auth-network-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "auth": true, + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "auth-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after network error during authentication", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "authNetworkErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authNetworkErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "auth-network-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "auth-network-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "auth-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-network-error.yml b/source/server-discovery-and-monitoring/tests/unified/auth-network-error.yml new file mode 100644 index 0000000000..cdf77c56df --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-network-error.yml @@ -0,0 +1,132 @@ +--- +description: auth-network-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + auth: true + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName auth-network-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after network error during authentication + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + closeConnection: true + appName: authNetworkErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: authNetworkErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + # Note: The first insert command is never attempted because connection + # checkout fails. + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: auth-network-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.json b/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.json new file mode 100644 index 0000000000..3cf9576eba --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.json @@ -0,0 +1,233 @@ +{ + "description": "auth-network-timeout-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "auth": true, + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "auth-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after network timeout error during authentication", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "blockConnection": true, + "blockTimeMS": 500, + "appName": "authNetworkTimeoutErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authNetworkTimeoutErrorTest", + "connectTimeoutMS": 250, + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "auth-network-timeout-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "auth-network-timeout-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "auth-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.yml b/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.yml new file mode 100644 index 0000000000..49b91d8373 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.yml @@ -0,0 +1,138 @@ +--- +description: auth-network-timeout-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + auth: true + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName auth-network-timeout-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after network timeout error during authentication + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + blockConnection: true + blockTimeMS: 500 + appName: authNetworkTimeoutErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: authNetworkTimeoutErrorTest + # Set a short connect/socket timeout to ensure the fail point causes the + # connection establishment to timeout. + connectTimeoutMS: 250 + socketTimeoutMS: 250 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + # Note: The first insert command is never attempted because connection + # checkout fails. + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: auth-network-timeout-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.json b/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.json new file mode 100644 index 0000000000..b9e503af66 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.json @@ -0,0 +1,230 @@ +{ + "description": "auth-shutdown-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "auth": true, + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "auth-shutdown-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after shutdown error during authentication", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "authShutdownErrorTest", + "errorCode": 91 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authShutdownErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "auth-shutdown-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "auth-shutdown-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "auth-shutdown-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.yml b/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.yml new file mode 100644 index 0000000000..f0bb4d17df --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/auth-shutdown-error.yml @@ -0,0 +1,133 @@ +--- +description: auth-shutdown-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + auth: true + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName auth-shutdown-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after shutdown error during authentication + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + appName: authShutdownErrorTest + errorCode: 91 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: authShutdownErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + # Note: The first insert command is never attempted because connection + # checkout fails. + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: auth-shutdown-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.json b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.json new file mode 100644 index 0000000000..ccaea8d135 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.json @@ -0,0 +1,142 @@ +{ + "description": "backpressure-network-error-fail-replicaset", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backpressure-network-error-fail", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "apply backpressure on network connection errors during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 1000000, + "serverMonitoringMode": "poll", + "appname": "backpressureNetworkErrorFailTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backpressure-network-error-fail" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "RSPrimary" + } + } + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "appName": "backpressureNetworkErrorFailTest", + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.yml b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.yml new file mode 100644 index 0000000000..3aac5349be --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-replicaset.yml @@ -0,0 +1,80 @@ +description: backpressure-network-error-fail-replicaset +schemaVersion: "1.17" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + topologies: + - replicaset +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backpressure-network-error-fail + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: apply backpressure on network connection errors during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 1000000 + serverMonitoringMode: poll + appname: backpressureNetworkErrorFailTest + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backpressure-network-error-fail + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverDescriptionChangedEvent: + newDescription: + type: RSPrimary + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + appName: backpressureNetworkErrorFailTest + closeConnection: true + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + expectEvents: + - client: client + eventType: cmap + events: [] diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.json b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.json new file mode 100644 index 0000000000..c1ff67c732 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.json @@ -0,0 +1,142 @@ +{ + "description": "backpressure-network-error-fail-single", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backpressure-network-error-fail", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "apply backpressure on network connection errors during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 1000000, + "serverMonitoringMode": "poll", + "appname": "backpressureNetworkErrorFailTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backpressure-network-error-fail" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Standalone" + } + } + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "appName": "backpressureNetworkErrorFailTest", + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.yml b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.yml new file mode 100644 index 0000000000..f5eebf2766 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-error-fail-single.yml @@ -0,0 +1,80 @@ +description: backpressure-network-error-fail-single +schemaVersion: "1.17" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + topologies: + - single +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backpressure-network-error-fail + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: apply backpressure on network connection errors during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 1000000 + serverMonitoringMode: poll + appname: backpressureNetworkErrorFailTest + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backpressure-network-error-fail + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverDescriptionChangedEvent: + newDescription: + type: Standalone + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + appName: backpressureNetworkErrorFailTest + closeConnection: true + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + expectEvents: + - client: client + eventType: cmap + events: [] diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.json b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.json new file mode 100644 index 0000000000..35b088f422 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.json @@ -0,0 +1,145 @@ +{ + "description": "backpressure-network-timeout-error-replicaset", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backpressure-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "apply backpressure on network timeout error during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 1000000, + "appname": "backpressureNetworkTimeoutErrorTest", + "serverMonitoringMode": "poll", + "connectTimeoutMS": 250, + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backpressure-network-timeout-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "RSPrimary" + } + } + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "blockConnection": true, + "blockTimeMS": 500, + "appName": "backpressureNetworkTimeoutErrorTest" + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.yml b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.yml new file mode 100644 index 0000000000..487872adc6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-replicaset.yml @@ -0,0 +1,83 @@ +description: backpressure-network-timeout-error-replicaset +schemaVersion: "1.17" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + topologies: + - replicaset +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backpressure-network-timeout-error + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: apply backpressure on network timeout error during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 1000000 + appname: backpressureNetworkTimeoutErrorTest + serverMonitoringMode: poll + connectTimeoutMS: 250 + socketTimeoutMS: 250 + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backpressure-network-timeout-error + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverDescriptionChangedEvent: + newDescription: + type: RSPrimary + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + blockConnection: true + blockTimeMS: 500 + appName: backpressureNetworkTimeoutErrorTest + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + expectEvents: + - client: client + eventType: cmap + events: [] diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.json b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.json new file mode 100644 index 0000000000..54b11d4d5b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.json @@ -0,0 +1,145 @@ +{ + "description": "backpressure-network-timeout-error-single", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backpressure-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "apply backpressure on network timeout error during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 1000000, + "appname": "backpressureNetworkTimeoutErrorTest", + "serverMonitoringMode": "poll", + "connectTimeoutMS": 250, + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backpressure-network-timeout-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Standalone" + } + } + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "blockConnection": true, + "blockTimeMS": 500, + "appName": "backpressureNetworkTimeoutErrorTest" + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.yml b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.yml new file mode 100644 index 0000000000..6d94173174 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-network-timeout-fail-single.yml @@ -0,0 +1,83 @@ +description: backpressure-network-timeout-error-single +schemaVersion: "1.17" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + topologies: + - single +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backpressure-network-timeout-error + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: apply backpressure on network timeout error during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 1000000 + appname: backpressureNetworkTimeoutErrorTest + serverMonitoringMode: poll + connectTimeoutMS: 250 + socketTimeoutMS: 250 + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backpressure-network-timeout-error + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverDescriptionChangedEvent: + newDescription: + type: Standalone + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + blockConnection: true + blockTimeMS: 500 + appName: backpressureNetworkTimeoutErrorTest + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + expectEvents: + - client: client + eventType: cmap + events: [] diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.json b/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.json new file mode 100644 index 0000000000..f0597124b7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.json @@ -0,0 +1,106 @@ +{ + "description": "backpressure-server-description-unchanged-on-min-pool-size-population-error", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "tests": [ + { + "description": "the server description is not changed on handshake error during minPoolSize population", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "authErrorTest", + "closeConnection": true + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "serverDescriptionChangedEvent", + "connectionClosedEvent" + ], + "uriOptions": { + "appname": "authErrorTest", + "minPoolSize": 5, + "maxConnecting": 1, + "serverMonitoringMode": "poll", + "heartbeatFrequencyMS": 1000000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "events": [ + { + "serverDescriptionChangedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.yml b/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.yml new file mode 100644 index 0000000000..e1d98dae20 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backpressure-server-description-unchanged-on-min-pool-size-population-error.yml @@ -0,0 +1,62 @@ +description: backpressure-server-description-unchanged-on-min-pool-size-population-error +schemaVersion: "1.17" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + topologies: + - single +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +tests: + - description: the server description is not changed on handshake error during minPoolSize population + operations: + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: + skip: 1 + data: + failCommands: + - hello + - isMaster + appName: authErrorTest + closeConnection: true + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + observeEvents: + - serverDescriptionChangedEvent + - connectionClosedEvent + uriOptions: + appname: authErrorTest + minPoolSize: 5 + maxConnecting: 1 + serverMonitoringMode: poll + heartbeatFrequencyMS: 1000000 + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverDescriptionChangedEvent: {} + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + connectionClosedEvent: {} + count: 1 + expectEvents: + - client: client + eventType: sdam + events: + - serverDescriptionChangedEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.json b/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.json new file mode 100644 index 0000000000..a60ccfcb41 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.json @@ -0,0 +1,201 @@ +{ + "description": "cancel-server-check", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ], + "serverless": "forbid" + }, + { + "minServerVersion": "4.2", + "topologies": [ + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "cancel-server-check", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Cancel server check", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": true, + "heartbeatFrequencyMS": 10000, + "serverSelectionTimeoutMS": 5000, + "appname": "cancelServerCheckTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "cancel-server-check" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + }, + "client": "setupClient" + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + } + }, + "expectResult": { + "insertedId": 2 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 3 + } + }, + "expectResult": { + "insertedId": 3 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "outcome": [ + { + "collectionName": "cancel-server-check", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.yml b/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.yml new file mode 100644 index 0000000000..af46792546 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/cancel-server-check.yml @@ -0,0 +1,143 @@ +--- +description: cancel-server-check + +schemaVersion: "1.4" + +runOnRequirements: + # General failCommand requirements (this file does not use appName + # with failCommand). + - minServerVersion: "4.0" + topologies: + - replicaset + serverless: forbid + - minServerVersion: "4.2" + topologies: + - sharded + serverless: forbid + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName cancel-server-check + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Cancel server check + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: true + heartbeatFrequencyMS: 10000 + # Server selection timeout MUST be less than heartbeatFrequencyMS for + # this test. This setting ensures that the retried insert will fail + # after 5 seconds if the driver does not properly cancel the in progress + # check. + serverSelectionTimeoutMS: 5000 + appname: cancelServerCheckTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + # Configure the next inserts to fail with a non-timeout network error. + # This should: + # 1) Mark the server Unknown + # 2) Clear the connection pool + # 3) Cancel the in progress hello or legacy hello check and close the Monitor + # connection + # 4) The write will be then we retried, server selection will request an + # immediate check, and block for ~500ms until the next Monitor check + # proceeds. + # 5) The write will succeed on the second attempt. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + closeConnection: true + client: *setupClient + - name: insertOne + object: *collection + arguments: + document: + _id: 2 + expectResult: + insertedId: 2 + # The first error should mark the server Unknown and then clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node still selectable. + - name: insertOne + object: *collection + arguments: + document: + _id: 3 + expectResult: + insertedId: 3 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + + # Order of operations is non-deterministic so we cannot check events. + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 diff --git a/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.json b/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.json new file mode 100644 index 0000000000..d3e860a9cb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.json @@ -0,0 +1,221 @@ +{ + "description": "connectTimeoutMS", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "connectTimeoutMS", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "connectTimeoutMS=0", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 0, + "heartbeatFrequencyMS": 500, + "appname": "connectTimeoutMS=0" + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "connectTimeoutMS" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "connectTimeoutMS=0", + "blockConnection": true, + "blockTimeMS": 550 + } + }, + "client": "setupClient" + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 750 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 0 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "connectTimeoutMS", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "connectTimeoutMS", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "connectTimeoutMS", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.yml b/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.yml new file mode 100644 index 0000000000..7c610623eb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/connectTimeoutMS.yml @@ -0,0 +1,130 @@ +--- +description: connectTimeoutMS + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName connectTimeoutMS + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: connectTimeoutMS=0 + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 0 + heartbeatFrequencyMS: 500 + appname: connectTimeoutMS=0 + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # Block the next streaming hello check for longer than + # heartbeatFrequencyMS to ensure that the connection timeout remains + # unlimited. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - hello + - isMaster + appName: connectTimeoutMS=0 + blockConnection: true + blockTimeMS: 550 + client: *setupClient + - name: wait + object: testRunner + arguments: + ms: 750 + # Perform an operation to ensure the node is still selectable. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + # Assert that the server was never marked Unknown and the pool was never + # cleared. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 0 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 0 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: connectTimeoutMS + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: connectTimeoutMS + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/find-network-error.json b/source/server-discovery-and-monitoring/tests/unified/find-network-error.json new file mode 100644 index 0000000000..c1b6db40ca --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-network-error.json @@ -0,0 +1,234 @@ +{ + "description": "find-network-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "find-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after network error on find", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true, + "appName": "findNetworkErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appname": "findNetworkErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "find-network-error" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "find-network-error" + }, + "commandName": "find", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "find-network-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "find-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/find-network-error.yml b/source/server-discovery-and-monitoring/tests/unified/find-network-error.yml new file mode 100644 index 0000000000..f97d799a2a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-network-error.yml @@ -0,0 +1,135 @@ +--- +description: find-network-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName find-network-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after network error on find + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + appName: findNetworkErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + retryReads: false + appname: findNetworkErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: find + object: *collection + arguments: + filter: + _id: 1 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: find-network-error + commandName: find + databaseName: *databaseName + - commandStartedEvent: + command: + insert: find-network-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.json b/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.json new file mode 100644 index 0000000000..e5ac9f21aa --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.json @@ -0,0 +1,199 @@ +{ + "description": "find-network-timeout-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "find-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Ignore network timeout error on find", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "blockConnection": true, + "blockTimeMS": 500, + "appName": "findNetworkTimeoutErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appname": "findNetworkTimeoutErrorTest", + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "find-network-timeout-error" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 3 + } + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 0 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "find-network-timeout-error" + }, + "commandName": "find", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "find-network-timeout-error", + "documents": [ + { + "_id": 3 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "find-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.yml b/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.yml new file mode 100644 index 0000000000..e00b7a2be0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.yml @@ -0,0 +1,119 @@ +--- +description: find-network-timeout-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName find-network-timeout-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Ignore network timeout error on find + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + blockConnection: true + blockTimeMS: 500 + appName: findNetworkTimeoutErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + retryReads: false + appname: findNetworkTimeoutErrorTest + # Set a short socket timeout to ensure the find command times out. + socketTimeoutMS: 250 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: find + object: *collection + arguments: + filter: + _id: 1 + expectError: + isError: true + # Perform another operation to ensure the node is still usable. + - name: insertOne + object: *collection + arguments: + document: + _id: 3 + # Assert the server was not marked Unknown and the pool was not cleared. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 0 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 0 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + find: find-network-timeout-error + commandName: find + databaseName: *databaseName + - commandStartedEvent: + command: + insert: find-network-timeout-error + documents: + - _id: 3 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 diff --git a/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.json b/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.json new file mode 100644 index 0000000000..6e5a2cac05 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.json @@ -0,0 +1,251 @@ +{ + "description": "find-shutdown-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "find-shutdown-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Concurrent shutdown error on find", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "heartbeatFrequencyMS": 500, + "appname": "shutdownErrorFindTest" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "find-shutdown-error" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "appName": "shutdownErrorFindTest", + "errorCode": 91, + "blockConnection": true, + "blockTimeMS": 500 + } + }, + "client": "setupClient" + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "thread": { + "id": "thread0" + } + }, + { + "thread": { + "id": "thread1" + } + } + ] + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread0", + "operation": { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread0" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 4 + } + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "outcome": [ + { + "collectionName": "find-shutdown-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.yml b/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.yml new file mode 100644 index 0000000000..395215244b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/find-shutdown-error.yml @@ -0,0 +1,163 @@ +--- +description: find-shutdown-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName find-shutdown-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Concurrent shutdown error on find + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + retryWrites: false + retryReads: false + heartbeatFrequencyMS: 500 + appname: shutdownErrorFindTest + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + # Configure the next two finds to fail with a non-timeout shutdown + # errors. Block the connection for 500ms to ensure both operations check + # out connections from the same pool generation. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - find + appName: shutdownErrorFindTest + errorCode: 91 + blockConnection: true + blockTimeMS: 500 + client: *setupClient + # Start threads. + - name: createEntities + object: testRunner + arguments: + entities: + - thread: + id: &thread0 thread0 + - thread: + id: &thread1 thread1 + # Perform concurrent find operations. Both fail with shutdown errors. + - name: runOnThread + object: testRunner + arguments: + thread: *thread0 + operation: + name: find + object: *collection + arguments: + filter: + _id: 1 + expectError: + isError: true + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: find + object: *collection + arguments: + filter: + _id: 1 + expectError: + isError: true + # Stop threads. + - name: waitForThread + object: testRunner + arguments: + thread: *thread0 + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + # The first shutdown error should mark the server Unknown and then clear + # the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node is rediscovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 4 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + + # Order of operations is non-deterministic so we cannot check events. + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-command-error.json b/source/server-discovery-and-monitoring/tests/unified/hello-command-error.json new file mode 100644 index 0000000000..87958cb2c0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-command-error.json @@ -0,0 +1,376 @@ +{ + "description": "hello-command-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4.7", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "hello-command-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Command error on Monitor handshake", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "commandErrorHandshakeTest", + "closeConnection": false, + "errorCode": 91 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent", + "commandStartedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 250, + "heartbeatFrequencyMS": 500, + "appname": "commandErrorHandshakeTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-command-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-command-error", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-command-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Command error on Monitor check", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 1000, + "heartbeatFrequencyMS": 500, + "appname": "commandErrorCheckTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-command-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "commandErrorCheckTest", + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 750, + "errorCode": 91 + } + }, + "client": "setupClient" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-command-error", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "hello-command-error", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-command-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-command-error.yml b/source/server-discovery-and-monitoring/tests/unified/hello-command-error.yml new file mode 100644 index 0000000000..1c9c079220 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-command-error.yml @@ -0,0 +1,233 @@ +--- +description: hello-command-error + +schemaVersion: "1.4" + +runOnRequirements: + # Require SERVER-49336 for failCommand + appName on the initial handshake. + - minServerVersion: "4.4.7" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName hello-command-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Command error on Monitor handshake + operations: + # Configure the next streaming hello check to fail with a command error. + # Use "times: 4" to increase the probability that the Monitor check fails + # since the RTT hello may trigger this failpoint one or many times as + # well. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + appName: commandErrorHandshakeTest + closeConnection: false + errorCode: 91 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + - commandStartedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 250 + heartbeatFrequencyMS: 500 + appname: commandErrorHandshakeTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # The command error on the initial handshake should mark the server + # Unknown (emitting a ServerDescriptionChangedEvent) and clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may or may not have + # triggered this failpoint as well. + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-command-error + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + + - description: Command error on Monitor check + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 1000 + heartbeatFrequencyMS: 500 + appname: commandErrorCheckTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # Configure the next streaming hello check to fail with a command + # error. + # Use times: 2 so that the RTT hello is blocked as well. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - hello + - isMaster + appName: commandErrorCheckTest + closeConnection: false + blockConnection: true + blockTimeMS: 750 + errorCode: 91 + client: *setupClient + # The command error on the next check should mark the server Unknown and + # clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-command-error + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: hello-command-error + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-network-error.json b/source/server-discovery-and-monitoring/tests/unified/hello-network-error.json new file mode 100644 index 0000000000..15ed2b605e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-network-error.json @@ -0,0 +1,346 @@ +{ + "description": "hello-network-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4.7", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "hello-network-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Network error on Monitor handshake", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "networkErrorHandshakeTest", + "closeConnection": true + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 250, + "heartbeatFrequencyMS": 500, + "appname": "networkErrorHandshakeTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-network-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-network-error", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Network error on Monitor check", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 250, + "heartbeatFrequencyMS": 500, + "appname": "networkErrorCheckTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-network-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "networkErrorCheckTest", + "closeConnection": true + } + }, + "client": "setupClient" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-network-error", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "hello-network-error", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-network-error.yml b/source/server-discovery-and-monitoring/tests/unified/hello-network-error.yml new file mode 100644 index 0000000000..5f29194fc6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-network-error.yml @@ -0,0 +1,227 @@ +--- +description: hello-network-error + +schemaVersion: "1.4" + +runOnRequirements: + # Require SERVER-49336 for failCommand + appName on the initial handshake. + - minServerVersion: "4.4.7" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName hello-network-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Network error on Monitor handshake + # Configure the initial handshake to fail with a network error. + # Use times: 2 so that the RTT hello fails as well. + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - hello + - isMaster + appName: networkErrorHandshakeTest + closeConnection: true + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 250 + heartbeatFrequencyMS: 500 + appname: networkErrorHandshakeTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # The network error on the initial handshake should mark the server + # Unknown (emitting a ServerDescriptionChangedEvent) and clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may or may not have + # triggered this failpoint as well. + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-network-error + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + + - description: Network error on Monitor check + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 250 + heartbeatFrequencyMS: 500 + appname: networkErrorCheckTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # Configure the next streaming hello check to fail with a non-timeout + # network error. Use "times: 4" to increase the probability that the + # Monitor check fails since the RTT hello may trigger this failpoint one + # or many times as well. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + appName: networkErrorCheckTest + closeConnection: true + client: *setupClient + # The network error on the next check should mark the server Unknown and + # clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may or may not have + # triggered this failpoint as well. + # - name: assertEventCount + # object: testRunner + # arguments: + # client: *client + # event: + # serverDescriptionChangedEvent: + # newDescription: + # type: Unknown + # count: 1 + # - name: assertEventCount + # object: testRunner + # arguments: + # event: + # poolClearedEvent: {} + # count: 1 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-network-error + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: hello-network-error + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-timeout.json b/source/server-discovery-and-monitoring/tests/unified/hello-timeout.json new file mode 100644 index 0000000000..fe7cf4e78d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-timeout.json @@ -0,0 +1,514 @@ +{ + "description": "hello-timeout", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "hello-timeout", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Network timeout on Monitor handshake", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "timeoutMonitorHandshakeTest", + "blockConnection": true, + "blockTimeMS": 1000 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 250, + "heartbeatFrequencyMS": 500, + "appname": "timeoutMonitorHandshakeTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-timeout" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-timeout", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-timeout", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Network timeout on Monitor check", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 750, + "heartbeatFrequencyMS": 500, + "appname": "timeoutMonitorCheckTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-timeout" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "timeoutMonitorCheckTest", + "blockConnection": true, + "blockTimeMS": 1000 + } + }, + "client": "setupClient" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-timeout", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "hello-timeout", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-timeout", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "Driver extends timeout while streaming", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "connectTimeoutMS": 250, + "heartbeatFrequencyMS": 500, + "appname": "extendsTimeoutTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "hello-timeout" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 2000 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 0 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "hello-timeout", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "hello-timeout", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "hello-timeout", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/hello-timeout.yml b/source/server-discovery-and-monitoring/tests/unified/hello-timeout.yml new file mode 100644 index 0000000000..2a3374d1e7 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/hello-timeout.yml @@ -0,0 +1,318 @@ +--- +description: hello-timeout + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName hello-timeout + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Network timeout on Monitor handshake + operations: + # Configure the initial handshake to fail with a timeout. + # Use times: 2 so that the RTT hello is blocked as well. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - hello + - isMaster + appName: timeoutMonitorHandshakeTest + blockConnection: true + blockTimeMS: 1000 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 250 + heartbeatFrequencyMS: 500 + appname: timeoutMonitorHandshakeTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # The network error on the initial handshake should mark the server + # Unknown (emitting a ServerDescriptionChangedEvent) and clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may or may not have + # triggered this failpoint as well. + # - name: assertEventCount + # object: testRunner + # arguments: + # event: ServerMarkedUnknownEvent + # count: 1 + # - name: assertEventCount + # object: testRunner + # arguments: + # event: PoolClearedEvent + # count: 1 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-timeout + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + + - description: Network timeout on Monitor check + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 750 + heartbeatFrequencyMS: 500 + appname: timeoutMonitorCheckTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # Configure the next streaming hello check to fail with a timeout. + # Use "times: 4" to increase the probability that the Monitor check times + # out since the RTT hello may trigger this failpoint one or many times as + # well. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + appName: timeoutMonitorCheckTest + blockConnection: true + # blockTimeMS is evaluated after the waiting for heartbeatFrequencyMS server-side, so this value only + # needs to be greater than connectTimeoutMS. The driver will wait for (500+750)ms and the server will + # respond after (500+1000)ms. + blockTimeMS: 1000 + client: *setupClient + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + # The network error on the next check should mark the server Unknown and + # clear the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + # We cannot assert the server was marked Unknown and pool was cleared an + # exact number of times because the RTT hello may have triggered this + # failpoint one or many times as well. + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-timeout + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: hello-timeout + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 + + - description: Driver extends timeout while streaming + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + connectTimeoutMS: 250 + heartbeatFrequencyMS: 500 + appname: extendsTimeoutTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 1 + - _id: 2 + # Wait for multiple monitor checks to complete. + - name: wait + object: testRunner + arguments: + ms: 2000 + # Perform an operation to ensure the node is still selectable. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + # Assert that the server was never marked Unknown and the pool was never + # cleared. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 0 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 0 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: hello-timeout + documents: + - _id: 1 + - _id: 2 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: hello-timeout + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/insert-network-error.json b/source/server-discovery-and-monitoring/tests/unified/insert-network-error.json new file mode 100644 index 0000000000..bfe41a4cb6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/insert-network-error.json @@ -0,0 +1,246 @@ +{ + "description": "insert-network-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "insert-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Reset server and pool after network error on insert", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true, + "appName": "insertNetworkErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandStartedEvent", + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "insertNetworkErrorTest" + }, + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "insert-network-error" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "insert-network-error", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "insert-network-error", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "insert-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/insert-network-error.yml b/source/server-discovery-and-monitoring/tests/unified/insert-network-error.yml new file mode 100644 index 0000000000..fcedf54357 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/insert-network-error.yml @@ -0,0 +1,137 @@ +--- +description: insert-network-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName insert-network-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Reset server and pool after network error on insert + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + closeConnection: true + appName: insertNetworkErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: insertNetworkErrorTest + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform another operation to ensure the node is rediscovered. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: insert-network-error + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: insert-network-error + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.json b/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.json new file mode 100644 index 0000000000..af7c6c987a --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.json @@ -0,0 +1,250 @@ +{ + "description": "insert-shutdown-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "insert-shutdown-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Concurrent shutdown error on insert", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 500, + "appname": "shutdownErrorInsertTest" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "insert-shutdown-error" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "appName": "shutdownErrorInsertTest", + "errorCode": 91, + "blockConnection": true, + "blockTimeMS": 500 + } + }, + "client": "setupClient" + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "thread": { + "id": "thread0" + } + }, + { + "thread": { + "id": "thread1" + } + } + ] + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread0", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + } + }, + "expectError": { + "isError": true + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 3 + } + }, + "expectError": { + "isError": true + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread0" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 4 + } + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "outcome": [ + { + "collectionName": "insert-shutdown-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.yml b/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.yml new file mode 100644 index 0000000000..ae32229ffa --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/insert-shutdown-error.yml @@ -0,0 +1,162 @@ +--- +description: insert-shutdown-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName insert-shutdown-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Concurrent shutdown error on insert + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 500 + appname: shutdownErrorInsertTest + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + # Configure the next two inserts to fail with a non-timeout shutdown + # errors. Block the connection for 500ms to ensure both operations check + # out connections from the same pool generation. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - insert + appName: shutdownErrorInsertTest + errorCode: 91 + blockConnection: true + blockTimeMS: 500 + client: *setupClient + # Start threads. + - name: createEntities + object: testRunner + arguments: + entities: + - thread: + id: &thread0 thread0 + - thread: + id: &thread1 thread1 + # Perform concurrent insert operations. Both fail with shutdown errors. + - name: runOnThread + object: testRunner + arguments: + thread: *thread0 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 2 + expectError: + isError: true + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 3 + expectError: + isError: true + # Stop threads. + - name: waitForThread + object: testRunner + arguments: + thread: *thread0 + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + # The first shutdown error should mark the server Unknown and then clear + # the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node is rediscovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 4 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + + # Order of operations is non-deterministic so we cannot check events. + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 4 diff --git a/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json b/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json new file mode 100644 index 0000000000..d9329646d4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json @@ -0,0 +1,591 @@ +{ + "description": "interruptInUse", + "schemaVersion": "1.11", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "interruptInUse", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Connection pool clear uses interruptInUseConnections=true after monitor timeout", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "poolClearedEvent", + "connectionClosedEvent", + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ], + "uriOptions": { + "connectTimeoutMS": 500, + "heartbeatFrequencyMS": 500, + "appname": "interruptInUse", + "retryReads": false, + "minPoolSize": 0 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "interruptInUse" + } + }, + { + "thread": { + "id": "thread1" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$where": "sleep(2000) || true" + } + }, + "expectError": { + "isError": true + } + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "blockConnection": true, + "blockTimeMS": 1500, + "appName": "interruptInUse" + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + }, + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": { + "interruptInUseConnections": true + } + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + } + ] + } + ], + "outcome": [ + { + "collectionName": "interruptInUse", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Error returned from connection pool clear with interruptInUseConnections=true is retryable", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "poolClearedEvent", + "connectionClosedEvent", + "commandStartedEvent", + "commandFailedEvent", + "commandSucceededEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ], + "uriOptions": { + "connectTimeoutMS": 500, + "heartbeatFrequencyMS": 500, + "appname": "interruptInUseRetryable", + "retryReads": true, + "minPoolSize": 0 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "interruptInUse" + } + }, + { + "thread": { + "id": "thread1" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "$where": "sleep(2000) || true" + } + } + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "blockConnection": true, + "blockTimeMS": 1500, + "appName": "interruptInUseRetryable" + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandSucceededEvent": { + "commandName": "find" + } + } + ] + }, + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": { + "interruptInUseConnections": true + } + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ], + "outcome": [ + { + "collectionName": "interruptInUse", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "Error returned from connection pool clear with interruptInUseConnections=true is retryable for write", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "poolClearedEvent", + "connectionClosedEvent", + "commandStartedEvent", + "commandFailedEvent", + "commandSucceededEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ], + "uriOptions": { + "connectTimeoutMS": 500, + "heartbeatFrequencyMS": 500, + "appname": "interruptInUseRetryableWrite", + "retryWrites": true, + "minPoolSize": 0 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "interruptInUse" + } + }, + { + "thread": { + "id": "thread1" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "updateOne", + "object": "collection", + "arguments": { + "filter": { + "$where": "sleep(2000) || true" + }, + "update": { + "$set": { + "a": "bar" + } + } + } + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "blockConnection": true, + "blockTimeMS": 1500, + "appName": "interruptInUseRetryableWrite" + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandSucceededEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandFailedEvent": { + "commandName": "update" + } + }, + { + "commandStartedEvent": { + "commandName": "update" + } + }, + { + "commandSucceededEvent": { + "commandName": "update" + } + } + ] + }, + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": { + "interruptInUseConnections": true + } + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ], + "outcome": [ + { + "collectionName": "interruptInUse", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1, + "a": "bar" + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.yml b/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.yml new file mode 100644 index 0000000000..67cd7d3ae3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.yml @@ -0,0 +1,341 @@ +--- +description: interruptInUse + +schemaVersion: "1.11" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName interruptInUse + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Connection pool clear uses interruptInUseConnections=true after monitor timeout + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - poolClearedEvent + - connectionClosedEvent + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + uriOptions: + connectTimeoutMS: 500 + heartbeatFrequencyMS: 500 + appname: interruptInUse + retryReads: false + minPoolSize: 0 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - thread: + id: &thread1 thread1 + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + # simulate a long-running query + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: find + object: *collection + arguments: + filter: + $where : sleep(2000) || true + expectError: + isError: true + # Configure the monitor check to fail with a timeout. + # Use "times: 4" to increase the probability that the Monitor check triggers + # the failpoint, since the RTT hello may trigger this failpoint one or many + # times as well. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + blockConnection: true + blockTimeMS: 1500 + appName: interruptInUse + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - client: *client + eventType: cmap + events: + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - poolClearedEvent: + interruptInUseConnections: true + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + + - description: Error returned from connection pool clear with interruptInUseConnections=true is retryable + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - poolClearedEvent + - connectionClosedEvent + - commandStartedEvent + - commandFailedEvent + - commandSucceededEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + uriOptions: + connectTimeoutMS: 500 + heartbeatFrequencyMS: 500 + appname: interruptInUseRetryable + retryReads: true + minPoolSize: 0 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - thread: + id: &thread1 thread1 + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + # simulate a long-running query + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: find + object: *collection + arguments: + filter: + $where : sleep(2000) || true + # Configure the monitor check to fail with a timeout. + # Use "times: 4" to increase the probability that the Monitor check triggers + # the failpoint, since the RTT hello may trigger this failpoint one or many + # times as well. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + blockConnection: true + blockTimeMS: 1500 + appName: interruptInUseRetryable + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: find + - commandFailedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandSucceededEvent: + commandName: find + - client: *client + eventType: cmap + events: + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - poolClearedEvent: + interruptInUseConnections: true + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - description: Error returned from connection pool clear with interruptInUseConnections=true is retryable for write + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - poolClearedEvent + - connectionClosedEvent + - commandStartedEvent + - commandFailedEvent + - commandSucceededEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + uriOptions: + connectTimeoutMS: 500 + heartbeatFrequencyMS: 500 + appname: interruptInUseRetryableWrite + retryWrites: true + minPoolSize: 0 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - thread: + id: &thread1 thread1 + # ensure the primary is discovered + - name: insertOne + object: *collection + arguments: + document: { _id: 1 } + # simulate a long-running query + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: updateOne + object: *collection + arguments: + filter: + $where: sleep(2000) || true + update: + "$set": { "a": "bar" } + # Configure the monitor check to fail with a timeout. + # Use "times: 4" to increase the probability that the Monitor check triggers + # the failpoint, since the RTT hello may trigger this failpoint one or many + # times as well. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 4 + data: + failCommands: + - hello + - isMaster + blockConnection: true + blockTimeMS: 1500 + appName: interruptInUseRetryableWrite + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + commandName: insert + - commandSucceededEvent: + commandName: insert + - commandStartedEvent: + commandName: update + - commandFailedEvent: + commandName: update + - commandStartedEvent: + commandName: update + - commandSucceededEvent: + commandName: update + - client: *client + eventType: cmap + events: + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - poolClearedEvent: + interruptInUseConnections: true + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, a : bar } + diff --git a/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.json b/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.json new file mode 100644 index 0000000000..30c0657630 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.json @@ -0,0 +1,88 @@ +{ + "description": "loadbalanced-emit-topology-description-changed-before-close", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "events": [ + { + "topologyOpeningEvent": {} + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": {} + } + }, + { + "topologyDescriptionChangedEvent": { + "newDescription": { + "type": "LoadBalanced" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.yml b/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.yml new file mode 100644 index 0000000000..c919a8cf8b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.yml @@ -0,0 +1,49 @@ +description: "loadbalanced-emit-topology-description-changed-before-close" + +schemaVersion: "1.20" + +runOnRequirements: + - topologies: + - load-balanced + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - topologyDescriptionChangedEvent + - topologyOpeningEvent + - topologyClosedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: close + object: *client + expectEvents: + - client: *client + eventType: sdam + events: + - topologyOpeningEvent: {} + - topologyDescriptionChangedEvent: # unknown -> unknown w disconnected server + previousDescription: + type: "Unknown" + newDescription: {} + - topologyDescriptionChangedEvent: # unknown w disconnected server -> loadBalanced + newDescription: + type: "LoadBalanced" + - topologyDescriptionChangedEvent: # loadbalanced -> unknown + newDescription: + type: "Unknown" + - topologyClosedEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.json b/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.json new file mode 100644 index 0000000000..0ad3b0ceaa --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.json @@ -0,0 +1,166 @@ +{ + "description": "loadbalanced-logging", + "schemaVersion": "1.16", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring", + "topologyId": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.yml b/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.yml new file mode 100644 index 0000000000..432ec354ed --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.yml @@ -0,0 +1,81 @@ +description: "loadbalanced-logging" + +schemaVersion: "1.16" + +runOnRequirements: + - topologies: + - load-balanced + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - topologyDescriptionChangedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: close + object: *client + expectLogMessages: + - client: *client + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # unknown topology + newDescription: { $$exists: true } # unknown topology, disconnected server + - level: debug + component: topology + data: + message: "Starting server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # loadBalanced topology + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # loadBalanced topology + newDescription: { $$exists: true } # unknown topology + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + topologyId: { $$exists: true } diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.json b/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.json new file mode 100644 index 0000000000..fe6ac60b68 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.json @@ -0,0 +1,610 @@ +{ + "description": "replicaset-logging", + "schemaVersion": "1.16", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ], + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient" + } + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 4 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring", + "topologyId": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "Successful heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatSucceededEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 3 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "serverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "serverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1 + } + } + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "serverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1 + } + } + } + } + } + ] + } + ] + }, + { + "description": "Failing heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatFailedEvent" + ], + "uriOptions": { + "appname": "failingHeartbeatLoggingTest" + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "failingHeartbeatLoggingTest", + "closeConnection": true + } + }, + "client": "setupClient" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatFailedEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.yml b/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.yml new file mode 100644 index 0000000000..0e7cc77067 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-replicaset.yml @@ -0,0 +1,289 @@ +description: "replicaset-logging" + +schemaVersion: "1.16" + +runOnRequirements: + - topologies: + - replicaset + minServerVersion: "4.4" # awaitable hello + +createEntities: + - client: + id: &setupClient setupClient + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - topologyDescriptionChangedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial cluster type change from unknown to ReplicaSet, and connect events for each of 3 servers. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 4 + - name: close + object: *client + expectLogMessages: + - client: *client + ignoreMessages: + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + - level: debug + component: topology + data: + message: "Server heartbeat failed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # unknown topology + newDescription: { $$exists: true } # ReplicaSet topology + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # server connected + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # server connected + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # server connected + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # ReplicaSet topology + newDescription: { $$exists: true } # unknown topology + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + topologyId: { $$exists: true } + - description: Successful heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: *client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatSucceededEvent + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatSucceededEvent: {} + count: 3 + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + serverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + reply: + $$matchAsDocument: + "$$matchAsRoot": + ok: 1 + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + serverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + reply: + $$matchAsDocument: + "$$matchAsRoot": + ok: 1 + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + serverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + reply: + $$matchAsDocument: + "$$matchAsRoot": + ok: 1 + - description: Failing heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: *client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatFailedEvent + uriOptions: + appname: failingHeartbeatLoggingTest + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: + - hello + - isMaster + appName: failingHeartbeatLoggingTest + closeConnection: true + client: *setupClient + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatFailedEvent: {} + count: 1 + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: + "$$exists": true + - level: debug + component: topology + data: + message: "Server heartbeat failed" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + failure: { $$exists: true } \ No newline at end of file diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-sharded.json b/source/server-discovery-and-monitoring/tests/unified/logging-sharded.json new file mode 100644 index 0000000000..3788708ab0 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-sharded.json @@ -0,0 +1,494 @@ +{ + "description": "sharded-logging", + "schemaVersion": "1.16", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ], + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ], + "useMultipleMongoses": true + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 3 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring", + "topologyId": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "Successful heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatSucceededEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "serverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1 + } + } + } + } + } + ] + } + ] + }, + { + "description": "Failing heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatFailedEvent" + ], + "uriOptions": { + "appname": "failingHeartbeatLoggingTest" + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "failingHeartbeatLoggingTest", + "closeConnection": true + } + }, + "client": "setupClient" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatFailedEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-sharded.yml b/source/server-discovery-and-monitoring/tests/unified/logging-sharded.yml new file mode 100644 index 0000000000..65309b6bb4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-sharded.yml @@ -0,0 +1,248 @@ +description: "sharded-logging" + +schemaVersion: "1.16" + +runOnRequirements: + - topologies: + - sharded + minServerVersion: "4.4" # awaitable hello + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - topologyDescriptionChangedEvent + useMultipleMongoses: true + # ensure the topology has been fully discovered before closing the client. + # expected events are initial cluster type change from unknown to sharded, and connect events for each of 2 servers. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 3 + - name: close + object: *client + expectLogMessages: + - client: *client + ignoreMessages: + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + - level: debug + component: topology + data: + message: "Server heartbeat failed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # unknown topology + newDescription: { $$exists: true } # Sharded topology + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # shard router connected + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # shard router connected + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # Sharded topology + newDescription: { $$exists: true } # unknown topology + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + topologyId: { $$exists: true } + - description: Successful heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: *client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatSucceededEvent + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + serverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + reply: + $$matchAsDocument: + "$$matchAsRoot": + ok: 1 + - description: Failing heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: *client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatFailedEvent + uriOptions: + appname: failingHeartbeatLoggingTest + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: + - hello + - isMaster + appName: failingHeartbeatLoggingTest + closeConnection: true + client: *setupClient + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatFailedEvent: {} + count: 1 + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: + "$$exists": true + - level: debug + component: topology + data: + message: "Server heartbeat failed" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + failure: { $$exists: true } \ No newline at end of file diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-standalone.json b/source/server-discovery-and-monitoring/tests/unified/logging-standalone.json new file mode 100644 index 0000000000..0682a1a4fb --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-standalone.json @@ -0,0 +1,519 @@ +{ + "description": "standalone-logging", + "schemaVersion": "1.16", + "runOnRequirements": [ + { + "topologies": [ + "single" + ], + "minServerVersion": "4.4" + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient" + } + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring", + "topologyId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring", + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed", + "topologyId": { + "$$exists": true + }, + "previousDescription": { + "$$exists": true + }, + "newDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring", + "topologyId": { + "$$exists": true + } + } + } + ] + } + ] + }, + { + "description": "Successful heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatSucceededEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "serverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "reply": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "ok": 1 + } + } + } + } + } + ] + } + ] + }, + { + "description": "Failing heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeLogMessages": { + "topology": "debug" + }, + "observeEvents": [ + "serverHeartbeatFailedEvent" + ], + "uriOptions": { + "appname": "failingHeartbeatLoggingTest" + } + } + } + ] + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "failingHeartbeatLoggingTest", + "closeConnection": true + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatFailedEvent": {} + }, + "count": 1 + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "ignoreExtraMessages": true, + "ignoreMessages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped topology monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Stopped server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Topology description changed" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting server monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Starting topology monitoring" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat started" + } + }, + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat succeeded" + } + } + ], + "messages": [ + { + "level": "debug", + "component": "topology", + "data": { + "message": "Server heartbeat failed", + "awaited": { + "$$exists": true + }, + "topologyId": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "driverConnectionId": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/logging-standalone.yml b/source/server-discovery-and-monitoring/tests/unified/logging-standalone.yml new file mode 100644 index 0000000000..b243abf6f6 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/logging-standalone.yml @@ -0,0 +1,258 @@ +description: "standalone-logging" + +schemaVersion: "1.16" + +runOnRequirements: + - topologies: + - single + minServerVersion: "4.4" # awaitable hello + +createEntities: + - client: + id: &setupClient setupClient + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - topologyDescriptionChangedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: close + object: *client + expectLogMessages: + - client: *client + ignoreMessages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + - level: debug + component: topology + data: + message: "Server heartbeat failed" + messages: + - level: debug + component: topology + data: + message: "Starting topology monitoring" + topologyId: { $$exists: true } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # unknown topology + newDescription: { $$exists: true } # unknown topology, disconnected server + - level: debug + component: topology + data: + message: "Starting server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } + newDescription: { $$exists: true } # standalone topology + - level: debug + component: topology + data: + message: "Stopped server monitoring" + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + - level: debug + component: topology + data: + message: "Topology description changed" + topologyId: { $$exists: true } + previousDescription: { $$exists: true } # standalone topology + newDescription: { $$exists: true } # unknown topology + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + topologyId: { $$exists: true } + - description: Successful heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatSucceededEvent + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: close + object: *client + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Topology description changed" + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Starting topology monitoring" + - level: debug + component: topology + data: + message: "Server heartbeat failed" + messages: + - level: debug + component: topology + data: + message: "Server heartbeat started" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + serverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + reply: + $$matchAsDocument: + "$$matchAsRoot": + ok: 1 + - description: Failing heartbeat + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeLogMessages: + topology: debug + observeEvents: + - serverHeartbeatFailedEvent + uriOptions: + appname: failingHeartbeatLoggingTest + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: "alwaysOn" + data: + failCommands: + - hello + - isMaster + appName: failingHeartbeatLoggingTest + closeConnection: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverHeartbeatFailedEvent: {} + count: 1 + expectLogMessages: + - client: *client + ignoreExtraMessages: true + ignoreMessages: + - level: debug + component: topology + data: + message: "Topology description changed" + - level: debug + component: topology + data: + message: "Stopped topology monitoring" + - level: debug + component: topology + data: + message: "Stopped server monitoring" + - level: debug + component: topology + data: + message: "Topology description changed" + - level: debug + component: topology + data: + message: "Starting server monitoring" + - level: debug + component: topology + data: + message: "Starting topology monitoring" + - level: debug + component: topology + data: + message: "Server heartbeat started" + - level: debug + component: topology + data: + message: "Server heartbeat succeeded" + messages: + - level: debug + component: topology + data: + message: "Server heartbeat failed" + awaited: { $$exists: true } + topologyId: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + driverConnectionId: { $$exists: true } + durationMS: { $$type: [double, int, long] } + failure: { $$exists: true } \ No newline at end of file diff --git a/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.json b/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.json new file mode 100644 index 0000000000..0ce2bdd6c1 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.json @@ -0,0 +1,178 @@ +{ + "description": "minPoolSize-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4.7", + "serverless": "forbid", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "sdam-minPoolSize-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Server error on minPoolSize background creation", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 3 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "SDAMminPoolSizeError", + "errorCode": 91 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent", + "poolReadyEvent" + ], + "uriOptions": { + "heartbeatFrequencyMS": 10000, + "appname": "SDAMminPoolSizeError", + "minPoolSize": 10, + "serverMonitoringMode": "poll", + "serverSelectionTimeoutMS": 1000 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "sdam-minPoolSize-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": {} + }, + "commandName": "ping" + }, + "expectError": { + "isError": true + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "off" + }, + "client": "setupClient" + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "command": { + "ping": 1 + }, + "commandName": "ping" + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 2 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.yml b/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.yml new file mode 100644 index 0000000000..1bbc0c376e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/minPoolSize-error.yml @@ -0,0 +1,126 @@ +--- +description: minPoolSize-error + +schemaVersion: "1.4" + +runOnRequirements: + # Require SERVER-49336 for failCommand + appName on the initial handshake. + - minServerVersion: "4.4.7" + serverless: forbid + topologies: + - single + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName sdam-minPoolSize-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: Server error on minPoolSize background creation + operations: + # Configure the initial monitor handshake to succeed but the + # first or second background minPoolSize establishments to fail. + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + skip: 3 + data: + failCommands: + - hello + - isMaster + appName: SDAMminPoolSizeError + errorCode: 91 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + - poolReadyEvent + uriOptions: + heartbeatFrequencyMS: 10000 + appname: SDAMminPoolSizeError + minPoolSize: 10 + serverMonitoringMode: poll + serverSelectionTimeoutMS: 1000 + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Wait for monitor to succeed handshake and mark the pool as ready. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + # Background connection establishment ensuring minPoolSize should fail, + # causing the pool to be cleared. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # The server should be marked as Unknown as part of this. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + # Executing a command should fail server selection due to not being able + # to find the primary. + - name: runCommand + object: *database + arguments: + command: + ping: {} + commandName: ping + expectError: + isError: true + # Disable the failpoint, allowing the monitor to discover the primary again. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: "off" + client: *setupClient + # Perform an operation to ensure the node is discovered. + - name: runCommand + object: *database + arguments: + command: + ping: 1 + commandName: ping + # Assert that the monitor discovered the primary and mark the pool as ready again. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 2 diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.json b/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.json new file mode 100644 index 0000000000..b8fd95fee3 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.json @@ -0,0 +1,149 @@ +{ + "description": "pool-clear-application-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "find-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Pool is cleared before application connection is checked into the pool", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true, + "appName": "findNetworkErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "poolClearedEvent", + "connectionCheckedInEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appname": "findNetworkErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "find-network-error" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionCheckedInEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.yml b/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.yml new file mode 100644 index 0000000000..43e425711e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-application-error.yml @@ -0,0 +1,88 @@ +--- +description: pool-clear-application-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName find-network-error + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Pool is cleared before application connection is checked into the pool + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + closeConnection: true + appName: findNetworkErrorTest + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - poolClearedEvent + - connectionCheckedInEvent + uriOptions: + retryWrites: false + retryReads: false + appname: findNetworkErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + - name: find + object: *collection + arguments: + filter: + _id: 1 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionCheckedInEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: cmap + events: + - poolClearedEvent: {} + - connectionCheckedInEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.json b/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.json new file mode 100644 index 0000000000..7e6c7c8df4 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.json @@ -0,0 +1,148 @@ +{ + "description": "pool-clear-on-error-checkout", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "tests": [ + { + "description": "Pool is cleared before connection is closed (authentication error)", + "runOnRequirements": [ + { + "auth": true + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "authErrorTest", + "errorCode": 18 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "poolClearedEvent", + "connectionClosedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "authErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "bar" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.yml b/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.yml new file mode 100644 index 0000000000..5f7c48b521 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.yml @@ -0,0 +1,86 @@ +--- +description: pool-clear-on-error-checkout + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single, replicaset, sharded ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +tests: + - description: Pool is cleared before connection is closed (authentication error) + runOnRequirements: + - auth: true + + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + appName: authErrorTest + errorCode: 18 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - connectionCheckOutStartedEvent + - poolClearedEvent + - connectionClosedEvent + uriOptions: + retryWrites: false + appname: authErrorTest + - database: + id: &database database + client: *client + databaseName: foo + - collection: + id: &collection collection + database: *database + collectionName: bar + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionClosedEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: cmap + events: + - connectionCheckOutStartedEvent: {} + - poolClearedEvent: {} + - connectionClosedEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json b/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json new file mode 100644 index 0000000000..a0139bd033 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json @@ -0,0 +1,192 @@ +{ + "description": "pool-cleared-on-min-pool-size-population-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "tests": [ + { + "description": "Pool is cleared on authentication error during minPoolSize population", + "runOnRequirements": [ + { + "auth": true + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "authErrorTest", + "errorCode": 18 + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent", + "poolClearedEvent", + "connectionClosedEvent" + ], + "uriOptions": { + "appname": "authErrorTest", + "minPoolSize": 1 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": {} + }, + "count": 1 + } + } + ] + }, + { + "description": "Pool is not cleared on handshake error during minPoolSize population", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "authErrorTest", + "closeConnection": true + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolReadyEvent", + "poolClearedEvent", + "connectionClosedEvent" + ], + "uriOptions": { + "appname": "authErrorTest", + "minPoolSize": 5, + "maxConnecting": 1, + "serverMonitoringMode": "poll", + "heartbeatFrequencyMS": 1000000 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": {} + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 0 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.yml b/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.yml new file mode 100644 index 0000000000..2c8e32a410 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.yml @@ -0,0 +1,132 @@ +--- +description: pool-cleared-on-min-pool-size-population-error + +schemaVersion: "1.4" + +runOnRequirements: + # failCommand appName requirements + - minServerVersion: "4.4" + serverless: forbid + topologies: [ single ] + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +tests: + - description: Pool is cleared on authentication error during minPoolSize population + runOnRequirements: + # failCommand appName requirements + - auth: true + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + appName: authErrorTest + errorCode: 18 + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - poolReadyEvent + - poolClearedEvent + - connectionClosedEvent + uriOptions: + appname: authErrorTest + minPoolSize: 1 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionClosedEvent: {} + count: 1 + + - description: Pool is not cleared on handshake error during minPoolSize population + operations: + - name: failPoint + object: testRunner + arguments: + client: *setupClient + failPoint: + configureFailPoint: failCommand + mode: + skip: 1 # skip one to let monitoring thread to move pool to ready state + data: + failCommands: + - hello + - isMaster + appName: authErrorTest + closeConnection: true + + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - poolReadyEvent + - poolClearedEvent + - connectionClosedEvent + uriOptions: + appname: authErrorTest + minPoolSize: 5 + maxConnecting: 1 + # ensure that once we've connected to the server, the failCommand won't + # be triggered by monitors and will only be triggered by handshakes + serverMonitoringMode: poll + heartbeatFrequencyMS: 1000000 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolReadyEvent: {} + count: 1 + + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + connectionClosedEvent: {} + count: 1 + + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 0 diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.json b/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.json new file mode 100644 index 0000000000..b7f6924f2b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.json @@ -0,0 +1,373 @@ +{ + "description": "pool-cleared-error", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.9", + "serverless": "forbid", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "pool-cleared-error", + "databaseName": "sdam-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "PoolClearedError does not mark server unknown", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverDescriptionChangedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": true, + "maxPoolSize": 1, + "appname": "poolClearedErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "pool-cleared-error" + } + } + ] + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "blockConnection": true, + "blockTimeMS": 100, + "closeConnection": true, + "appName": "poolClearedErrorTest" + } + }, + "client": "setupClient" + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "thread": { + "id": "thread0" + } + }, + { + "thread": { + "id": "thread1" + } + }, + { + "thread": { + "id": "thread2" + } + }, + { + "thread": { + "id": "thread3" + } + }, + { + "thread": { + "id": "thread4" + } + }, + { + "thread": { + "id": "thread5" + } + } + ] + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread0", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 2 + } + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread1", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 3 + } + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread2", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 4 + } + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread3", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 5 + } + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread4", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 6 + } + } + } + } + }, + { + "name": "runOnThread", + "object": "testRunner", + "arguments": { + "thread": "thread5", + "operation": { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 7 + } + } + } + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread0" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread1" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread2" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread3" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread4" + } + }, + { + "name": "waitForThread", + "object": "testRunner", + "arguments": { + "thread": "thread5" + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 8 + } + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ], + "outcome": [ + { + "collectionName": "pool-cleared-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + }, + { + "_id": 6 + }, + { + "_id": 7 + }, + { + "_id": 8 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.yml b/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.yml new file mode 100644 index 0000000000..f3bad7959e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/pool-cleared-error.yml @@ -0,0 +1,239 @@ +--- +description: pool-cleared-error + +schemaVersion: "1.4" + +runOnRequirements: + # This test requires retryable writes, failCommand appName, and + # failCommand blockConnection with closeConnection:true (SERVER-53512). + - minServerVersion: "4.9" + serverless: forbid + topologies: + - replicaset + - sharded + +createEntities: + - client: + id: &setupClient setupClient + useMultipleMongoses: false + +initialData: &initialData + - collectionName: &collectionName pool-cleared-error + databaseName: &databaseName sdam-tests + documents: [] + +tests: + - description: PoolClearedError does not mark server unknown + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: + - serverDescriptionChangedEvent + - poolClearedEvent + uriOptions: + retryWrites: true + maxPoolSize: 1 + appname: poolClearedErrorTest + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Perform an operation to ensure the node is discovered. + - name: insertOne + object: *collection + arguments: + document: + _id: 1 + # Configure the next insert to fail with a network error which will + # clear the pool leaving it paused until the server is rediscovered. + - name: failPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + blockConnection: true + blockTimeMS: 100 + closeConnection: true + appName: poolClearedErrorTest + client: *setupClient + # Start threads. + - name: createEntities + object: testRunner + arguments: + entities: + - thread: + id: &thread0 thread0 + - thread: + id: &thread1 thread1 + - thread: + id: &thread2 thread2 + - thread: + id: &thread3 thread3 + - thread: + id: &thread4 thread4 + - thread: + id: &thread5 thread5 + # Perform concurrent insert operations. The first one to execute will + # fail with a network error, mark the server Unknown, clear the pool, + # and retry. + # The other operations will either: + # - Notice the pool is paused, fail with a PoolClearedError, and retry. + # - Or block waiting in server selection until the server is + # rediscovered. + # + # Note that this test does not guarantee that a PoolClearedError will be + # raised but it is likely since the initial insert is delayed. + - name: runOnThread + object: testRunner + arguments: + thread: *thread0 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 2 + - name: runOnThread + object: testRunner + arguments: + thread: *thread1 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 3 + - name: runOnThread + object: testRunner + arguments: + thread: *thread2 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 4 + - name: runOnThread + object: testRunner + arguments: + thread: *thread3 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 5 + - name: runOnThread + object: testRunner + arguments: + thread: *thread4 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 6 + - name: runOnThread + object: testRunner + arguments: + thread: *thread5 + operation: + name: insertOne + object: *collection + arguments: + document: + _id: 7 + # Stop threads. + - name: waitForThread + object: testRunner + arguments: + thread: *thread0 + - name: waitForThread + object: testRunner + arguments: + thread: *thread1 + - name: waitForThread + object: testRunner + arguments: + thread: *thread2 + - name: waitForThread + object: testRunner + arguments: + thread: *thread3 + - name: waitForThread + object: testRunner + arguments: + thread: *thread4 + - name: waitForThread + object: testRunner + arguments: + thread: *thread5 + # The first shutdown error should mark the server Unknown and then clear + # the pool. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + # Perform an operation to ensure the node still usable. + - name: insertOne + object: *collection + arguments: + document: + _id: 8 + # Assert the server was marked Unknown and pool was cleared exactly once. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 1 + + # Order of operations is non-deterministic so we cannot check events. + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 + - _id: 5 + - _id: 6 + - _id: 7 + - _id: 8 diff --git a/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.json b/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.json new file mode 100644 index 0000000000..3147a07a1e --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.json @@ -0,0 +1,242 @@ +{ + "description": "rediscover-quickly-after-step-down", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient" + } + }, + { + "database": { + "id": "adminDatabase", + "client": "setupClient", + "databaseName": "admin" + } + } + ], + "initialData": [ + { + "collectionName": "test-replSetStepDown", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Rediscover quickly after replSetStepDown", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "poolClearedEvent", + "commandStartedEvent" + ], + "uriOptions": { + "appname": "replSetStepDownTest", + "heartbeatFrequencyMS": 60000, + "serverSelectionTimeoutMS": 5000, + "w": "majority" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "test-replSetStepDown" + } + } + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + }, + { + "name": "recordTopologyDescription", + "object": "testRunner", + "arguments": { + "client": "client", + "id": "topologyDescription" + } + }, + { + "name": "assertTopologyType", + "object": "testRunner", + "arguments": { + "topologyDescription": "topologyDescription", + "topologyType": "ReplicaSetWithPrimary" + } + }, + { + "name": "runCommand", + "object": "adminDatabase", + "arguments": { + "command": { + "replSetFreeze": 0 + }, + "readPreference": { + "mode": "secondary" + }, + "commandName": "replSetFreeze" + } + }, + { + "name": "runCommand", + "object": "adminDatabase", + "arguments": { + "command": { + "replSetStepDown": 30, + "secondaryCatchUpPeriodSecs": 30, + "force": false + }, + "commandName": "replSetStepDown" + } + }, + { + "name": "waitForPrimaryChange", + "object": "testRunner", + "arguments": { + "client": "client", + "priorTopologyDescription": "topologyDescription", + "timeoutMS": 15000 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 0 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test-replSetStepDown", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test-replSetStepDown", + "documents": [ + { + "_id": 5 + }, + { + "_id": 6 + } + ] + }, + "commandName": "insert", + "databaseName": "sdam-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test-replSetStepDown", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + }, + { + "_id": 6 + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.yml b/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.yml new file mode 100644 index 0000000000..f3e7509160 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.yml @@ -0,0 +1,144 @@ +--- +description: rediscover-quickly-after-step-down + +schemaVersion: "1.4" + +runOnRequirements: + # 4.4 is required for streaming. + # A replica set is required for replSetStepDown. + - minServerVersion: "4.4" + serverless: forbid + topologies: + - replicaset + +createEntities: + - client: + id: &setupClient setupClient + - database: + id: &adminDatabase adminDatabase + client: *setupClient + databaseName: admin + +initialData: &initialData + - collectionName: &collectionName test-replSetStepDown + databaseName: &databaseName sdam-tests + documents: + - _id: 1 + - _id: 2 + +tests: + - description: Rediscover quickly after replSetStepDown + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - poolClearedEvent + - commandStartedEvent + uriOptions: + appname: replSetStepDownTest + # Configure a large heartbeatFrequencyMS + heartbeatFrequencyMS: 60000 + # Configure a much smaller server selection timeout so that the test + # will error when it cannot discover the new primary soon. + serverSelectionTimeoutMS: 5000 + w: majority + - database: + id: &database database + client: *client + databaseName: *databaseName + - collection: + id: &collection collection + database: *database + collectionName: *collectionName + # Discover the primary. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 3 + - _id: 4 + - name: recordTopologyDescription + object: testRunner + arguments: + client: *client + id: &topologyDescription topologyDescription + - name: assertTopologyType + object: testRunner + arguments: + topologyDescription: *topologyDescription + topologyType: ReplicaSetWithPrimary + # Unfreeze a secondary with replSetFreeze:0 to ensure a speedy election. + - name: runCommand + object: *adminDatabase + arguments: + command: + replSetFreeze: 0 + readPreference: + mode: secondary + commandName: replSetFreeze + # Run replSetStepDown on the meta client. + - name: runCommand + object: *adminDatabase + arguments: + command: + replSetStepDown: 30 + secondaryCatchUpPeriodSecs: 30 + force: false + commandName: replSetStepDown + - name: waitForPrimaryChange + object: testRunner + arguments: + client: *client + priorTopologyDescription: *topologyDescription + # We use a relatively large timeout here to workaround slow + # elections on Windows, possibly caused by SERVER-48154. + timeoutMS: 15000 + # Rediscover the new primary. + - name: insertMany + object: *collection + arguments: + documents: + - _id: 5 + - _id: 6 + # Assert that no pools were cleared. + - name: assertEventCount + object: testRunner + arguments: + client: *client + event: + poolClearedEvent: {} + count: 0 + expectEvents: + - client: *client + eventType: command + events: + - commandStartedEvent: + command: + insert: test-replSetStepDown + documents: + - _id: 3 + - _id: 4 + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: test-replSetStepDown + documents: + - _id: 5 + - _id: 6 + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - _id: 4 + - _id: 5 + - _id: 6 diff --git a/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.json b/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.json new file mode 100644 index 0000000000..066a4ffee5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.json @@ -0,0 +1,89 @@ +{ + "description": "replicaset-emit-topology-description-changed-before-close", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 4 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": false, + "events": [ + { + "topologyOpeningEvent": {} + }, + { + "topologyDescriptionChangedEvent": {} + }, + { + "topologyDescriptionChangedEvent": {} + }, + { + "topologyDescriptionChangedEvent": {} + }, + { + "topologyDescriptionChangedEvent": {} + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "ReplicaSetWithPrimary" + }, + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.yml b/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.yml new file mode 100644 index 0000000000..d0a1158ea9 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.yml @@ -0,0 +1,49 @@ +description: "replicaset-emit-topology-description-changed-before-close" + +schemaVersion: "1.20" + +runOnRequirements: + - topologies: + - replicaset + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - topologyDescriptionChangedEvent + - topologyOpeningEvent + - topologyClosedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial server discovery and 3 server connect events. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 4 + - name: close + object: *client + expectEvents: + - client: *client + eventType: sdam + ignoreExtraEvents: false + events: + - topologyOpeningEvent: {} + - topologyDescriptionChangedEvent: {} # unknown -> replset no primary + - topologyDescriptionChangedEvent: {} # server connected + - topologyDescriptionChangedEvent: {} # server connected + - topologyDescriptionChangedEvent: {} # server connected + - topologyDescriptionChangedEvent: # replicaset -> unknown + previousDescription: + type: "ReplicaSetWithPrimary" + newDescription: + type: "Unknown" + - topologyClosedEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.json b/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.json new file mode 100644 index 0000000000..4b492f7d85 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.json @@ -0,0 +1,512 @@ +{ + "description": "serverMonitoringMode", + "schemaVersion": "1.17", + "runOnRequirements": [ + { + "topologies": [ + "single", + "sharded", + "sharded-replicaset" + ], + "serverless": "forbid" + } + ], + "tests": [ + { + "description": "connect with serverMonitoringMode=auto >=4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "auto" + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent", + "serverHeartbeatFailedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + }, + { + "serverHeartbeatSucceededEvent": { + "awaited": false + } + }, + { + "serverHeartbeatStartedEvent": { + "awaited": true + } + } + ] + } + ] + }, + { + "description": "connect with serverMonitoringMode=auto <4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "auto", + "heartbeatFrequencyMS": 500 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent", + "serverHeartbeatFailedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + }, + { + "serverHeartbeatSucceededEvent": { + "awaited": false + } + }, + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + } + ] + } + ] + }, + { + "description": "connect with serverMonitoringMode=stream >=4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "stream" + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent", + "serverHeartbeatFailedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + }, + { + "serverHeartbeatSucceededEvent": { + "awaited": false + } + }, + { + "serverHeartbeatStartedEvent": { + "awaited": true + } + } + ] + } + ] + }, + { + "description": "connect with serverMonitoringMode=stream <4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "stream", + "heartbeatFrequencyMS": 500 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent", + "serverHeartbeatFailedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + }, + { + "serverHeartbeatSucceededEvent": { + "awaited": false + } + }, + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + } + ] + } + ] + }, + { + "description": "connect with serverMonitoringMode=poll", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "poll", + "heartbeatFrequencyMS": 500 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent", + "serverHeartbeatFailedEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "runCommand", + "object": "db", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 2 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + }, + { + "serverHeartbeatSucceededEvent": { + "awaited": false + } + }, + { + "serverHeartbeatStartedEvent": { + "awaited": false + } + } + ] + } + ] + }, + { + "description": "poll waits after successful heartbeat", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "serverMonitoringMode": "poll", + "heartbeatFrequencyMS": 1000000 + }, + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatStartedEvent", + "serverHeartbeatSucceededEvent" + ] + } + }, + { + "database": { + "id": "db", + "client": "client", + "databaseName": "sdam-tests" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "wait", + "object": "testRunner", + "arguments": { + "ms": 500 + } + }, + { + "name": "assertEventCount", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatStartedEvent": {} + }, + "count": 1 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.yml b/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.yml new file mode 100644 index 0000000000..5a81f0e1ad --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/serverMonitoringMode.yml @@ -0,0 +1,213 @@ +description: serverMonitoringMode + +schemaVersion: "1.17" +# These tests cannot run on replica sets because the order of the expected +# SDAM events are non-deterministic when monitoring multiple servers. +# They also cannot run on Serverless or load balanced clusters where SDAM is disabled. +runOnRequirements: + - topologies: [single, sharded, sharded-replicaset] + serverless: forbid +tests: + - description: "connect with serverMonitoringMode=auto >=4.4" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "auto" + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - serverHeartbeatFailedEvent + - database: + id: db + client: client + databaseName: sdam-tests + - &ping + name: runCommand + object: db + arguments: + commandName: ping + command: { ping: 1 } + expectResult: { ok: 1 } + # Wait for the second serverHeartbeatStartedEvent to ensure we start streaming. + - &waitForSecondHeartbeatStarted + name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatStartedEvent: {} + count: 2 + expectEvents: &streamingStartedEvents + - client: client + eventType: sdam + ignoreExtraEvents: true + events: + - serverHeartbeatStartedEvent: + awaited: False + - serverHeartbeatSucceededEvent: + awaited: False + - serverHeartbeatStartedEvent: + awaited: True + + - description: "connect with serverMonitoringMode=auto <4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "auto" + heartbeatFrequencyMS: 500 + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - serverHeartbeatFailedEvent + - database: + id: db + client: client + databaseName: sdam-tests + - *ping + # Wait for the second serverHeartbeatStartedEvent to ensure we do not stream. + - *waitForSecondHeartbeatStarted + expectEvents: &pollingStartedEvents + - client: client + eventType: sdam + ignoreExtraEvents: true + events: + - serverHeartbeatStartedEvent: + awaited: False + - serverHeartbeatSucceededEvent: + awaited: False + - serverHeartbeatStartedEvent: + awaited: False + + - description: "connect with serverMonitoringMode=stream >=4.4" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "stream" + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - serverHeartbeatFailedEvent + - database: + id: db + client: client + databaseName: sdam-tests + - *ping + # Wait for the second serverHeartbeatStartedEvent to ensure we start streaming. + - *waitForSecondHeartbeatStarted + expectEvents: *streamingStartedEvents + + - description: "connect with serverMonitoringMode=stream <4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "stream" + heartbeatFrequencyMS: 500 + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - serverHeartbeatFailedEvent + - database: + id: db + client: client + databaseName: sdam-tests + - *ping + # Wait for the second serverHeartbeatStartedEvent to ensure we do not stream. + - *waitForSecondHeartbeatStarted + expectEvents: *pollingStartedEvents + + - description: "connect with serverMonitoringMode=poll" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "poll" + heartbeatFrequencyMS: 500 + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - serverHeartbeatFailedEvent + - database: + id: db + client: client + databaseName: sdam-tests + - *ping + # Wait for the second serverHeartbeatStartedEvent to ensure we do not stream. + - *waitForSecondHeartbeatStarted + expectEvents: *pollingStartedEvents + + - description: "poll waits after successful heartbeat" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + serverMonitoringMode: "poll" + heartbeatFrequencyMS: 1000000 + useMultipleMongoses: false + observeEvents: + - serverHeartbeatStartedEvent + - serverHeartbeatSucceededEvent + - database: + id: db + client: client + databaseName: sdam-tests + # Wait for the first serverHeartbeatSucceededEvent to ensure we start polling. + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + # Wait for a bit longer to ensure we wait heartbeatFrequencyMS before starting the next check. + - name: wait + object: testRunner + arguments: + ms: 500 + - name: assertEventCount + object: testRunner + arguments: + client: client + event: + serverHeartbeatStartedEvent: {} + count: 1 diff --git a/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.json b/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.json new file mode 100644 index 0000000000..98fb585531 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.json @@ -0,0 +1,108 @@ +{ + "description": "sharded-emit-topology-description-changed-before-close", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ], + "useMultipleMongoses": true + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 3 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": false, + "events": [ + { + "topologyOpeningEvent": {} + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Sharded" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Sharded" + }, + "newDescription": { + "type": "Sharded" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Sharded" + }, + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.yml b/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.yml new file mode 100644 index 0000000000..cb6cc5ad4d --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.yml @@ -0,0 +1,62 @@ +description: "sharded-emit-topology-description-changed-before-close" + +schemaVersion: "1.20" + +runOnRequirements: + - topologies: + - sharded + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - topologyDescriptionChangedEvent + - topologyOpeningEvent + - topologyClosedEvent + useMultipleMongoses: true + # ensure the topology has been fully discovered before closing the client. + # expected events are initial cluster type change from unknown to sharded and connect events + # for each of 2 servers + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 3 + - name: close + object: *client + expectEvents: + - client: *client + eventType: sdam + ignoreExtraEvents: false + events: + - topologyOpeningEvent: {} + - topologyDescriptionChangedEvent: # unknown -> unknown w disconnected server + previousDescription: + type: "Unknown" + newDescription: + type: "Unknown" + - topologyDescriptionChangedEvent: # server connected + previousDescription: + type: "Unknown" + newDescription: + type: "Sharded" + - topologyDescriptionChangedEvent: # server connected + previousDescription: + type: "Sharded" + newDescription: + type: "Sharded" + - topologyDescriptionChangedEvent: # sharded -> unknown + previousDescription: + type: "Sharded" + newDescription: + type: "Unknown" + - topologyClosedEvent: {} diff --git a/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.json b/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.json new file mode 100644 index 0000000000..27b5444d54 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.json @@ -0,0 +1,97 @@ +{ + "description": "standalone-emit-topology-description-changed-before-close", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "single" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "Topology lifecycle", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "close", + "object": "client" + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": false, + "events": [ + { + "topologyOpeningEvent": {} + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Single" + } + } + }, + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Single" + }, + "newDescription": { + "type": "Unknown" + } + } + }, + { + "topologyClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.yml b/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.yml new file mode 100644 index 0000000000..20fa380bd5 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.yml @@ -0,0 +1,55 @@ +description: "standalone-emit-topology-description-changed-before-close" + +schemaVersion: "1.20" + +runOnRequirements: + - topologies: + - single + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "Topology lifecycle" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + observeEvents: + - topologyDescriptionChangedEvent + - topologyOpeningEvent + - topologyClosedEvent + # ensure the topology has been fully discovered before closing the client. + # expected events are initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: close + object: *client + expectEvents: + - client: *client + eventType: sdam + ignoreExtraEvents: false + events: + - topologyOpeningEvent: {} + - topologyDescriptionChangedEvent: # unknown -> unknown w disconnected server + previousDescription: + type: "Unknown" + newDescription: + type: "Unknown" + - topologyDescriptionChangedEvent: # unknown w disconnected server -> standalone + previousDescription: + type: "Unknown" + newDescription: + type: "Single" + - topologyDescriptionChangedEvent: # standalone -> unknown + previousDescription: + type: "Single" + newDescription: + type: "Unknown" + - topologyClosedEvent: {} diff --git a/source/server-selection/server-selection-tests.md b/source/server-selection/server-selection-tests.md new file mode 100644 index 0000000000..eb055242a9 --- /dev/null +++ b/source/server-selection/server-selection-tests.md @@ -0,0 +1,254 @@ +# Server Selection Test Plan + +- Status: Accepted + +- Minimum Server Version: 2.4 + +See also the YAML test files and their accompanying README in the "tests" directory. + +______________________________________________________________________ + +## ReadPreference Document Validation + +While there are no YAML tests for this section, clients implementing this spec SHOULD perform validations on +ReadPreference documents provided by the user. Specifically, documents with the following values should raise an error: + +- Mode PRIMARY and non-empty tag set + +## Calculating Round Trip Time + +Drivers implementing server selection MUST test that RTT values are calculated correctly. YAML tests for RTT +calculations can be found in the "tests" directory and they test for correctness in the following scenarios: + +- first RTT: new average RTT equals measurement +- subsequent measurements: new average RTT is calculated using the new measurement and the previous average as described + in the spec. + +Additionally, drivers SHOULD ensure that their implementations reject negative RTT values. + +Lastly, drivers SHOULD ensure that average RTT for a given ServerDescription is reset to 0 if that server is +disconnected (ie a network error occurs during a `hello` or legacy hello call). Upon reconnect, the first new RTT value +should become the average RTT for this server. + +The RTT tests are intentionally simplified to test the implementation of the EWMA algorithm without imposing any +additional conditions on drivers that might affect architecture. For some drivers, RTT tests might require mocks; for +others, it might just require unit tests. + +## Server Selection + +The following test cases can be found in YAML form in the "tests" directory. Each test lists a TopologyDescription +representing a set of servers, a ReadPreference document, and sets of servers returned at various stages of the server +selection process. These sets are described below. Note that it is not required to test for correctness at every step. + +| Test Case | Description | +| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `suitable_servers` | the set of servers matching all server selection logic. | +| `in_latency_window` | the subset of `suitable_servers` that falls within the allowable latency window (required). NOTE: tests use the default localThresholdMS of 15 ms. | +| `deprioritized_servers` | the set of servers that are deprioritized and must only be selected if no other suitable server exists. | + +Drivers implementing server selection MUST test that their implementations correctly return **one** of the servers in +`in_latency_window`. Drivers SHOULD test against the full set of servers in `in_latency_window` and against +`suitable_servers` if possible. + +For tests containing `deprioritized_servers`, drivers MUST pass the given list of deprioritized servers to each server +selection call. + +### Topology Type Single + +- The single server is always selected. + +### Topology Type ReplicaSetNoPrimary + +**Reads** + +- PRIMARY + - no server selected +- PRIMARY_PREFERRED + - Matching tags: select any eligible secondary + - Non-matching tags: no server selected +- SECONDARY + - Matching tags: select any eligible secondary + - Non-matching tags: no server selected +- SECONDARY_PREFERRED + - Matching tags: select any eligible secondary + - Non-matching tags: no server selected +- NEAREST + - Matching tags: select any eligible secondary + - Non-matching tags: no server selected + +**Writes** + +- Writes must go to a primary, no server can be selected. + +### Topology Type ReplicaSetWithPrimary + +**Reads** + +- PRIMARY + - primary is selected **NOTE:** it is an error to provide tags with mode PRIMARY. See "ReadPreference Document + Validation." +- PRIMARY_PREFERRED + - Matching tags: primary is selected + - Non-matching tags: primary is selected +- SECONDARY + - Matching tags: select any eligible secondary + - Non-matching tags: no server selected +- SECONDARY_PREFERRED + - Matching tags: select any eligible secondary + - Non-matching tags: primary is selected +- NEAREST + - Matching tags: select any eligible server + - Non-matching tags: no server selected + +**Writes** + +- Primary is selected. + +### Topology Type Sharded + +**Reads** + +- Select any mongos. + +**Writes** + +- Select any mongos. + +### Topology Type Unknown + +**Reads** + +- No server is selected. + +**Writes** + +- No server is selected. + +## Passing ReadPreference to Mongos + +While there are no YAML tests for this, drivers are strongly encouraged to test in a way specific to their +implementation that ReadPreference is correctly passed to Mongos in the following scenarios: + +- PRIMARY + - the SecondaryOk wire protocol flag is NOT set + - $readPreference is NOT used +- PRIMARY_PREFERRED + - the SecondaryOk wire protocol flag is set + - $readPreference is used +- SECONDARY + - the SecondaryOk wire protocol flag is set + - $readPreference is used +- SECONDARY_PREFERRED + - the SecondaryOk wire protocol flag is set + - if `tag_sets` or `hedge` are specified $readPreference is used, otherwise $readPreference is NOT used +- NEAREST + - the SecondaryOk wire protocol flag is set + - $readPreference is used + +## Random Selection Within Latency Window (single-threaded drivers) + +The Server Selection spec mandates that single-threaded drivers select a server at random from the set of suitable +servers that are within the latency window. Drivers implementing the spec SHOULD test their implementations in a +language-specific way to confirm randomness. + +For example, the following topology description, operation, and read preference will return a set of three suitable +servers within the latency window: + +```yaml + topology_description: + type: ReplicaSetWithPrimary + servers: + - &secondary_1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: {} + - &secondary_2 + address: c:27017 + avg_rtt_ms: 10 + type: RSSecondary + tags: {} + - &primary + address: a:27017 + avg_rtt_ms: 6 + type: RSPrimary + tags: {} + operation: read + read_preference: + mode: Nearest + tags: {} + in_latency_window: + - *primary + - *secondary_1 + - *secondary_2 +``` + +Drivers SHOULD check that their implementation selects one of `primary`, `secondary_1`, and `secondary_2` at random. + +## operationCount-based Selection Within Latency Window (multi-threaded or async drivers) + +The Server Selection spec mandates that multi-threaded or async drivers select a server from within the latency window +according to their operationCounts. There are YAML tests verifying that drivers implement this selection correctly which +can be found in the `tests/in_window` directory. Multi-threaded or async drivers implementing the spec MUST use them to +test their implementations. + +The YAML tests each include some information about the servers within the late ncy window. For each case, the driver +passes this information into whatever function it uses to select from within the window. Because the selection algorithm +relies on randomness, this process MUST be repeated 2000 times. Once the 2000 selections are complete, the runner +tallies up the number of times each server was selected and compares those counts to the expected results included in +the test case. Specifics of the test format and how to run the tests are included in the tests README. + +### Prose Test + +Multi-threaded and async drivers MUST also implement the following prose test: + +1. Configure a sharded cluster with two mongoses. Use a 4.2.9 or newer server version. + +2. Enable the following failpoint against exactly one of the mongoses: + + ```javascript + { + configureFailPoint: "failCommand", + mode: { times: 10000 }, + data: { + failCommands: ["find"], + blockConnection: true, + blockTimeMS: 500, + appName: "loadBalancingTest", + }, + } + ``` + +3. Create a client with both mongoses' addresses in its seed list, appName="loadBalancingTest", and + localThresholdMS=30000. + + - localThresholdMS is set to a high value to help avoid a single mongos being selected too many times due to a random + spike in latency in the other mongos. + +4. Using CMAP events, ensure the client's connection pools for both mongoses have been saturated, either via setting + minPoolSize=maxPoolSize or executing operations. + + - This helps reduce any noise introduced by connection establishment latency during the actual server selection + tests. + +5. Start 10 concurrent threads / tasks that each run 10 `findOne` operations with empty filters using that client. + +6. Using command monitoring events, assert that fewer than 25% of the CommandStartedEvents occurred on the mongos that + the failpoint was enabled on. + +7. Disable the failpoint. + +8. Start 10 concurrent threads / tasks that each run 100 `findOne` operations with empty filters using that client. + +9. Using command monitoring events, assert that each mongos was selected roughly 50% of the time (within +/- 10%). + +## Application-Provided Server Selector + +The Server Selection spec allows drivers to configure registration of a server selector function that filters the list +of suitable servers. Drivers implementing this part of the spec MUST test that: + +- The application-provided server selector is executed as part of the server selection process when there is a nonzero + number of candidate or eligible servers. For example, execute a test against a replica set: Register a server + selector that selects the suitable server with the highest port number. Execute 10 queries with nearest read + preference and, using command monitoring, assert that all the operations execute on the member with the highest port + number. diff --git a/source/server-selection/server-selection-tests.rst b/source/server-selection/server-selection-tests.rst deleted file mode 100644 index 0d009155a0..0000000000 --- a/source/server-selection/server-selection-tests.rst +++ /dev/null @@ -1,277 +0,0 @@ -============================= -Server Selection -- Test Plan -============================= - -:Spec: 103 -:Title: Server Selection -:Author: Samantha Ritter -:Advisors: David Golden -:Status: Draft -:Type: Standards -:Last Modified: November 15, 2017 - -See also the YAML test files and their accompanying README in the "tests" -directory. - -.. contents:: - --------- - -ReadPreference Document Validation -================================== - -While there are no YAML tests for this section, clients implementing this spec -SHOULD perform validations on ReadPreference documents provided by the user. -Specifically, documents with the following values should raise an error: - - - Mode PRIMARY and non-empty tag set - - -Calculating Round Trip Time -=========================== - -Drivers implementing server selection MUST test that RTT values are calculated correctly. -YAML tests for RTT calculations can be found in the "tests" directory and -they test for correctness in the following scenarios: - -- first RTT: new average RTT equals measurement -- subsequent measurements: new average RTT is calculated using the new measurement - and the previous average as described in the spec. - -Additionally, drivers SHOULD ensure that their implementations reject negative RTT values. - -Lastly, drivers SHOULD ensure that average RTT for a given ServerDescription is reset to 0 if that -server is disconnected (ie a network error occurs during an ``ismaster`` call). Upon reconnect, -the first new RTT value should become the average RTT for this server. - -The RTT tests are intentionally simplified to test the implementation of the -EWMA algorithm without imposing any additional conditions on drivers that might -affect architecture. For some drivers, RTT tests might require mocks; for others, -it might just require unit tests. - -Server Selection -================ - -The following test cases can be found in YAML form in the "tests" -directory. Each test lists a TopologyDescription representing a set of servers, -a ReadPreference document, and sets of servers returned at various stages of -the server selection process. These sets are described below. Note that it -is not required to test for correctness at every step. - -+------------------------+--------------------------------------------------------+ -| ``suitable_servers`` | the set of servers matching all server selection logic.| -+------------------------+--------------------------------------------------------+ -| ``in_latency_window`` | the subset of ``suitable_servers`` that falls within | -| | the allowable latency window (required). | -| | NOTE: tests use the default localThresholdMS of 15 ms. | -+------------------------+--------------------------------------------------------+ - -Drivers implementing server selection MUST test that their implementations -correctly return **one** of the servers in ``in_latency_window``. Drivers SHOULD test -against the full set of servers in ``in_latency_window`` and against -``suitable_servers`` if possible. - -Topology Type Single --------------------- - -- The single server is always selected. - - -Topology Type ReplicaSetNoPrimary ---------------------------------- - -**Reads** - -- PRIMARY - - - no server selected - - -- PRIMARY_PREFERRED - - - Matching tags: select any eligible secondary - - Non-matching tags: no server selected - - -- SECONDARY - - - Matching tags: select any eligible secondary - - Non-matching tags: no server selected - - -- SECONDARY_PREFERRED - - - Matching tags: select any eligible secondary - - Non-matching tags: no server selected - - -- NEAREST - - - Matching tags: select any eligible secondary - - Non-matching tags: no server selected - -**Writes** - -- Writes must go to a primary, no server can be selected. - - -Topology Type ReplicaSetWithPrimary ------------------------------------ - -**Reads** - - -- PRIMARY - - - primary is selected - - **NOTE:** it is an error to provide tags with mode PRIMARY. - See "ReadPreference Document Validation." - - -- PRIMARY_PREFERRED - - - Matching tags: primary is selected - - Non-matching tags: primary is selected - - -- SECONDARY - - - Matching tags: select any eligible secondary - - Non-matching tags: no server selected - - -- SECONDARY_PREFERRED - - - Matching tags: select any eligible secondary - - Non-matching tags: primary is selected - - -- NEAREST - - - Matching tags: select any eligible server - - Non-matching tags: no server selected - - -**Writes** - -- Primary is selected. - - -Topology Type Sharded ---------------------- - -**Reads** - -- Select any mongos. - - -**Writes** - -- Select any mongos. - - -Topology Type Unknown ---------------------- - -**Reads** - -- No server is selected. - - -**Writes** - -- No server is selected. - - -Passing ReadPreference to Mongos -================================ - -While there are no YAML tests for this, drivers are strongly encouraged to test -in a way specific to their implementation that ReadPreference is -correctly passed to Mongos in the following scenarios: - -- PRIMARY - - - the slaveOK wire protocol flag is NOT set - - $readPreference is NOT used - -- PRIMARY_PREFERRED - - - the slaveOK wire protocol flag is set - - $readPreference is used - -- SECONDARY - - - the slaveOK wire protocol flag is set - - $readPreference is used - -- SECONDARY_PREFERRED - - - the slaveOK wire protocol flag is set - - if tags are specified $readPreference is used, otherwise $readPreference is NOT used - -- NEAREST - - - the slaveOK wire protocol flag is set - - $readPreference is used - - -Random Selection Within Latency Window -====================================== - -The Server Selection spec mandates that drivers select a server at random from the -set of suitable servers that are within the latency window. Drivers implementing the -spec SHOULD test their implementations in a language-specific way to confirm randomness. - -For example, the following topology description, operation, and read preference will -return a set of three suitable servers within the latency window:: - - topology_description: - type: ReplicaSetWithPrimary - servers: - - &secondary_1 - address: b:27017 - avg_rtt_ms: 5 - type: RSSecondary - tags: {} - - &secondary_2 - address: c:27017 - avg_rtt_ms: 10 - type: RSSecondary - tags: {} - - &primary - address: a:27017 - avg_rtt_ms: 6 - type: RSPrimary - tags: {} - operation: read - read_preference: - mode: Nearest - tags: {} - in_latency_window: - - *primary - - *secondary_1 - - *secondary_2 - -Drivers SHOULD check that their implementation selects one of ``primary``, ``secondary_1``, -and ``secondary_2`` at random. - -Application-Provided Server Selector -==================================== - -The Server Selection spec allows drivers to configure registration of a server selector -function that filters the list of suitable servers. Drivers implementing this part -of the spec MUST test that: - -- The application-provided server selector is executed as part of the server selection process when - there is a nonzero number of candidate or eligible servers. For example, execute a test against a replica - set: Register a server selector that selects the suitable server with the highest port number. Execute 10 - queries with nearest read preference and, using command monitoring, assert that all the operations execute - on the member with the highest port number. - -- The application-provided server selector is not executed as part of the server selection process when - there are no candidate or eligible servers. For example, execute a test against a replica set - with no primary: Register a server selector that increments an internal counter (starting at 0) each time - it is called. Attempt server selection with primary read preference, catch the resulting server selection - timeout exception, and assert that the internal counter of the server selector is still 0. diff --git a/source/server-selection/server-selection.md b/source/server-selection/server-selection.md new file mode 100644 index 0000000000..39c2bf3aa4 --- /dev/null +++ b/source/server-selection/server-selection.md @@ -0,0 +1,1707 @@ +# Server Selection + +- Status: Accepted +- Minimum Server Version: 2.4 + +## Abstract + +MongoDB deployments may offer more than one server that can service an operation. This specification describes how +MongoDB drivers and mongos shall select a server for either read or write operations. It includes the definition of a +"read preference" document, configuration options, and algorithms for selecting a server for different deployment +topologies. + +## Meta + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Motivation for Change + +This specification builds upon the prior "Driver Read Preference" specification, which had a number of omissions, flaws +or other deficiencies: + +1. Mandating features that implied monotonicity for situations where monotonicity is not guaranteed +2. Mandating features that are not supported by mongos +3. Neglecting to specify a single, standard way to calculate average latency times +4. Specifying complex command-helper rules +5. Omitting rules for applying read preferences to a single server or to select among multiple mongos servers +6. Omitting test cases for verification of spec compliance + +This revision addresses these problems as well as improving structure and specificity. + +Additionally, it adds specifications for server selection more broadly: + +- Selection of a server for write operations +- Server selection retry and timeout + +## Specification + +### Scope and general requirements + +This specification describes how MongoDB drivers and mongos select a server for read and write operations, including +commands, OP_QUERY, OP_INSERT, OP_UPDATE, and OP_DELETE. For read operations, it describes how drivers and mongos shall +interpret a read preference document. + +This specification does not apply to OP_GET_MORE or OP_KILL_CURSORS operations on cursors, which need to go to the same +server that received an OP_QUERY and returned a cursor ID. + +For operations that are part of a sharded transaction this specification only applies to the initial operation which +starts the transaction on a mongos. This specification does not apply to subsequent operations that are part of the +sharded transaction because all operations in a sharded transaction need to go to the same mongos server. + +Drivers and mongos MUST conform to the semantics of this document, but SHOULD use language-appropriate data models or +variable names. + +This specification does not apply to commands issued for server monitoring or authentication. + +### Terms + +**Available** + +Describes a server that is believed to be reachable over the network and able to respond to requests. A server of type +Unknown or PossiblePrimary is not available; other types are available. + +**Client** + +Software that communicates with a MongoDB deployment. This includes both drivers and mongos. + +**Candidate** + +Describes servers in a deployment that enter the selection process, determined by the read preference `mode` parameter +and the servers' type. Depending on the `mode`, candidate servers might only include secondaries or might apply to all +servers in the deployment. + +**Deployment** + +One or more servers that collectively provide access to a single logical set of MongoDB databases. + +**Command** + +An OP_QUERY operation targeting the '$cmd' collection namespace. + +**Direct connection** + +A driver connection mode that sends all database operations to a single server without regard for type. + + + +**Eligible** + +Describes candidate servers that also meet the criteria specified by the `tag_sets` and `maxStalenessSeconds` read +preference parameters. + +**Hedged Read** + +A server mode in which the same query is dispatched in parallel to multiple replica set members. + +> [!NOTE] +> Hedged reads are deprecated in MongoDB Server version 8.0. + +**Immediate topology check** + +For a multi-threaded or asynchronous client, this means waking all server monitors for an immediate check. For a +single-threaded client, this means a (blocking) scan of all servers. + +**Latency window** + +When choosing between several suitable servers, the latency window is the range of acceptable RTTs from the shortest RTT +to the shortest RTT plus the local threshold. E.g. if the shortest RTT is 15ms and the local threshold is 200ms, then +the latency window ranges from 15ms - 215ms. + +**Local threshold** + +The maximum acceptable difference in milliseconds between the shortest RTT and the longest RTT of servers suitable to be +selected. + +**Mode** + +One of several enumerated values used as part of a read preference, defining which server types are candidates for reads +and the semantics for choosing a specific one. + +**Primary** + +Describes a server of type RSPrimary. + +**Query** + +An OP_QUERY operation targeting a regular (non '$cmd') collection namespace. + +**Read preference** + +The parameters describing which servers in a deployment can receive read operations, including `mode`, `tag_sets`, +`maxStalenessSeconds`, and `hedge` (deprecated). + +**RS** + +Abbreviation for "replica set". + +**RTT** + +Abbreviation for "round trip time". + +**Round trip time** + +The time in milliseconds to execute a `hello` or legacy hello command and receive a response for a given server. This +spec differentiates between the RTT of a single `hello` or legacy hello command and a server's *average* RTT over +several such commands. + +**Secondary** + +A server of type RSSecondary. + +**Staleness** + +A worst-case estimate of how far a secondary's replication lags behind the primary's last write. + +**Server** + +A mongod or mongos process. + +**Server selection** + +The process by which a server is chosen for a database operation out of all potential servers in a deployment. + +**Server type** + +An enumerated type indicating whether a server is up or down, whether it is a mongod or mongos, whether it belongs to a +replica set and, if so, what role it serves in the replica set. See the +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec for more +details. + +**Suitable** + +Describes a server that meets all specified criteria for a read or write operation. + +**Tag** + +A single key/value pair describing either (1) a user-specified characteristic of a replica set member or (2) a desired +characteristic for the target of a read operation. The key and value have no semantic meaning to the driver; they are +arbitrary user choices. + +**Tag set** + +A document of zero or more tags. Each member of a replica set can be configured with zero or one tag set. + +**Tag set list** + +A list of zero or more tag sets. A read preference might have a tag set list used for selecting servers. + +**Topology** + +The state of a deployment, including its type, which servers are members, and the server types of members. + +**Topology type** + +An enumerated type indicating the semantics for monitoring servers and selecting servers for database operations. See +the [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec for +more details. + +### Assumptions + +1. Unless they explicitly override these priorities, we assume our users prefer their applications to be, in order: + - Predictable: the behavior of the application should not change based on the deployment type, whether single mongod, + replica set or sharded cluster. + - Resilient: applications will adapt to topology changes, if possible, without raising errors or requiring manual + reconfiguration. + - Low-latency: all else being equal, faster responses to queries and writes are preferable. +2. Clients know the state of a deployment based on some form of ongoing monitoring, following the rules defined in the + [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec. + - They know which members are up or down, what their tag sets are, and their types. + - They know average round trip times to each available member. + - They detect reconfiguration and the addition or removal of members. +3. The state of a deployment could change at any time, in between any network interaction. + - Servers might or might not be reachable; they can change type at any time, whether due to partitions, elections, or + misconfiguration. + - Data rollbacks could occur at any time. + +### MongoClient Configuration + +Selecting a server requires the following client-level configuration options: + +#### localThresholdMS + +This defines the size of the latency window for selecting among multiple suitable servers. The default is 15 +(milliseconds). It MUST be configurable at the client level. It MUST NOT be configurable at the level of a database +object, collection object, or at the level of an individual query. + +In the prior read preference specification, `localThresholdMS` was called `secondaryAcceptableLatencyMS` by drivers. +Drivers MUST support the new name for consistency, but MAY continue to support the legacy name to avoid a +backward-breaking change. + +mongos currently uses `localThreshold` and MAY continue to do so. + +#### serverSelectionTimeoutMS + +This defines the maximum time to block for server selection before throwing an exception. The default is 30,000 +(milliseconds). It MUST be configurable at the client level. It MUST NOT be configurable at the level of a database +object, collection object, or at the level of an individual query. + +The actual timeout for server selection can be less than `serverSelectionTimeoutMS`. See [Timeouts](#timeouts) for rules +to compute the exact value. + +This default value was chosen to be sufficient for a typical server primary election to complete. As the server improves +the speed of elections, this number may be revised downward. + +Users that can tolerate long delays for server selection when the topology is in flux can set this higher. Users that +want to "fail fast" when the topology is in flux can set this to a small number. + +A serverSelectionTimeoutMS of zero MAY have special meaning in some drivers; zero's meaning is not defined in this spec, +but all drivers SHOULD document the meaning of zero. + +#### serverSelectionTryOnce + +Single-threaded drivers MUST provide a "serverSelectionTryOnce" mode, in which the driver scans the topology exactly +once after server selection fails, then either selects a server or raises an error. + +The serverSelectionTryOnce option MUST be true by default. If it is set false, then the driver repeatedly searches for +an appropriate server until the selection process times out (pausing +[minHeartbeatFrequencyMS](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#minheartbeatfrequencyms) +between attempts, as required by the +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec). + +Users of single-threaded drivers MUST be able to control this mode in one or both of these ways: + +- In code, pass true or false for an option called serverSelectionTryOnce, spelled idiomatically for the language, to + the MongoClient constructor. +- Include "serverSelectionTryOnce=true" or "serverSelectionTryOnce=false" in the URI. The URI option is spelled the same + for all drivers. + +Conflicting usages of the URI option and the symbol is an error. + +Multi-threaded drivers MUST NOT provide this mode. (See +[single-threaded server selection implementation](#single-threaded-server-selection-implementation) and the rationale +for a ["try once" mode](#try-once-mode).) + +#### heartbeatFrequencyMS + +This controls when topology updates are scheduled. See +[heartbeatFrequencyMS](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#heartbeatfrequencyms) in +the [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec for +details. + +#### socketCheckIntervalMS + +Only for single-threaded drivers. + +The default socketCheckIntervalMS MUST be 5000 (5 seconds), and it MAY be configurable. If socket has been idle for at +least this long, it must be checked before being used again. + +See [checking an idle socket after socketCheckIntervalMS](#checking-an-idle-socket-after-socketcheckintervalms) and +[what is the purpose of socketCheckIntervalMS?](#what-is-the-purpose-of-socketcheckintervalms). + +#### idleWritePeriodMS + +A constant, how often an idle primary writes a no-op to the oplog. See +[idleWritePeriodMS](../max-staleness/max-staleness.md#idlewriteperiodms) in the +[Max Staleness](../max-staleness/max-staleness.md) spec for details. + +#### smallestMaxStalenessSeconds + +A constant, 90 seconds. See "Smallest allowed value for maxStalenessSeconds" in the Max Staleness Spec. + +#### serverSelector + +Implementations MAY allow configuration of an optional, application-provided function that augments the server selection +rules. The function takes as a parameter a list of server descriptions representing the suitable servers for the read or +write operation, and returns a list of server descriptions that should still be considered suitable. + +### Read Preference + +A read preference determines which servers are considered suitable for read operations. Read preferences are interpreted +differently based on topology type. See topology-type-specific server selection rules for details. + +When no servers are suitable, the selection might be retried or will eventually fail following the rules described in +the [Rules for server selection](#rules-for-server-selection) section. + +#### Components of a read preference + +A read preference consists of a `mode` and optional `tag_sets`, `maxStalenessSeconds`, and `hedge` (deprecated). The +`mode` prioritizes between primaries and secondaries to produce either a single suitable server or a list of candidate +servers. If `tag_sets` and `maxStalenessSeconds` are set, they determine which candidate servers are for selection. If +`hedge` is set, it configures how server hedged reads are used. + +The default `mode` is 'primary'. The default `tag_sets` is a list with an empty tag set: `[{}]`. The default +`maxStalenessSeconds` is -1 or null, depending on the language. The default `hedge` is unset. + +Each is explained in greater detail below. + +##### mode + +For a deployment with topology type ReplicaSetWithPrimary or ReplicaSetNoPrimary, the `mode` parameter controls whether +primaries or secondaries are deemed suitable. Topology types Single and Sharded have different selection criteria and +are described elsewhere. + +Clients MUST support these modes: + +**primary** + +Only an available primary is suitable. + +**secondary** + +All secondaries (and *only* secondaries) are candidates, but only [eligible](#eligible) candidates (i.e. after applying +`tag_sets` and `maxStalenessSeconds`) are suitable. + +**primaryPreferred** + +If a primary is available, only the primary is suitable. Otherwise, all secondaries are candidates, but only eligible +secondaries are suitable. + +**secondaryPreferred** + +All secondaries are candidates. If there is at least one eligible secondary, only eligible secondaries are suitable. +Otherwise, when there are no eligible secondaries, the primary is suitable. + +**nearest** + +The primary and all secondaries are candidates, but only eligible candidates are suitable. + +*Note on other server types*: The +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec defines +several other server types that could appear in a replica set. Such types are never candidates, eligible or suitable. + + + +##### maxStalenessSeconds + +The maximum replication lag, in wall clock time, that a secondary can suffer and still be eligible. + +The default is no maximum staleness. + +A `maxStalenessSeconds` of -1 MUST mean "no maximum". Drivers are also free to use None, null, or other representations +of "no value" to represent "no max staleness". + +Drivers MUST raise an error if `maxStalenessSeconds` is a positive number and the `mode` field is 'primary'. + +A driver MUST raise an error if the TopologyType is ReplicaSetWithPrimary or ReplicaSetNoPrimary and either of these +conditions is false: + +```javascript +maxStalenessSeconds * 1000 >= heartbeatFrequencyMS + idleWritePeriodMS +maxStalenessSeconds >= smallestMaxStalenessSeconds +``` + +`heartbeatFrequencyMS` is defined in the +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec, and +`idleWritePeriodMS` is defined to be 10 seconds in the [Max Staleness](../max-staleness/max-staleness.md) spec. + +See "Smallest allowed value for maxStalenessSeconds" in the Max Staleness Spec. + +mongos MUST reject a read with `maxStalenessSeconds` provided and a `mode` of 'primary'. + +mongos MUST reject a read with `maxStalenessSeconds` that is not a positive integer. + +mongos MUST reject a read if `maxStalenessSeconds` is less than smallestMaxStalenessSeconds, with error code 160 +(SERVER-24421). + +During server selection, drivers (but not mongos) with `minWireVersion` < 5 MUST raise an error if `maxStalenessSeconds` +is a positive number, and any available server's `maxWireVersion` is less than 5.[^1] + +After filtering servers according to `mode`, and before filtering with `tag_sets`, eligibility MUST be determined from +`maxStalenessSeconds` as follows: + +- If `maxStalenessSeconds` is not a positive number, then all servers are eligible. + +- Otherwise, calculate staleness. Non-secondary servers (including Mongos servers) have zero staleness. If TopologyType + is ReplicaSetWithPrimary, a secondary's staleness is calculated using its ServerDescription "S" and the primary's + ServerDescription "P": + + ```javascript + (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS + ``` + + (All datetime units are in milliseconds.) + + If TopologyType is ReplicaSetNoPrimary, a secondary's staleness is calculated using its ServerDescription "S" and the + ServerDescription of the secondary with the greatest lastWriteDate, "SMax": + + ```javascript + SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS + ``` + + Servers with staleness less than or equal to `maxStalenessSeconds` are eligible. + +See the Max Staleness Spec for overall description and justification of this feature. + + + +##### tag_sets + +The read preference `tag_sets` parameter is an ordered list of tag sets used to restrict the eligibility of servers, +such as for data center awareness. + +Clients MUST raise an error if a non-empty tag set is given in `tag_sets` and the `mode` field is 'primary'. + +A read preference tag set (`T`) matches a server tag set (`S`) – or equivalently a server tag set (`S`) matches a read +preference tag set (`T`) — if `T` is a subset of `S` (i.e. `T ⊆ S`). + +For example, the read preference tag set "{ dc: 'ny', rack: '2' }" matches a secondary server with tag set "{ dc: 'ny', +rack: '2', size: 'large' }". + +A tag set that is an empty document matches any server, because the empty tag set is a subset of any tag set. This means +the default `tag_sets` parameter (`[{}]`) matches all servers. + +Tag sets are applied after filtering servers by `mode` and `maxStalenessSeconds`, and before selecting one server within +the latency window. + +Eligibility MUST be determined from `tag_sets` as follows: + +- If the `tag_sets` list is empty then all candidate servers are eligible servers. (Note, the default of `[{}]` means an + empty list probably won't often be seen, but if the client does not forbid an empty list, this rule MUST be + implemented to handle that case.) +- If the `tag_sets` list is not empty, then tag sets are tried in order until a tag set matches at least one candidate + server. All candidate servers matching that tag set are eligible servers. Subsequent tag sets in the list are + ignored. +- If the `tag_sets` list is not empty and no tag set in the list matches any candidate server, no servers are eligible + servers. + +##### hedge + +The deprecated read preference `hedge` parameter is a document that configures how the server will perform hedged reads. +It consists of the following keys: + +- `enabled`: Enables or disables hedging + +Hedged reads are automatically enabled in MongoDB 4.4+ when using a `nearest` read preference. To explicitly enable +hedging, the `hedge` document must be passed. An empty document uses server defaults to control hedging, but the +`enabled` key may be set to `true` or `false` to explicitly enable or disable hedged reads. + +Drivers MAY allow users to specify an empty hedge document if they accept documents for read preference options. Any +driver that exposes a builder API for read preference objects MUST NOT allow an empty `hedge` document to be +constructed. In this case, the user MUST specify a value for `enabled`, which MUST default to `true`. If the user does +not call a `hedge` API method, drivers MUST NOT send a `hedge` option to the server. + +Driver APIs related to the `hedge` parameter SHOULD be annotated and documented as deprecated. If static annotations are +not used, drivers MUST emit a runtime deprecation warning if a `hedge` parameter is specified. Such deprecation warnings +should note that hedged reads are deprecated in MongoDB Server 8.0 and will be removed in a future version. + +#### Read preference configuration + +Drivers MUST allow users to configure a default read preference on a `MongoClient` object. Drivers MAY allow users to +configure a default read preference on a `Database` or `Collection` object. + +A read preference MAY be specified as an object, document or individual `mode`, `tag_sets`, and `maxStalenessSeconds` +parameters, depending on what is most idiomatic for the language. + +If more than one object has a default read preference, the default of the most specific object takes precedence. I.e. +`Collection` is preferred over `Database`, which is preferred over `MongoClient`. + +Drivers MAY allow users to set a read preference on queries on a per-operation basis similar to how `hint` or +`batchSize` are set. E.g., in Python: + +```python +db.collection.find({}, read_preference=ReadPreference.SECONDARY) +db.collection.find( + {}, + read_preference=ReadPreference.NEAREST, + tag_sets=[{'dc': 'ny'}], + maxStalenessSeconds=120) +``` + + + +#### Passing read preference to mongos and load balancers + +If a server of type Mongos or LoadBalancer is selected for a read operation, the read preference is passed to the +selected mongos through the use of `$readPreference` (as a +[Global Command Argument](../message/OP_MSG.md#global-command-arguments) for OP_MSG or a query modifier for OP_QUERY) +and, for OP_QUERY only, the `SecondaryOk` wire protocol flag, according to the following rules. + +##### For OP_MSG: + +- For mode 'primary', drivers MUST NOT set `$readPreference` +- For all other read preference modes (i.e. 'secondary', 'primaryPreferred', ...), drivers MUST set `$readPreference` + +##### For OP_QUERY: + +If the read preference contains **only** a `mode` parameter and the mode is 'primary' or 'secondaryPreferred', for +maximum backwards compatibility with older versions of mongos, drivers MUST only use the value of the `SecondaryOk` wire +protocol flag (i.e. set or unset) to indicate the desired read preference and MUST NOT use a `$readPreference` query +modifier. + +Therefore, when sending queries to a mongos or load balancer, the following rules apply: + +- For mode 'primary', drivers MUST NOT set the `SecondaryOk` wire protocol flag and MUST NOT use `$readPreference` +- For mode 'secondary', drivers MUST set the `SecondaryOk` wire protocol flag and MUST also use `$readPreference` +- For mode 'primaryPreferred', drivers MUST set the `SecondaryOk` wire protocol flag and MUST also use `$readPreference` +- For mode 'secondaryPreferred', drivers MUST set the `SecondaryOk` wire protocol flag. If the read preference contains + a non-empty `tag_sets` parameter, `maxStalenessSeconds` is a positive integer, or the deprecated `hedge` parameter + is non-empty, drivers MUST use `$readPreference`; otherwise, drivers MUST NOT use `$readPreference` +- For mode 'nearest', drivers MUST set the `SecondaryOk` wire protocol flag and MUST also use `$readPreference` + +The `$readPreference` query modifier sends the read preference as part of the query. The read preference fields +`tag_sets` is represented in a `$readPreference` document using the field name `tags`. + +When sending a read operation via OP_QUERY and any `$` modifier is used, including the `$readPreference` modifier, the +query MUST be provided using the `$query` modifier like so: + +```javascript +{ + $query: { + field1: 'query_value', + field2: 'another_query_value' + }, + $readPreference: { + mode: 'secondary', + tags: [ { 'dc': 'ny' } ], + maxStalenessSeconds: 120 + } +} +``` + +##### Document structure + +A valid `$readPreference` document for mongos or load balancer has the following requirements: + +1. The `mode` field MUST be present exactly once with the mode represented in camel case: + + - 'primary' + - 'secondary' + - 'primaryPreferred' + - 'secondaryPreferred' + - 'nearest' + +2. If the `mode` field is "primary", the `tags`, `maxStalenessSeconds`, and `hedge` fields MUST be absent. + + Otherwise, for other `mode` values, the `tags` field MUST either be absent or be present exactly once and have an + array value containing at least one document. It MUST contain only documents, no other type. + + The `maxStalenessSeconds` field MUST be either be absent or be present exactly once with an integer value. + + The `hedge` field MUST be either absent or be a document. + +Mongos or service receiving a query with `$readPreference` SHOULD validate the `mode`, `tags`, `maxStalenessSeconds`, +and `hedge` fields according to rules 1 and 2 above, but SHOULD ignore unrecognized fields for forward-compatibility +rather than throwing an error. + +#### Use of read preferences with commands + +Because some commands are used for writes, deployment-changes or other state-changing side-effects, the use of read +preference by a driver depends on the command and how it is invoked: + +1. Write commands: `insert`, `update`, `delete`, `findAndModify` + + Write commands are considered write operations and MUST follow the corresponding + [Rules for server selection](#rules-for-server-selection) for each topology type. + +2. Generic command method: typically `command` or `runCommand` + + The generic command method MUST act as a read operation for the purposes of server selection. + + The generic command method has a default read preference of `mode` 'primary'. The generic command method MUST ignore + any default read preference from client, database or collection configuration. The generic command method SHOULD + allow an optional read preference argument. + + If an explicit read preference argument is provided as part of the generic command method call, it MUST be used for + server selection, regardless of the name of the command. It is up to the user to use an appropriate read + preference, e.g. not calling `renameCollection` with a `mode` of 'secondary'. + + N.B.: "used for server selection" does not supersede rules for server selection on "Standalone" topologies, which + ignore any requested read preference. + +3. Command-specific helper: methods that wrap database commands, like `count`, `distinct`, `listCollections` or + `renameCollection`. + + Command-specific helpers MUST act as read operations for the purposes of server selection, with read preference rules + defined by the following three categories of commands: + + - "must-use-primary": these commands have state-modifying effects and will only succeed on a primary. An example is + `renameCollection`. + + These command-specific helpers MUST use a read preference `mode` of 'primary', MUST NOT take a read preference + argument and MUST ignore any default read preference from client, database or collection configuration. Languages + with dynamic argument lists MUST throw an error if a read preference is provided as an argument. + + Clients SHOULD rely on the server to return a "not writable primary" or other error if the command is + "must-use-primary". Clients MAY raise an exception before sending the command if the topology type is Single and + the server type is not "Standalone", "RSPrimary" or "Mongos", but the identification of the set of + 'must-use-primary' commands is out of scope for this specification. + + - "should-use-primary": these commands are intended to be run on a primary, but would succeed -- albeit with possibly + stale data -- when run against a secondary. An example is `listCollections`. + + These command-specific helpers MUST use a read preference `mode` of 'primary', MUST NOT take a read preference + argument and MUST ignore any default read preference from client, database or collection configuration. Languages + with dynamic argument lists MUST throw an error if a read preference is provided as an argument. + + Clients MUST NOT raise an exception if the topology type is Single. + + - "may-use-secondary": these commands run against primaries or secondaries, according to users' read preferences. + They are sometimes called "query-like" commands. + + The current list of "may-use-secondary" commands includes: + + - aggregate without a write stage (e.g. `$out`, `$merge`) + - collStats + - count + - dbStats + - distinct + - find + - geoNear + - geoSearch + - group + - mapReduce where the `out` option is `{ inline: 1 }` + - parallelCollectionScan + + Associated command-specific helpers SHOULD take a read preference argument and otherwise MUST use the default read + preference from client, database, or collection configuration. + + For pre-5.0 servers, an aggregate command is "must-use-primary" if its pipeline contains a write stage (e.g. + `$out`, `$merge`); otherwise, it is "may-use-secondary". For 5.0+ servers, secondaries can execute an aggregate + command with a write stage and all aggregate commands are "may-use-secondary". This is discussed in more detail + in [Read preferences and server selection](../crud/crud.md#read-preferences-and-server-selection) in the CRUD + spec. + + If a client provides a specific helper for inline mapReduce, then it is "may-use-secondary" and the *regular* + mapReduce helper is "must-use-primary". Otherwise, the mapReduce helper is "may-use-secondary" and it is the + user's responsibility to specify `{inline: 1}` when running mapReduce on a secondary. + + New command-specific helpers implemented in the future will be considered "must-use-primary", "should-use-primary" or + "may-use-secondary" according to the specifications for those future commands. Command helper specifications SHOULD + use those terms for clarity. + +### Rules for server selection + +Server selection is a process which takes an operation type (read or write), a ClusterDescription, and optionally a read +preference and, on success, returns a ServerDescription for an operation of the given type. + +Server selection varies depending on whether a client is multi-threaded/asynchronous or single-threaded because a +single-threaded client cannot rely on the topology state being updated in the background. + +#### Timeouts + +Multi-threaded drivers and single-threaded drivers with `serverSelectionTryOnce` set to false MUST enforce a timeout for +the server selection process. The timeout MUST be computed as described in +[Client Side Operations Timeout: Server Selection](../client-side-operations-timeout/client-side-operations-timeout.md#server-selection). + +#### Multi-threaded or asynchronous server selection + +A driver that uses multi-threaded or asynchronous monitoring MUST unblock waiting operations as soon as server selection +completes, even if not all servers have been checked by a monitor. Put differently, the client MUST NOT block server +selection while waiting for server discovery to finish. + +For example, if the client is discovering a replica set and the application attempts a read operation with mode +'primaryPreferred', the operation MUST proceed immediately if a suitable secondary is found, rather than blocking until +the client has checked all members and possibly discovered a primary. + +The number of threads allowed to wait for server selection SHOULD be either (a) the same as the number of threads +allowed to wait for a connection from a pool; or (b) governed by a global or client-wide limit on number of waiting +threads, depending on how resource limits are implemented by a driver. + +##### operationCount + +Multi-threaded or async drivers MUST keep track of the number of operations that a given server is currently executing +(the server's `operationCount`). This value MUST be incremented once a server is selected for an operation and MUST be +decremented once that operation has completed, regardless of its outcome. Where this value is stored is left as a +implementation detail of the driver; some example locations include the `Server` type that also owns the connection pool +for the server (if there exists such a type in the driver's implementation) or on the pool itself. Incrementing or +decrementing a server's `operationCount` MUST NOT wake up any threads that are waiting for a topology update as part of +server selection. See +[operationCount-based selection within the latency window (multi-threaded or async)](#operationcount-based-selection-within-the-latency-window-multi-threaded-or-async) +for the rationale behind the way this value is used. + +##### Server Selection Algorithm + +For multi-threaded clients, the server selection algorithm is as follows: + +1. Record the server selection start time and log a + ["Server selection started" message](#server-selection-started-message). +2. If the topology wire version is invalid, raise an error and log a + ["Server selection failed" message](#server-selection-failed-message). +3. Find suitable servers as follows: + - Filter out any deprioritized server addresses. + - Find suitable servers from the filtered list by topology type and operation type. + - If there are no suitable servers, perform the previous step again without filtering out deprioritized servers. +4. Filter the suitable servers by calling the optional, application-provided server selector. +5. If there are any suitable servers, filter them according to + [Filtering suitable servers based on the latency window](#filtering-suitable-servers-based-on-the-latency-window) + and continue to the next step; otherwise, log a + ["Waiting for suitable server to become available" message](#waiting-for-suitable-server-to-become-available-message) + if one has not already been logged for this operation, and goto Step #9. +6. Choose two servers at random from the set of suitable servers in the latency window. If there is only 1 server in + the latency window, just select that server and goto Step #8. +7. Of the two randomly chosen servers, select the one with the lower `operationCount`. If both servers have the same + `operationCount`, select arbitrarily between the two of them. +8. Increment the `operationCount` of the selected server and return it. Log a + ["Server selection succeeded" message](#server-selection-succeeded-message). Do not go onto later steps. +9. Request an immediate topology check, then block the server selection thread until the topology changes or until the + server selection timeout has elapsed +10. If server selection has timed out, raise a [server selection error](#server-selection-errors) and log a + ["Server selection failed" message](#server-selection-failed-message). +11. Goto Step #2 + +#### Single-threaded server selection + +Single-threaded drivers do not monitor the topology in the background. Instead, they MUST periodically update the +topology during server selection as described below. + +When `serverSelectionTryOnce` is true, server selection timeouts have no effect; a single immediate topology check will +be done if the topology starts stale or if the first selection attempt fails. + +When `serverSelectionTryOnce` is false, then the server selection loops until a server is successfully selected or until +the selection timeout is exceeded. + +Therefore, for single-threaded clients, the server selection algorithm is as follows: + +1. Record the server selection start time and log a + ["Server selection started" message](#server-selection-started-message). +2. Record the maximum time as start time plus the computed timeout +3. If the topology has not been scanned in `heartbeatFrequencyMS` milliseconds, mark the topology stale +4. If the topology is stale, proceed as follows: + - record the target scan time as last scan time plus `minHeartBeatFrequencyMS` + - if [serverSelectionTryOnce](#serverselectiontryonce) is false and the target scan time would exceed the maximum + time, raise a [server selection error](#server-selection-errors) and log a + ["Server selection failed" message](#server-selection-failed-message). + - if the current time is less than the target scan time, sleep until the target scan time + - do a blocking immediate topology check (which must also update the last scan time and mark the topology as no + longer stale) +5. If the topology wire version is invalid, raise an error and log a + ["Server selection failed" message](#server-selection-failed-message). +6. Find suitable servers as follows: + - Filter out any deprioritized server addresses. + - Find suitable servers from the filtered list by topology type and operation type. + - If there are no suitable servers, perform the previous step again without filtering out deprioritized servers. +7. Filter the suitable servers by calling the optional, application-provided server selector. +8. If there are any suitable servers, filter them according to + [Filtering suitable servers based on the latency window](#filtering-suitable-servers-based-on-the-latency-window) + and return one at random from the filtered servers, and log a + ["Server selection succeeded" message](#server-selection-succeeded-message).; otherwise, mark the topology stale + and continue to step #9. +9. If [serverSelectionTryOnce](#serverselectiontryonce) is true and the last scan time is newer than the selection + start time, raise a [server selection error](#server-selection-errors) and log a + ["Server selection failed" message](#server-selection-failed-message); otherwise, log a + ["Waiting for suitable server to become available" message](#waiting-for-suitable-server-to-become-available-message) + if one has not already been logged for this operation, and goto Step #4 +10. If the current time exceeds the maximum time, raise a [server selection error](#server-selection-errors) and log a + ["Server selection failed" message](#server-selection-failed-message). +11. Goto Step #4 + +Before using a socket to the selected server, drivers MUST check whether the socket has been used in +[socketCheckIntervalMS](#socketcheckintervalms) milliseconds. If the socket has been idle for longer, the driver MUST +update the ServerDescription for the selected server. After updating, if the server is no longer suitable, the driver +MUST repeat the server selection algorithm and select a new server. + +Because single-threaded selection can do a blocking immediate check, the server selection timeout is not a hard +deadline. The actual maximum server selection time for any given request can vary from the timeout minus +`minHeartbeatFrequencyMS` to the timeout plus the time required for a blocking scan. + +Single-threaded drivers MUST document that when `serverSelectionTryOne` is true, selection may take up to the time +required for a blocking scan, and when `serverSelectionTryOne` is false, selection may take up to the timeout plus the +time required for a blocking scan. + +#### Topology type: Unknown + +When a deployment has topology type "Unknown", no servers are suitable for read or write operations. + +#### Topology type: Single + +A deployment of topology type Single contains only a single server of any type. Topology type Single signifies a direct +connection intended to receive all read and write operations. + +Therefore, read preference is ignored during server selection with topology type Single. The single server is always +suitable for reads if it is available. Depending on server type, the read preference is communicated to the server +differently: + +- Type Mongos: the read preference is sent to the server using the rules for + [Passing read preference to mongos and load balancers](#passing-read-preference-to-mongos-and-load-balancers). +- Type Standalone: clients MUST NOT send the read preference to the server +- For all other types, using OP_QUERY: clients MUST always set the `SecondaryOk` wire protocol flag on reads to ensure + that any server type can handle the request. +- For all other types, using OP_MSG: If no read preference is configured by the application, or if the application read + preference is Primary, then `$readPreference` MUST be set to `{ "mode": "primaryPreferred" }` to ensure that any + server type can handle the request. If the application read preference is set otherwise, `$readPreference` MUST be + set following [Document structure](#document-structure). + +The single server is always suitable for write operations if it is available. + +#### Topology type: LoadBalanced + +During command construction, drivers MUST add a `$readPreference` field to the command when required by +[Passing read preference to mongos and load balancers](#passing-read-preference-to-mongos-and-load-balancers); see the +[Load Balancer Specification](../load-balancers/load-balancers.md#server-selection) for details. + +#### Topology types: ReplicaSetWithPrimary or ReplicaSetNoPrimary + +A deployment with topology type ReplicaSetWithPrimary or ReplicaSetNoPrimary can have a mix of server types: RSPrimary +(only in ReplicaSetWithPrimary), RSSecondary, RSArbiter, RSOther, RSGhost, Unknown or PossiblePrimary. + +##### Read operations + +For the purpose of selecting a server for read operations, the same rules apply to both ReplicaSetWithPrimary and +ReplicaSetNoPrimary. + +To select from the topology a server that matches the user's Read Preference: + +If `mode` is 'primary', select the primary server. + +If `mode` is 'secondary' or 'nearest': + +> 1. Select all secondaries if `mode` is 'secondary', or all secondaries and the primary if `mode` is 'nearest'. +> 2. From these, filter out servers staler than `maxStalenessSeconds` if it is a positive number. +> 3. From the remaining servers, select servers matching the `tag_sets`. +> 4. From these, select one server within the latency window. + +(See [algorithm for filtering by staleness](#algorithm-for-filtering-by-staleness), +[algorithm for filtering by tag_sets](#algorithm-for-filtering-by-tag_sets), and +[filtering suitable servers based on the latency window](#filtering-suitable-servers-based-on-the-latency-window) for +details on each step, and +[why is maxStalenessSeconds applied before tag_sets?](#why-is-maxstalenessseconds-applied-before-tag_sets).) + +If `mode` is 'secondaryPreferred', attempt the selection algorithm with `mode` 'secondary' and the user's +`maxStalenessSeconds` and `tag_sets`. If no server matches, select the primary. Note that if all secondaries are +deprioritized, the primary MUST be selected if it is available. + +If `mode` is 'primaryPreferred', select the primary if it is known, otherwise attempt the selection algorithm with +`mode` 'secondary' and the user's `maxStalenessSeconds` and `tag_sets`. Note that if the primary is deprioritized, a +secondary MUST be selected if one is available. + +For all read preferences modes except 'primary', clients MUST set the `SecondaryOk` wire protocol flag (OP_QUERY) or +`$readPreference` global command argument (OP_MSG) to ensure that any suitable server can handle the request. If the +read preference mode is 'primary', clients MUST NOT set the `SecondaryOk` wire protocol flag (OP_QUERY) or +`$readPreference` global command argument (OP_MSG). + +##### Write operations + +If the topology type is ReplicaSetWithPrimary, only an available primary is suitable for write operations. + +If the topology type is ReplicaSetNoPrimary, no servers are suitable for write operations. + +#### Topology type: Sharded + +A deployment of topology type Sharded contains one or more servers of type Mongos or Unknown. + +For read operations, all servers of type Mongos are suitable; the `mode`, `tag_sets`, and `maxStalenessSeconds` read +preference parameters are ignored for selecting a server, but are passed through to mongos. See +[Passing read preference to mongos and load balancers](#passing-read-preference-to-mongos-and-load-balancers). + +For write operations, all servers of type Mongos are suitable. + +If more than one mongos is suitable, drivers MUST select a suitable server within the latency window (see +[Filtering suitable servers based on the latency window](#filtering-suitable-servers-based-on-the-latency-window)). + +### Round Trip Times and the Latency Window + +#### Calculation of Average Round Trip Times + +For every available server, clients MUST track the average RTT of server monitoring `hello` or legacy hello commands. + +An Unknown server has no average RTT. When a server becomes unavailable, its average RTT MUST be cleared. Clients MAY +implement this idiomatically (e.g nil, -1, etc.). + +When there is no average RTT for a server, the average RTT MUST be set equal to the first RTT measurement (i.e. the +first `hello` or legacy hello command after the server becomes available). + +After the first measurement, average RTT MUST be computed using an exponentially-weighted moving average formula, with a +weighting factor (`alpha`) of 0.2. If the prior average is denoted `old_rtt`, then the new average (`new_rtt`) is +computed from a new RTT measurement (`x`) using the following formula: + +```javascript +alpha = 0.2 +new_rtt = alpha * x + (1 - alpha) * old_rtt +``` + +A weighting factor of 0.2 was chosen to put about 85% of the weight of the average RTT on the 9 most recent +observations. + +#### Filtering suitable servers based on the latency window + +Server selection results in a set of zero or more suitable servers. If more than one server is suitable, a server MUST +be selected from among those within the latency window. + +The `localThresholdMS` configuration parameter controls the size of the latency window used to select a suitable server. + +The shortest average round trip time (RTT) from among suitable servers anchors one end of the latency window (`A`). The +other end is determined by adding `localThresholdMS` (`B = A + localThresholdMS`). + +A server MUST be selected from among suitable servers that have an average RTT (`RTT`) within the latency window (i.e. +`A ≤ RTT ≤ B`). In other words, the suitable server with the shortest average RTT is **always** a possible choice. Other +servers could be chosen if their average RTTs are no more than `localThresholdMS` more than the shortest average RTT. + +See either [Single-threaded server selection](#single-threaded-server-selection) or +[Multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection) for information on +how to select a server from among those within the latency window. + +### Checking an Idle Socket After socketCheckIntervalMS + +Only for single-threaded drivers. + +If a server is selected that has an existing connection that has been idle for socketCheckIntervalMS, the driver MUST +check the connection with the "ping" command. If the ping succeeds, use the selected connection. If not, set the +server's type to Unknown and update the Topology Description according to the Server Discovery and Monitoring Spec, and +attempt **once** more to select a server. + +The logic is expressed in this pseudocode. The algorithm for the "getServer" function is suggested below, in +[Single-threaded server selection implementation](#single-threaded-server-selection-implementation): + +```python +def getConnection(criteria): + # Get a server for writes, or a server matching read prefs, by + # running the server selection algorithm. + server = getServer(criteria) + if not server: + throw server selection error + + connection = server.connection + if connection is NULL: + # connect to server and return connection + else if connection has been idle < socketCheckIntervalMS: + return connection + else: + try: + use connection for "ping" command + return connection + except network error: + close connection + mark server Unknown and update Topology Description + + # Attempt *once* more to select. + server = getServer(criteria) + if not server: + throw server selection error + + # connect to server and return connection +``` + +See [What is the purpose of socketCheckIntervalMS?](#what-is-the-purpose-of-socketcheckintervalms). + +### Requests and Pinning Deprecated + +The prior read preference specification included the concept of a "request", which pinned a server to a thread for +subsequent, related reads. Requests and pinning are now **deprecated**. See +[What happened to pinning?](#what-happened-to-pinning) for the rationale for this change. + +Drivers with an existing request API MAY continue to provide it for backwards compatibility, but MUST document that +pinning for the request does not guarantee monotonic reads. + +Drivers MUST NOT automatically pin the client or a thread to a particular server without an explicit `start_request` (or +comparable) method call. + +Outside a legacy "request" API, drivers MUST use server selection for each individual read operation. + +### Logging + +Please refer to the [logging specification](../logging/logging.md) for details on logging implementations in general, +including log levels, log components, and structured versus unstructured logging. + +Drivers MUST support logging of server selection information via the following log messages. These messages MUST use the +`serverSelection` log component. + +The types used in the structured message definitions below are demonstrative, and drivers MAY use similar types instead +so long as the information is present (e.g. a double instead of an integer, or a string instead of an integer if the +structured logging framework does not support numeric types.) + +#### Common Fields + +The following key-value pairs MUST be included in all server selection log messages: + +| Key | Suggested Type | Value | +| ------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| selector | String | String representation of the selector being used to select the server. This can be a read preference or an application-provided custom selector. The exact content of is flexible depending on what the driver is able to log. At minimum, when the selector is a read preference this string MUST contain all components of the read preference, and when it is an application-provided custom selector the string MUST somehow indicate that it is a custom selector. | +| operationId | Int | The driver-generated operation ID. Optional; only present if the driver generates operation IDs and this command has one. | +| operation | String | The name of the operation for which a server is being selected. When server selection is being performed to select a server for a command, this MUST be the command name. | +| topologyDescription | String | String representation of the current topology description. The format of is flexible and could be e.g. the `toString()` implementation for a driver's topology type, or an extended JSON representation of the topology object. | + +#### "Server selection started" message + +This message MUST be logged at `debug` level. It MUST be emitted on the occasions specified either in +[Multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection) or +[Single-threaded server selection](#single-threaded-server-selection), depending on which algorithm the driver +implements. + +This message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | -------------------------- | +| message | String | "Server selection started" | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Server selection started for operation {{operation}} with ID {{operationId}}. Selector: {{selector}}, topology +> description: {{topologyDescription}} + +#### "Server selection succeeded" message + +This message MUST be logged at `debug` level. It MUST be emitted on the occasions specified either in +[Multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection) or +[Single-threaded server selection](#single-threaded-server-selection), depending on which algorithm the driver +implements. + +This message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| message | String | "Server selection succeeded" | +| serverHost | String | The hostname, IP address, or Unix domain socket path for the selected server. | +| serverPort | Int | The port for the selected server. Optional; not present for Unix domain sockets. When the user does not specify a port and the default (27017) is used, the driver SHOULD include it here. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Server selection succeeded for operation {{operation}} with ID {{operationId}}. Selected server: +> {{serverHost}}:{{serverPort}}. Selector: {{selector}}, topology description: {{topologyDescription}} + +#### "Server selection failed" message + +This message MUST be logged at `debug` level. It MUST be emitted on the occasions specified either in +[Multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection) or +[Single-threaded server selection](#single-threaded-server-selection), depending on which algorithm the driver +implements. + +This message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| message | String | "Server selection failed" | +| failure | Flexible | Representation of the error the driver will throw regarding server selection failing. The type and format of this value is flexible; see the [logging specification](../logging/logging.md#representing-errors-in-log-messages) for details on representing errors in log messages. Drivers MUST take care to not include any information in this field that is already included in the log message; e.g. the topology description should not be duplicated within this field. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Server selection failed for operation {{operationName}} with ID {{operationId}}. Failure: {{failure}}. Selector: +> {{selector}}, topology description: {{topologyDescription}} + +#### "Waiting for suitable server to become available" message + +This message MUST be logged at `info` level. It MUST be emitted on the occasions specified either in +[Multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection) or +[Single-threaded server selection](#single-threaded-server-selection), depending on which algorithm the driver +implements. + +In order to avoid generating redundant log messages, the driver MUST take care to only emit this message once per +operation. We only log the message once because the only values that can change over time are: + +- The remaining time: given the initial message's timestamp and the initial timestamp, the time remaining can always be + inferred from the original message. +- The topology description: rather than logging these changes on a per-operation basis, users should observe them with a + single set of messages for the entire client via SDAM log messages. + +This message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| --------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | String | "Waiting for suitable server to become available" | +| remainingTimeMS | Int | The remaining time left until server selection will time out. This MAY be omitted if the driver supports disabling server selection timeout altogether. | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Waiting for server to become available for operation {{operationName}} with ID {{operationId}}. Remaining time: +> {{remainingTimeMS}} ms. Selector: {{selector}}, topology description: {{topologyDescription}}. + +## Implementation Notes + +These are suggestions. As always, driver authors should balance cross-language standardization with backwards +compatibility and the idioms of their language. + +### Modes + +Modes ('primary', 'secondary', ...) are constants declared in whatever way is idiomatic for the programming language. +The constant values may be ints, strings, or whatever. However, when attaching modes to `$readPreference` camel case +must be used as described above in +[Passing read preference to mongos and load balancers](#passing-read-preference-to-mongos-and-load-balancers). + +#### primaryPreferred and secondaryPreferred + +'primaryPreferred' is equivalent to selecting a server with read preference mode 'primary' (without `tag_sets` or +`maxStalenessSeconds`), or, if that fails, falling back to selecting with read preference mode 'secondary' (with +`tag_sets` and `maxStalenessSeconds`, if provided). + +'secondaryPreferred' is the inverse: selecting with mode 'secondary' (with `tag_sets` and `maxStalenessSeconds`) and +falling back to selecting with mode 'primary' (without `tag_sets` or `maxStalenessSeconds`). + +Depending on the implementation, this may result in cleaner code. + +#### nearest + +The term 'nearest' is unfortunate, as it implies a choice based on geographic locality or absolute lowest latency, +neither of which are true. + +Instead, and unlike the other read preference modes, 'nearest' does not favor either primaries or secondaries; instead +all servers are candidates and are filtered by `tag_sets` and `maxStalenessSeconds`. + +To always select the server with the lowest RTT, users should use mode 'nearest' without `tag_sets` or +`maxStalenessSeconds` and set `localThresholdMS` to zero. + +To distribute reads across all members evenly regardless of RTT, users should use mode 'nearest' without `tag_sets` or +`maxStalenessSeconds` and set `localThresholdMS` very high so that all servers fall within the latency window. + +In both cases, `tag_sets` and `maxStalenessSeconds` could be used to further restrict the set of eligible servers, if +desired. + +### Tag set lists + +Tag set lists can be configured in the driver in whatever way is natural for the language. + +### Multi-threaded server selection implementation + +The following example uses a single lock for clarity. Drivers are free to implement whatever concurrency model best +suits their design. + +The following is pseudocode for +[multi-threaded or asynchronous server selection](#multi-threaded-or-asynchronous-server-selection): + +```python +def getServer(criteria): + client.lock.acquire() + + now = gettime() + endTime = now + computed server selection timeout + + log a "server selection started" message + while true: + # The topologyDescription keeps track of whether any server has an + # an invalid wire version range + if not topologyDescription.compatible: + client.lock.release() + log a "server selection failed" message + throw invalid wire protocol range error with details + + if maxStalenessSeconds is set: + if client minWireVersion < 5 and ": + client.lock.release() + throw error + + if topologyDescription.type in (ReplicaSetWithPrimary, ReplicaSetNoPrimary): + if (maxStalenessSeconds * 1000 < heartbeatFrequencyMS + idleWritePeriodMS or + maxStalenessSeconds < smallestMaxStalenessSeconds): + client.lock.release() + throw error + + servers = all servers in topologyDescription matching criteria + + if serverSelector is not null: + servers = serverSelector(servers) + + if servers is not empty: + in_window = servers within the latency window + if len(in_window) == 1: + selected = in_window[0] + else: + server1, server2 = random two entries from in_window + if server1.operation_count <= server2.operation_count: + selected = server1 + else: + selected = server2 + selected.operation_count += 1 + client.lock.release() + return selected + + request that all monitors check immediately + if the message was not logged already for this operation: + log a "waiting for suitable server to become available" message + + # Wait for a new TopologyDescription. condition.wait() releases + # client.lock while waiting and reacquires it before returning. + # While a thread is waiting on client.condition, it is awakened + # early whenever a server check completes. + timeout_left = endTime - gettime() + client.condition.wait(timeout_left) + + if now after endTime: + client.lock.release() + throw server selection error +``` + +### Single-threaded server selection implementation + +The following is pseudocode for [single-threaded server selection](#single-threaded-server-selection): + +```python +def getServer(criteria): + startTime = gettime() + loopEndTime = startTime + maxTime = startTime + computed server selection timeout + nextUpdateTime = topologyDescription.lastUpdateTime + + heartbeatFrequencyMS/1000: + + if nextUpdateTime < startTime: + topologyDescription.stale = true + + while true: + + if topologyDescription.stale: + scanReadyTime = topologyDescription.lastUpdateTime + + minHeartbeatFrequencyMS/1000 + + if ((not serverSelectionTryOnce) and (scanReadyTime > maxTime)): + throw server selection error with details + + # using loopEndTime below is a proxy for "now" but avoids + # the overhead of another gettime() call + sleepTime = scanReadyTime - loopEndTime + + if sleepTime > 0: + sleep sleepTime + + rescan all servers + topologyDescription.lastupdateTime = gettime() + topologyDescription.stale = false + + # topologyDescription keeps a record of whether any + # server has an incompatible wire version range + if not topologyDescription.compatible: + topologyDescription.stale = true + # throw invalid wire version range error with details + + if maxStalenessSeconds is set: + if client minWireVersion < 5 and "": + # throw error + + if topologyDescription.type in (ReplicaSetWithPrimary, ReplicaSetNoPrimary): + if (maxStalenessSeconds * 1000 < heartbeatFrequencyMS + idleWritePeriodMS or + maxStalenessSeconds < smallestMaxStalenessSeconds): + # throw error + + servers = all servers in topologyDescription matching criteria + + if serverSelector is not null: + servers = serverSelector(servers) + + if servers is not empty: + in_window = servers within the latency window + return random entry from in_window + else: + topologyDescription.stale = true + + loopEndTime = gettime() + + if serverSelectionTryOnce: + if topologyDescription.lastUpdateTime > startTime: + throw server selection error with details + else if loopEndTime > maxTime: + throw server selection error with details + + if the message was not logged already: + log a "waiting for suitable server to become available" message +``` + +### Server Selection Errors + +Drivers should use server descriptions and their error attributes (if set) to return useful error messages. + +For example, when there are no members matching the ReadPreference: + +- "No server available for query with ReadPreference primary" +- "No server available for query with ReadPreference secondary" +- "No server available for query with ReadPreference " + mode + ", tag set list " + tag_sets + ", and + `maxStalenessSeconds` " + maxStalenessSeconds + +Or, if authentication failed: + +- "Authentication failed: `[specific error message]`" + +Here is a sketch of some pseudocode for handling error reporting when errors could be different across servers: + +```text +if there are any available servers: + error_message = "No servers are suitable for " + criteria +else if all ServerDescriptions' errors are the same: + error_message = a ServerDescription.error value +else: + error_message = ', '.join(all ServerDescriptions' errors) +``` + +### Cursors + +Cursor operations OP_GET_MORE and OP_KILL_CURSOR do not go through the server selection process. Cursor operations must +be sent to the original server that received the query and sent the OP_REPLY. For exhaust cursors, the same socket must +be used for OP_GET_MORE until the cursor is exhausted. + +### Sharded Transactions + +Operations that are part of a sharded transaction (after the initial command) do not go through the server selection +process. Sharded transaction operations MUST be sent to the original mongos server on which the transaction was started. + +### The 'text' command and mongos + +*Note*: As of MongoDB 2.6, mongos doesn't distribute the "text" command to secondaries, see +[SERVER-10947](https://jira.mongodb.org/browse/SERVER-10947). + +However, the "text" command is deprecated in 2.6, so this command-specific helper may become deprecated before this is +fixed. + +## Test Plan + +The server selection test plan is given in a separate document that describes the tests and supporting data files: +[Server Selection Tests](./server-selection-tests.md) + +## Design Rationale + +### Use of topology types + +The prior version of the read preference spec had only a loose definition of server or topology types. The +[Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) spec defines +these terms explicitly and they are used here for consistency and clarity. + +### Consistency with mongos + +In order to ensure that behavior is consistent regardless of topology type, read preference behaviors are limited to +those that mongos can proxy. + +For example, mongos ignores read preference 'secondary' when a shard consists of a single server. Therefore, this spec +calls for topology type Single to ignore read preferences for consistency. + +The spec has been written with the intention that it can apply to both drivers and mongos and the term "client" has been +used when behaviors should apply to both. Behaviors that are specific to drivers are largely limited to those for +communicating with a mongos. + +### New localThresholdMS configuration option name + +Because this does not apply **only** to secondaries and does not limit absolute latency, the name +`secondaryAcceptableLatencyMS` is misleading. + +The mongos name `localThreshold` misleads because it has nothing to do with locality. It also doesn't include the `MS` +units suffix for consistency with other time-related configuration options. + +However, given a choice between the two, `localThreshold` is a more general term. For drivers, we add the `MS` suffix +for clarity about units and consistency with other configuration options. + +### Random selection within the latency window (single-threaded) + +When more than one server is judged to be suitable, the spec calls for random selection to ensure a fair distribution of +work among servers within the latency window. + +It would be hard to ensure a fair round-robin approach given the potential for servers to come and go. Making newly +available servers either first or last could lead to unbalanced work. Random selection has a better fairness guarantee +and keeps the design simpler. + +### operationCount-based selection within the latency window (multi-threaded or async) + +As operation execution slows down on a node (e.g. due to degraded server-side performance or increased network latency), +checked-out pooled connections to that node will begin to remain checked out for longer periods of time. Assuming at +least constant incoming operation load, more connections will then need to be opened against the node to service new +operations that it gets selected for, further straining it and slowing it down. This can lead to runaway connection +creation scenarios that can cripple a deployment ("connection storms"). As part of DRIVERS-781, the random choice +portion of multi-threaded server selection was changed to more evenly spread out the workload among suitable servers in +order to prevent any single node from being overloaded. The new steps achieve this by approximating an individual +server's load via the number of concurrent operations that node is processing (operationCount) and then routing +operations to servers with less load. This should reduce the number of new operations routed towards nodes that are +busier and thus increase the number routed towards nodes that are servicing operations faster or are simply less busy. +The previous random selection mechanism did not take load into account and could assign work to nodes that were under +too much stress already. + +As an added benefit, the new approach gives preference to nodes that have recently been discovered and are thus are more +likely to be alive (e.g. during a rolling restart). The narrowing to two random choices first ensures new servers aren't +overly preferred however, preventing a "thundering herd" situation. Additionally, the +[maxConnecting](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool) provisions +included in the CMAP specification prevent drivers from crippling new nodes with connection storms. + +This approach is based on the +["Power of Two Random Choices with Least Connections"](https://web.archive.org/web/20191212194243/https://www.nginx.com/blog/nginx-power-of-two-choices-load-balancing-algorithm/) +load balancing algorithm. + +An alternative approach to this would be to prefer selecting servers that already have available connections. While that +approach could help reduce latency, it does not achieve the benefits of routing operations away from slow servers or of +preferring newly introduced servers. Additionally, that approach could lead to the same node being selected repeatedly +rather than spreading the load out among all suitable servers. + +### The SecondaryOk wire protocol flag + +In server selection, there is a race condition that could exist between what a selected server type is believed to be +and what it actually is. + +The `SecondaryOk` wire protocol flag solves the race problem by communicating to the server whether a secondary is +acceptable. The server knows its type and can return a "not writable primary" error if `SecondaryOk` is false and the +server is a secondary. + +However, because topology type Single is used for direct connections, we want read operations to succeed even against a +secondary, so the `SecondaryOk` wire protocol flag must be sent to mongods with topology type Single. + +(If the server type is Mongos, follow the rules for +[Passing read preference to mongos and load balancers](#passing-read-preference-to-mongos-and-load-balancers), even for +topology type Single.) + +### General command method going to primary + +The list of commands that can go to secondaries changes over time and depends not just on the command but on parameters. +For example, the `mapReduce` command may or may not be able to be run on secondaries depending on the value of the `out` +parameter. + +It significantly simplifies implementation for the general command method always to go to the primary unless a explicit +read preference is set and rely on users of the general command method to provide a read preference appropriate to the +command. + +The command-specific helpers will need to implement a check of read preferences against the semantics of the command and +its parameters, but keeping this logic close to the command rather than in a generic method is a better design than +either delegating this check to the generic method, duplicating the logic in the generic method, or coupling both to +another validation method. + +### Average round trip time calculation + +Using an exponentially-weighted moving average avoids having to store and rotate an arbitrary number of RTT +observations. All observations count towards the average. The weighting makes recent observations count more heavily +while smoothing volatility. + +### Verbose errors + +Error messages should be sufficiently verbose to allow users and/or support engineers to determine the reasons for +server selection failures from log or other error messages. + +### "Try once" mode + +Single-threaded drivers in languages like PHP and Perl are typically deployed as many processes per application server. +Each process must independently discover and monitor the MongoDB deployment. + +When no suitable server is available (due to a partition or misconfiguration), it is better for each request to fail as +soon as its process detects a problem, instead of waiting and retrying to see if the deployment recovers. + +Minimizing response latency is important for maximizing request-handling capacity and for user experience (e.g. a quick +fail message instead of a slow web page). + +However, when a request arrives and the topology information is already stale, or no suitable server is known, making a +single attempt to update the topology to service the request is acceptable. + +A user of a single-threaded driver who prefers resilience in the face of topology problems, rather than short response +times, can turn the "try once" mode off. Then driver rescans the topology every minHeartbeatFrequencyMS until a suitable +server is found or the timeout expires. + +### What is the purpose of socketCheckIntervalMS? + +Single-threaded clients need to make a compromise: if they check servers too frequently it slows down regular +operations, but if they check too rarely they cannot proactively avoid errors. + +Errors are more disruptive for single-threaded clients than for multi-threaded. If one thread in a multi-threaded +process encounters an error, it warns the other threads not to use the disconnected server. But single-threaded clients +are deployed as many independent processes per application server, and each process must throw an error until all have +discovered that a server is down. + +The compromise specified here balances the cost of frequent checks against the disruption of many errors. The client +preemptively checks individual sockets that have not been used in the last +[socketCheckIntervalMS](#socketcheckintervalms), which is more frequent by default than `heartbeatFrequencyMS` defined +in the Server Discovery and Monitoring Spec. + +The client checks the socket with a "ping" command, rather than "hello" or legacy hello, because it is not checking the +server's full state as in the Server Discovery and Monitoring Spec, it is only verifying that the connection is still +open. We might also consider a `select` or `poll` call to check if the socket layer considers the socket closed, without +requiring a round-trip to the server. However, this technique usually will not detect an uncleanly shutdown server or a +network outage. + +## Backwards Compatibility + +In general, backwards breaking changes have been made in the name of consistency with mongos and avoiding misleading +users about monotonicity. + +- Features removed: + + > - Automatic pinning (see [What happened to pinning?](#what-happened-to-pinning)) + > - Auto retry (replaced by the general server selection algorithm) + > - mongos "high availability" mode (effectively, mongos pinning) + +- Other features and behaviors have changed explicitly + + > - Ignoring read preferences for topology type Single + > - Default read preference for the generic command method + +- Changes with grandfather clauses + + > - Alternate names for `localThresholdMS` + > - Pinning for legacy request APIs + +- Internal changes with little user-visibility + + > - Clarifying calculation of average RTT + +## Questions and Answers + +### What happened to pinning? + +The prior read preference spec, which was implemented in the versions of the drivers and mongos released concomitantly +with MongoDB 2.2, stated that a thread / client should remain pinned to an RS member as long as that member matched the +current mode, tags, and acceptable latency. This increased the odds that reads would be monotonic (assuming no +rollback), but had the following surprising consequence: + +1. Thread / client reads with mode 'secondary' or 'secondaryPreferred', gets pinned to a secondary +2. Thread / client reads with mode 'primaryPreferred', driver / mongos sees that the pinned member (a secondary) matches + the mode (which *allows* for a secondary) and reads from secondary, even though the primary is available and + preferable + +The old spec also had the swapped problem, reading from the primary with 'secondaryPreferred', except for mongos which +was changed at the last minute before release with [SERVER-6565](https://jira.mongodb.org/browse/SERVER-6565). + +This left application developers with two problems: + +1. 'primaryPreferred' and 'secondaryPreferred' acted surprisingly and unpredictably within requests +2. There was no way to specify a common need: read from a secondary if possible with 'secondaryPreferred', then from + primary if possible with 'primaryPreferred', all within a request. Instead an application developer would have to + do the second read with 'primary', which would unpin the thread but risk unavailability if only secondaries were + up. + +Additionally, mongos 2.4 introduced the releaseConnectionsAfterResponse option (RCAR), mongos 2.6 made it the default +and mongos 2.8 will remove the ability to turn it off. This means that pinning to a mongos offers no guarantee that +connections to shards are pinned. Since we can't provide the same guarantees for replica sets and sharded clusters, we +removed automatic pinning entirely and deprecated "requests". See +[SERVER-11956](https://jira.mongodb.org/browse/SERVER-11956) and +[SERVER-12273](https://jira.mongodb.org/browse/SERVER-12273). + +Regardless, even for replica sets, pinning offers no monotonicity because of the ever-present possibility of rollbacks. +Through MongoDB 2.6, secondaries did not close sockets on rollback, so a rollback could happen between any two queries +without any indication to the driver. + +Therefore, an inconsistent feature that doesn't actually do what people think it does has no place in the spec and has +been removed. Should the server eventually implement some form of "sessions", this spec will need to be revised +accordingly. + +### Why change from mongos High Availability (HA) to random selection? + +Mongos HA has similar problems with pinning, in that one can wind up pinned to a high-latency mongos even if a +lower-latency mongos later becomes available. + +Selection within the latency window avoids this problem and makes server selection exactly analogous to having multiple +suitable servers from a replica set. This is easier to explain and implement. + +### What happened to auto-retry? + +The old auto-retry mechanism was closely connected to server pinning, which has been removed. It also mandated exactly +three attempts to carry out a query on different servers, with no way to disable or adjust that value, and only for the +first query within a request. + +To the extent that auto-retry was trying to compensate for unavailable servers, the Server Discovery and Monitoring spec +and new server selection algorithm provide a more robust and configurable way to direct *all* queries to available +servers. + +After a server is selected, several error conditions could still occur that make the selected server unsuitable for +sending the operation, such as: + +> - the server could have shutdown the socket (e.g. a primary stepping down), +> - a connection pool could be empty, requiring new connections; those connections could fail to connect or could fail +> the server handshake + +Once an operation is sent over the wire, several additional error conditions could occur, such as: + +> - a socket timeout could occur before the server responds +> - the server might send an RST packet, indicating the socket was already closed +> - for write operations, the server might return a "not writable primary" error + +This specification does not require nor prohibit drivers from attempting automatic recovery for various cases where it +might be considered reasonable to do so, such as: + +> - repeating server selection if, after selection, a socket is determined to be unsuitable before a message is sent on +> it +> - for a read operation, after a socket error, selecting a new server meeting the read preference and resending the +> query +> - for a write operation, after a "not writable primary" error, selecting a new server (to locate the primary) and +> resending the write operation + +Driver-common rules for retrying operations (and configuring such retries) could be the topic of a different, future +specification. + +### Why is maxStalenessSeconds applied before tag_sets? + +The intention of read preference's list of tag sets is to allow a user to prefer the first tag set but fall back to +members matching later tag sets. In order to know whether to fall back or not, we must first filter by all other +criteria. + +Say you have two secondaries: + +> - Node 1, tagged `{'tag': 'value1'}`, estimated staleness 5 minutes +> - Node 2, tagged `{'tag': 'value2'}`, estimated staleness 1 minute + +And a read preference: + +> - mode: "secondary" +> - maxStalenessSeconds: 120 (2 minutes) +> - tag_sets: `[{'tag': 'value1'}, {'tag': 'value2'}]` + +If tag sets were applied before maxStalenessSeconds, we would select Node 1 since it matches the first tag set, then +filter it out because it is too stale, and be left with no eligible servers. + +The user's intent in specifying two tag sets was to fall back to the second set if needed, so we filter by +maxStalenessSeconds first, then tag_sets, and select Node 2. + +### Why does server deprioritization use only server addresses and not ServerDescription objects? + +A server's address is the minimum identifying attribute that stays constant for across topology changes. Drivers create +new ServerDescription objects on each topology change, and since ServerDescription objects check multiple attributes to +determine equality comparisons, a deprioritized server could become non-equal to itself after a change and therefore +incorrectly be considered suitable for a retry operation. + +By using addresses, we ensure that once a server is marked as deprioritized by an operation, it cannot be used again for +a retry on that operation unless there are no other suitable servers. + +## References + +- [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) specification +- [Driver Authentication](../auth/auth.md) specification +- [Connection Monitoring and Pooling](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md) + specification + +## Changelog + +- 2025-12-08: Require server deprioritization for all topology types and clarify the order of server candidate + filtering. + +- 2015-06-26: Updated single-threaded selection logic with "stale" and serverSelectionTryOnce. + +- 2015-08-10: Updated single-threaded selection logic to ensure a scan always happens at least once under + serverSelectionTryOnce if selection fails. Removed the general selection algorithm and put full algorithms for each + of the single- and multi-threaded sections. Added a requirement that single-threaded drivers document selection time + expectations. + +- 2016-07-21: Updated for Max Staleness support. + +- 2016-08-03: Clarify selection algorithm, in particular that maxStalenessMS comes before tag_sets. + +- 2016-10-24: Rename option from "maxStalenessMS" to "maxStalenessSeconds". + +- 2016-10-25: Change minimum maxStalenessSeconds value from 2 * heartbeatFrequencyMS to heartbeatFrequencyMS + + idleWritePeriodMS (with proper conversions of course). + +- 2016-11-01: Update formula for secondary staleness estimate with the equivalent, and clearer, expression of this + formula from the Max Staleness Spec + +- 2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the future, require maxStalenessSeconds to + be at least 90. + +- 2017-06-07: Clarify socketCheckIntervalMS behavior, single-threaded drivers must retry selection after checking an + idle socket and discovering it is broken. + +- 2017-11-10: Added application-configurated server selector. + +- 2017-11-12: Specify read preferences for OP_MSG with direct connection, and delete obsolete comment direct connections + to secondaries getting "not writable primary" errors by design. + +- 2018-01-22: Clarify that `$query` wrapping is only for OP_QUERY + +- 2018-01-22: Clarify that `$out` on aggregate follows the "`$out` Aggregation Pipeline Operator" spec and warns if read + preference is not primary. + +- 2018-01-29: Remove reference to '`$out` Aggregation spec'. Clarify runCommand selection rules. + +- 2018-12-13: Update tag_set example to use only String values + +- 2019-05-20: Added rule to not send read preferene to standalone servers + +- 2019-06-07: Clarify language for aggregate and mapReduce commands that write + +- 2020-03-17: Specify read preferences with support for server hedged reads + +- 2020-10-10: Consider server load when selecting servers within the latency window. + +- 2021-04-07: Adding in behaviour for load balancer mode. + +- 2021-05-12: Removed deprecated URI option in favour of readPreference=secondaryPreferred. + +- 2021-05-13: Updated to use modern terminology. + +- 2021-08-05: Updated `$readPreference` logic to describe OP_MSG behavior. + +- 2021-09-03: Clarify that wire version check only applies to available servers. + +- 2021-09-28: Note that 5.0+ secondaries support aggregate with write stages (e.g. `$out` and `$merge`). Clarify setting + `SecondaryOk` wire protocol flag or `$readPreference` global command argument for replica set topology. + +- 2022-01-19: Require that timeouts be applied per the client-side operations timeout spec + +- 2022-10-05: Remove spec front matter, move footnote, and reformat changelog. + +- 2022-11-09: Add log messages and tests. + +- 2023-08-26: Add list of deprioritized servers for sharded cluster topology. + +- 2024-02-07: Migrated from reStructuredText to Markdown. + +- 2025-02-25: Note the deprecation of hedged reads. + +[^1]: mongos 3.4 refuses to connect to mongods with maxWireVersion < 5, so it does no additional wire version checks + related to maxStalenessSeconds. diff --git a/source/server-selection/server-selection.rst b/source/server-selection/server-selection.rst index 256e005b27..46bae3b193 100644 --- a/source/server-selection/server-selection.rst +++ b/source/server-selection/server-selection.rst @@ -1,1715 +1,3 @@ -================ -Server Selection -================ - -:Spec: 103 -:Title: Server Selection -:Author: David Golden -:Lead: Bernie Hackett -:Advisors: \A. Jesse Jiryu Davis, Samantha Ritter, Robert Stam, Jeff Yemin -:Status: Accepted -:Type: Standards -:Last Modified: January 29, 2018 -:Version: 1.10 - -.. contents:: - -Abstract -======== - -MongoDB deployments may offer more than one server that can service an -operation. This specification describes how MongoDB drivers and mongos shall -select a server for either read or write operations. It includes the definition -of a "read preference" document, configuration options, and algorithms for -selecting a server for different deployment topologies. - -Meta -==== - -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", -"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be -interpreted as described in `RFC 2119`_. - -.. _RFC 2119: https://www.ietf.org/rfc/rfc2119.txt - -Motivation for Change -===================== - -This specification builds upon the prior "Driver Read Preference" -specification, which had a number of omissions, flaws -or other deficiencies: - -#. Mandating features that implied monotonicity for situations where - monotonicity is not guaranteed - -#. Mandating features that are not supported by mongos - -#. Neglecting to specify a single, standard way to calculate average latency - times - -#. Specifying complex command-helper rules - -#. Omitting rules for applying read preferences to a single server or to - select among multiple mongos servers - -#. Omitting test cases for verification of spec compliance - -This revision addresses these problems as well as improving structure and -specificity. - -Additionally, it adds specifications for server selection more broadly: - -* Selection of a server for write operations - -* Server selection retry and timeout - -Specification -============= - -Scope and general requirements ------------------------------- - -This specification describes how MongoDB drivers and mongos select a server -for read and write operations, including commands, OP_QUERY, OP_INSERT, OP_UPDATE, -and OP_DELETE. For read operations, it describes how drivers and mongos -shall interpret a read preference document. - -This specification does not apply to OP_GET_MORE or OP_KILL_CURSORS -operations on cursors, which need to go to the same server that received an -OP_QUERY and returned a cursor ID. - -Drivers and mongos MUST conform to the semantics of this document, but SHOULD -use language-appropriate data models or variable names. - -This specification does not apply to commands issued for server monitoring or -authentication. - -Terms ------ - -**Available** - Describes a server that is believed to be reachable over the network and - able to respond to requests. A server of type Unknown or PossiblePrimary - is not available; other types are available. - -**Client** - Software that communicates with a MongoDB deployment. This includes both - drivers and mongos. - -**Candidate** - Describes servers in a deployment that enter the selection process, - determined by the read preference ``mode`` parameter and the servers' type. - Depending on the ``mode``, candidate servers might only include secondaries - or might apply to all servers in the deployment. - -**Deployment** - One or more servers that collectively provide access to a single logical - set of MongoDB databases. - -**Command** - An OP_QUERY operation targeting the '$cmd' collection namespace. - -**Direct connection** - A driver connection mode that sends all database operations to a single - server without regard for type. - -.. _eligible: - -**Eligible** - Describes candidate servers that also meet the criteria specified by the - ``tag_sets`` and ``maxStalenessSeconds`` read preference parameters. - -**Immediate topology check** - For a multi-threaded or asynchronous client, this means waking all - server monitors for an immediate check. For a single-threaded client, - this means a (blocking) scan of all servers. - -**Latency window** - When choosing between several suitable servers, the latency window is the - range of acceptable RTTs from the shortest RTT to the shortest RTT plus the - local threshold. E.g. if the shortest RTT is 15ms and the local threshold - is 200ms, then the latency window ranges from 15ms - 215ms. - -**Local threshold** - The maximum acceptable difference in milliseconds between the shortest RTT - and the longest RTT of servers suitable to be selected. - -**Mode** - One of several enumerated values used as part of a read preference, defining - which server types are candidates for reads and the semantics for choosing a - specific one. - -**Primary** - Describes a server of type RSPrimary. - -**Query** - An OP_QUERY operation targeting a regular (non '$cmd') collection namespace. - -**Read preference** - The parameters describing which servers in a deployment can receive - read operations, including ``mode``, ``tag_sets``, and ``maxStalenessSeconds``. - -**RS** - Abbreviation for "replica set". - -**RTT** - Abbreviation for "round trip time". - -**Round trip time** - The time in milliseconds to execute an ``ismaster`` command and - receive a response for a given server. This spec differentiates between - the RTT of a single ``ismaster`` command and a server's *average* RTT over - several such commands. - -**Secondary** - A server of type RSSecondary. - -**Staleness** - A worst-case estimate of how far a secondary's replication lags behind the primary's last write. - -**Server** - A mongod or mongos process. - -**Server selection** - The process by which a server is chosen for a database operation out of all - potential servers in a deployment. - -**Server type** - An enumerated type indicating whether a server is up or down, whether it is - a mongod or mongos, whether it belongs to a replica set and, if so, what - role it serves in the replica set. See the `Server Discovery and Monitoring`_ - spec for more details. - -**Suitable** - Describes a server that meets all specified criteria for a read or write - operation. - -**Tag** - A single key/value pair describing either (1) a user-specified - characteristic of a replica set member or (2) a desired characteristic for - the target of a read operation. The key and value have no semantic meaning - to the driver; they are arbitrary user choices. - -**Tag set** - A document of zero or more tags. Each member of a replica set can be - configured with zero or one tag set. - -**Tag set list** - A list of zero or more tag sets. A read preference might have a tag set list - used for selecting servers. - -**Topology** - The state of a deployment, including its type, which servers are - members, and the server types of members. - -**Topology type** - An enumerated type indicating the semantics for monitoring servers and - selecting servers for database operations. See the `Server Discovery and - Monitoring`_ spec for more details. - -Assumptions ------------ - -1. Unless they explicitly override these priorities, we assume our users - prefer their applications to be, in order: - - - Predictable: the behavior of the application should not change based on - the deployment type, whether single mongod, replica set or sharded cluster. - - - Resilient: applications will adapt to topology changes, if possible, - without raising errors or requiring manual reconfiguration. - - - Low-latency: all else being equal, faster responses to queries and writes - are preferable. - -2. Clients know the state of a deployment based on some form of ongoing - monitoring, following the rules defined in the `Server Discovery and - Monitoring`_ spec. - - - They know which members are up or down, what their tag sets are, and - their types. - - - They know average round trip times to each available member. - - - They detect reconfiguration and the addition or removal of members. - -3. The state of a deployment could change at any time, in between any network - interaction. - - - Servers might or might not be reachable; they can change type at any - time, whether due to partitions, elections, or misconfiguration. - - - Data rollbacks could occur at any time. - -MongoClient Configuration -------------------------- - -Selecting a server requires the following client-level configuration -options: - -localThresholdMS -~~~~~~~~~~~~~~~~~~ - -This defines the size of the latency window for selecting among multiple -suitable servers. The default is 15 (milliseconds). It MUST be configurable at -the client level. It MUST NOT be configurable at the level of a database -object, collection object, or at the level of an individual query. - -In the prior read preference specification, ``localThresholdMS`` was called -``secondaryAcceptableLatencyMS`` by drivers. Drivers MUST support the new -name for consistency, but MAY continue to support the legacy name to avoid -a backward-breaking change. - -mongos currently uses ``localThreshold`` and MAY continue to do so. - -serverSelectionTimeoutMS -~~~~~~~~~~~~~~~~~~~~~~~~ - -This defines how long to block for server selection before throwing an -exception. The default is 30,000 (milliseconds). It MUST be configurable at -the client level. It MUST NOT be configurable at the level of a database -object, collection object, or at the level of an individual query. - -This default value was chosen to be sufficient for a typical server primary -election to complete. As the server improves the speed of elections, this -number may be revised downward. - -Users that can tolerate long delays for server selection when the topology -is in flux can set this higher. Users that want to "fail fast" when the -topology is in flux can set this to a small number. - -A serverSelectionTimeoutMS of zero MAY have special meaning in some drivers; -zero's meaning is not defined in this spec, but all drivers SHOULD document -the meaning of zero. - -serverSelectionTryOnce -~~~~~~~~~~~~~~~~~~~~~~ - -Single-threaded drivers MUST provide a "serverSelectionTryOnce" mode, -in which the driver scans the topology exactly once after server selection fails, -then either selects a server or raises an error. - -The serverSelectionTryOnce option MUST be true by default. -If it is set false, then the driver repeatedly searches for an appropriate server -for up to serverSelectionTimeoutMS milliseconds -(pausing `minHeartbeatFrequencyMS -`_ -between attempts, as required by the `Server Discovery and Monitoring`_ -spec). - -Users of single-threaded drivers MUST be able to control this mode in one or -both of these ways: - -* In code, pass true or false for an option called serverSelectionTryOnce, - spelled idiomatically for the language, to the MongoClient constructor. -* Include "serverSelectionTryOnce=true" or "serverSelectionTryOnce=false" - in the URI. The URI option is spelled the same for all drivers. - -Conflicting usages of the URI option and the symbol is an error. - -Multi-threaded drivers MUST NOT provide this mode. -(See `single-threaded server selection implementation`_ -and the rationale for a `"try once" mode`_.) - -heartbeatFrequencyMS -~~~~~~~~~~~~~~~~~~~~ - -This controls when topology updates are scheduled. -See `heartbeatFrequencyMS`_ in the `Server Discovery and Monitoring`_ spec for details. - -socketCheckIntervalMS -~~~~~~~~~~~~~~~~~~~~~ - -Only for single-threaded drivers. - -The default socketCheckIntervalMS MUST be 5000 (5 seconds), and it MAY be -configurable. If socket has been idle for at least this long, it must be -checked before being used again. - -See `checking an idle socket after socketCheckIntervalMS`_ and `what is the -purpose of socketCheckIntervalMS?`_. - -idleWritePeriodMS -~~~~~~~~~~~~~~~~~ - -A constant, how often an idle primary writes a no-op to the oplog. -See `idleWritePeriodMS`_ in the `Max Staleness`_ spec for details. - -smallestMaxStalenessSeconds -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A constant, 90 seconds. See "Smallest allowed value for maxStalenessSeconds" -in the Max Staleness Spec. - -serverSelector -~~~~~~~~~~~~~~ - -Implementations MAY allow configuration of an optional, application-provided function -that augments the server selection rules. The function takes as a parameter a list -of server descriptions representing the suitable servers for the read or write operation, -and returns a list of server descriptions that should still be considered suitable. - -Read Preference ---------------- - -A read preference determines which servers are considered suitable for read -operations. Read preferences are interpreted differently based on topology -type. See topology-type-specific server selection rules for details. - -When no servers are suitable, the selection might be retried or will eventually -fail following the rules described in the `Rules for server selection`_ -section. - -Components of a read preference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A read preference consists of a ``mode`` and optional -``tag_sets`` and ``maxStalenessSeconds``. The ``mode`` prioritizes between primaries and -secondaries to produce either a single suitable server or a list of candidate -servers. If ``tag_sets`` and ``maxStalenessSeconds`` are set, they determine -which candidate servers are eligible for selection. - -The default ``mode`` is 'primary'. The default ``tag_sets`` -is a list with an empty tag set: ``[{}]``. The default ``maxStalenessSeconds`` -is -1 or null, depending on the language. - -Each is explained in greater detail below. - -mode -```` - -For a deployment with topology type ReplicaSetWithPrimary or -ReplicaSetNoPrimary, the ``mode`` parameter controls whether primaries or -secondaries are deemed suitable. Topology types Single and Sharded have -different selection criteria and are described elsewhere. - -Clients MUST support these modes: - -**primary** - Only an available primary is suitable. - -**secondary** - All secondaries (and *only* secondaries) are candidates, but only - `eligible`_ candidates (i.e. after applying ``tag_sets`` and ``maxStalenessSeconds``) are suitable. - -**primaryPreferred** - If a primary is available, only the primary is suitable. Otherwise, - all secondaries are candidates, but only eligible secondaries are suitable. - -**secondaryPreferred** - All secondaries are candidates. If there is at least one eligible - secondary, only eligible secondaries are suitable. Otherwise, when there - are no eligible secondaries, the primary is suitable. - -**nearest** - The primary and all secondaries are candidates, but only eligible - candidates are suitable. - -*Note on other server types*: The `Server Discovery and Monitoring`_ spec defines -several other server types that could appear in a replica set. Such types are never -candidates, eligible or suitable. - -.. _algorithm for filtering by staleness: - -maxStalenessSeconds -``````````````````` - -The maximum replication lag, in wall clock time, that a secondary can suffer -and still be eligible. - -The default is no maximum staleness. - -A ``maxStalenessSeconds`` of -1 MUST mean "no maximum". Drivers are also free to use -None, null, or other representations of "no value" to represent "no max staleness". - -Drivers MUST raise an error if ``maxStalenessSeconds`` is a positive number -and the ``mode`` field is 'primary'. - -A driver MUST raise an error -if the TopologyType is ReplicaSetWithPrimary or ReplicaSetNoPrimary -and either of these conditions is false:: - - maxStalenessSeconds * 1000 >= heartbeatFrequencyMS + idleWritePeriodMS - maxStalenessSeconds >= smallestMaxStalenessSeconds - -``heartbeatFrequencyMS`` is defined in the `Server Discovery and Monitoring`_ spec, -and ``idleWritePeriodMS`` is defined to be 10 seconds in the `Max Staleness`_ spec. - -See "Smallest allowed value for maxStalenessSeconds" in the Max Staleness Spec. - -mongos MUST reject a read with ``maxStalenessSeconds`` provided and a ``mode`` of 'primary'. - -mongos MUST reject a read with ``maxStalenessSeconds`` that is not a positive integer. - -mongos MUST reject a read if ``maxStalenessSeconds`` is less than smallestMaxStalenessSeconds, -with error code 160 (SERVER-24421). - -During server selection, -drivers (but not mongos) MUST raise an error if ``maxStalenessSeconds`` is a positive number, -and any server's ``maxWireVersion`` is less than 5. [#]_ - -After filtering servers according to ``mode``, and before filtering with ``tag_sets``, -eligibility MUST be determined from ``maxStalenessSeconds`` as follows: - -- If ``maxStalenessSeconds`` is not a positive number, then all servers are eligible. - -- Otherwise, calculate staleness. Non-secondary servers (including Mongos - servers) have zero staleness. - If TopologyType is ReplicaSetWithPrimary, - a secondary's staleness is calculated using its ServerDescription "S" - and the primary's ServerDescription "P":: - - (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate) + heartbeatFrequencyMS - - (All datetime units are in milliseconds.) - - If TopologyType is ReplicaSetNoPrimary, - a secondary's staleness is calculated using its ServerDescription "S" - and the ServerDescription of the secondary with the greatest lastWriteDate, - "SMax":: - - SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS - - Servers with staleness less than or equal to ``maxStalenessSeconds`` are eligible. - -See the Max Staleness Spec for overall description and justification of this -feature. - -.. _algorithm for filtering by tag_sets: - -tag_sets -```````` - -The read preference ``tag_sets`` parameter is an ordered list of tag sets used -to restrict the eligibility of servers, such as for data center awareness. - -Clients MUST raise an error if a non-empty tag set is given in ``tag_sets`` -and the ``mode`` field is 'primary'. - -A read preference tag set (``T``) matches a server tag set (``S``) – -or equivalently a server tag set (``S``) matches a read preference -tag set (``T``) — if ``T`` is a subset of ``S`` (i.e. ``T ⊆ S``). - -For example, the read preference tag set "\{ dc: 'ny', rack: 2 \}" matches a -secondary server with tag set "\{ dc: 'ny', rack: 2, size: 'large' \}". - -A tag set that is an empty document matches any server, because the empty -tag set is a subset of any tag set. This means the default ``tag_sets`` -parameter (``[{}]``) matches all servers. - -Tag sets are applied after filtering servers by ``mode`` and ``maxStalenessSeconds``, -and before selecting one server within the latency window. - -Eligibility MUST be determined from ``tag_sets`` as follows: - -- If the ``tag_sets`` list is empty then all candidate servers are eligible - servers. (Note, the default of ``[{}]`` means an empty list probably won't - often be seen, but if the client does not forbid an empty list, this rule - MUST be implemented to handle that case.) - -- If the ``tag_sets`` list is not empty, then tag sets are tried in order until - a tag set matches at least one candidate server. All candidate servers - matching that tag set are eligible servers. Subsequent tag sets in the list - are ignored. - -- If the ``tag_sets`` list is not empty and no tag set in the list matches any - candidate server, no servers are eligible servers. - -Read preference configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MUST allow users to configure a default read preference on a -``MongoClient`` object. Drivers MAY allow users to configure a default read -preference on a ``Database`` or ``Collection`` object. - -A read preference MAY be specified as an object, document or individual -``mode``, ``tag_sets``, and ``maxStalenessSeconds`` parameters, -depending on what is most idiomatic for the language. - -If more than one object has a default read preference, the default of the most -specific object takes precedence. I.e. ``Collection`` is preferred over -``Database``, which is preferred over ``MongoClient``. - -Drivers MAY allow users to set a read preference on queries on a per-operation -basis similar to how ``addSpecial``, ``hint``, or ``batchSize`` are set. E.g., -in Python:: - - db.collection.find({}, read_preference=ReadPreference.SECONDARY) - db.collection.find( - {}, - read_preference=ReadPreference.NEAREST, - tag_sets=[{'dc': 'ny'}], - maxStalenessSeconds=120) - -If a driver API allows users to potentially set both the legacy ``slaveOK`` -configuration option and a default read preference configuration option, -passing a value for both MUST be an error. (See `Use of slaveOk`_ for the two -uses of ``slaveOK``.) - -Passing read preference to mongos -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If a server of type Mongos is selected for a read operation, the read -preference is passed to the selected mongos through the use of the -``slaveOK`` wire protocol flag, the ``$readPreference`` query -modifier or both, according to the following rules. - -If the read preference contains **only** a ``mode`` parameter and the mode is -'primary' or 'secondaryPreferred', for maximum backwards compatibility with -older versions of mongos, drivers MUST only use the value of the ``slaveOK`` -wire protocol flag (i.e. set or unset) to indicate the desired read preference -and MUST NOT use a ``$readPreference`` query modifier. - -Therefore, when sending queries to a mongos, the following rules apply: - - - For mode 'primary', drivers MUST NOT set the ``slaveOK`` wire protocol flag - and MUST NOT use ``$readPreference`` - - - For mode 'secondary', drivers MUST set the ``slaveOK`` wire protocol flag - and MUST also use ``$readPreference`` - - - For mode 'primaryPreferred', drivers MUST set the ``slaveOK`` wire protocol flag - and MUST also use ``$readPreference`` - - - For mode 'secondaryPreferred', drivers MUST set the ``slaveOK`` wire protocol flag. - If the read preference contains a non-empty ``tag_sets`` parameter, - or ``maxStalenessSeconds`` is a positive integer, drivers MUST - use ``$readPreference``; otherwise, drivers MUST NOT use ``$readPreference`` - - - For mode 'nearest', drivers MUST set the ``slaveOK`` wire protocol flag - and MUST also use ``$readPreference`` - -The ``$readPreference`` query modifier sends the read preference as part of the -query. The read preference fields ``tag_sets`` is represented in a ``$readPreference`` -document using the field name ``tags``. - -When sending a read operation via OP_QUERY and any ``$`` modifier is used, including the ``$readPreference`` modifier, -the query MUST be provided using the ``$query`` modifier like so:: - - { - $query: { - field1: 'query_value', - field2: 'another_query_value' - }, - $readPreference: { - mode: 'secondary', - tags: [ { 'dc': 'ny' } ], - maxStalenessSeconds: 120 - } - } - -A valid ``$readPreference`` document for mongos has the following requirements: - -1. The ``mode`` field MUST be present exactly once with the mode represented - in camel case: - - - 'primary' - - 'secondary' - - 'primaryPreferred' - - 'secondaryPreferred' - - 'nearest' - -2. If the ``mode`` field is "primary", the ``tags`` and ``maxStalenessSeconds`` fields MUST be absent. - - Otherwise, for other ``mode`` values, the ``tags`` field MUST either be - absent or be present exactly once and have an array value containing at - least one document. It MUST contain only documents, no other type. - - The ``maxStalenessSeconds`` field MUST be either be absent or be present - exactly once with an integer value. - -Mongos receiving a query with ``$readPreference`` SHOULD validate the -``mode``, ``tags``, and ``maxStalenessSeconds`` fields according to rules 1 and 2 above, -but SHOULD ignore unrecognized fields for -forward-compatibility rather than throwing an error. - -Use of read preferences with commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Because some commands are used for writes, deployment-changes or other -state-changing side-effects, the use of read preference by a driver depends on -the command and how it is invoked: - -1. Write commands: ``insert``, ``update``, ``delete``, ``findAndModify`` - - Write commands are considered write operations and MUST follow the - corresponding `Rules for server selection`_ for each topology type. - -2. Generic command method: typically ``command`` or ``runCommand`` - - The generic command method MUST act as a read operation for the purposes of - server selection. - - The generic command method has a default read preference of ``mode`` - 'primary'. The generic command method MUST ignore any default read - preference from client, database or collection configuration. The generic - command method SHOULD allow an optional read preference argument. - - If an explicit read preference argument is provided as part of the generic - command method call, it MUST be used for server selection, regardless of - the name of the command. It is up to the user to use an appropriate read - preference, e.g. not calling ``renameCollection`` with a ``mode`` of - 'secondary'. - - N.B.: "used for server selection" does not supercede rules for server - selection on "Standalone" topologies, which ignore any requested read - preference. - -3. Command-specific helper: methods that wrap database commands, like - ``count``, ``distinct``, ``listCollections`` or ``renameCollection``. - - Command-specific helpers MUST act as read operations for the purposes of - server selection, with read preference rules defined by the following three - categories of commands: - - - "must-use-primary": these commands have state-modifying effects and will - only succeed on a primary. An example is ``renameCollection``. - - These command-specific helpers MUST use a read preference ``mode`` of - 'primary', MUST NOT take a read preference argument and MUST ignore any - default read preference from client, database or collection - configuration. Languages with dynamic argument lists MUST throw an error - if a read preference is provided as an argument. - - Clients SHOULD rely on the server to return a "not master" or other error - if the command is "must-use-primary". Clients MAY raise an exception - before sending the command if the topology type is Single and the server - type is not "Standalone", "RSPrimary" or "Mongos", but the identification - of the set of 'must-use-primary' commands is out of scope for this - specification. - - - "should-use-primary": these commands are intended to be run on a primary, - but would succeed -- albeit with possibly stale data -- when run against - a secondary. An example is ``listCollections``. - - These command-specific helpers MUST use a read preference ``mode`` of - 'primary', MUST NOT take a read preference argument and MUST ignore any - default read preference from client, database or collection - configuration. Languages with dynamic argument lists MUST throw an error - if a read preference is provided as an argument. - - Clients MUST NOT raise an exception if the topology type is Single. - - - "may-use-secondary": these commands run against primaries or secondaries, - according to users' read preferences. They are sometimes called - "query-like" commands. - - The current list of "may-use-secondary" commands includes: - - - aggregate (without $out specified) - - collStats - - count - - dbStats - - distinct - - find - - geoNear - - geoSearch - - group - - mapReduce, mapreduce (with out: {inline: 1}) - - parallelCollectionScan - - Associated command-specific helpers SHOULD take a read preference - argument and otherwise MUST use the default read preference from client, - database or collection configuration. - - The aggregate command succeeds on a secondary unless $out is specified. - When $out is specified, the command must be treated as a write command. - If the read preference is not 'primary', the driver SHOULD warn if $out - is use. - - If a client provides a specific helper for inline mapreduce, then it is - "may-use-secondary" and the *regular* mapreduce helper is "must use - primary". Otherwise mapreduce behaves like the aggregate helper: it is the - user's responsibility to specify {inline: 1} when running mapreduce on a - secondary. - - New command-specific helpers implemented in the future will be considered - "must-use-primary", "should-use-primary" or "may-use-secondary" according - to the specifications for those future commands. Command helper - specifications SHOULD use those terms for clarity. - -Rules for server selection --------------------------- - -Server selection is a process which takes an operation type (read or write), a -ClusterDescription, and optionally a read preference and, on success, returns a -ServerDescription for an operation of the given type. - -Server selection varies depending on whether a client is -multi-threaded/asynchronous or single-threaded because a single-threaded -client cannot rely on the topology state being updated in the background. - -Multi-threaded or asynchronous server selection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A driver that uses multi-threaded or asynchronous monitoring MUST unblock -waiting operations as soon as server selection completes, even if not all -servers have been checked by a monitor. Put differently, the client MUST NOT -block server selection while waiting for server discovery to finish. - -For example, if the client is discovering a replica set and the application -attempts a read operation with mode 'primaryPreferred', the operation MUST -proceed immediately if a suitable secondary is found, rather than blocking -until the client has checked all members and possibly discovered a primary. - -The number of threads allowed to wait for server selection SHOULD be either -(a) the same as the number of threads allowed to wait for a connection from -a pool; or (b) governed by a global or client-wide limit on number of -waiting threads, depending on how resource limits are implemented by a -driver. - -For multi-threaded clients, the server selection algorithm is -as follows: - -1. Record the server selection start time - -2. If the topology wire version is invalid, raise an error - -3. Find suitable servers by topology type and operation type - -4. Filter the suitable servers by calling the optional, application-provided server - selector. - -5. If there are any suitable servers, choose one at random from those - within the latency window and return it; otherwise, continue to the next step - -6. Request an immediate topology check, then block the server selection - thread until the topology changes or until the server selection - timeout has elapsed - -7. If more than ``serverSelectionTimeoutMS`` milliseconds have elapsed since - the selection start time, raise a `server selection error`_ - -8. Goto Step #2 - -Single-threaded server selection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Single-threaded drivers do not monitor the topology in the background. -Instead, they MUST periodically update the topology during server selection -as described below. - -When ``serverSelectionTryOnce`` is true, ``serverSelectionTimeoutMS`` has -no effect; a single immediate topology check will be done if the topology -starts stale or if the first selection attempt fails. - -When ``serverSelectionTryOnce`` is false, then the server selection loops -until a server is successfully selected or until -``serverSelectionTimeoutMS`` is exceeded. - -Therefore, for single-threaded clients, the server selection algorithm is -as follows: - -1. Record the server selection start time - -2. Record the maximum time as start time plus ``serverSelectionTimeoutMS`` - -3. If the topology has not been scanned in ``heartbeatFrequencyMS`` - milliseconds, mark the topology stale - -4. If the topology is stale, proceed as follows: - - - record the target scan time as last scan time plus ``minHeartBeatFrequencyMS`` - - - if `serverSelectionTryOnce`_ is false and the target scan time would - exceed the maximum time, raise a `server selection error`_ - - - if the current time is less than the target scan time, sleep until - the target scan time - - - do a blocking immediate topology check (which must also update the - last scan time and mark the topology as no longer stale) - -5. If the topology wire version is invalid, raise an error - -6. Find suitable servers by topology type and operation type - -7. Filter the suitable servers by calling the optional, application-provided - server selector. - -8. If there are any suitable servers, choose one at random from those - within the latency window and return it; otherwise, mark the topology - stale and continue to step #8 - -9. If `serverSelectionTryOnce`_ is true and the last scan time is newer than - the selection start time, raise a `server selection error`_; otherwise, - goto Step #4 - -10. If the current time exceeds the maximum time, raise a - `server selection error`_ - -11. Goto Step #4 - -Before using a socket to the selected server, drivers MUST check whether -the socket has been used in `socketCheckIntervalMS`_ milliseconds. If the -socket has been idle for longer, the driver MUST update the -ServerDescription for the selected server. After updating, if the server -is no longer suitable, the driver MUST repeat the server selection -algorithm and select a new server. - -Because single-threaded selection can do a blocking immediate check, -``serverSelectionTimeoutMS`` is not a hard deadline. The actual -maximum server selection time for any given request can vary from -``serverSelectionTimeoutMS`` minus ``minHeartbeatFrequencyMS`` to -``serverSelectionTimeoutMS`` plus the time required for a blocking scan. - -Single-threaded drivers MUST document that when ``serverSelectionTryOne`` -is true, selection may take up to the time required for a blocking scan, -and when ``serverSelectionTryOne`` is false, selection may take up to -``serverSelectionTimeoutMS`` plus the time required for a blocking scan. - -Topology type: Unknown -~~~~~~~~~~~~~~~~~~~~~~ - -When a deployment has topology type "Unknown", no servers are suitable for read or write -operations. - -Topology type: Single -~~~~~~~~~~~~~~~~~~~~~ - -A deployment of topology type Single contains only a single server of any type. -Topology type Single signifies a direct connection intended to receive all read -and write operations. - -Therefore, read preference is ignored during server selection with topology -type Single. The single server is always suitable for reads if it is -available. Depending on server type, the read preference is communicated -to the server differently: - -- Type Mongos: the read preference is sent to the server using the rules - for `Passing read preference to mongos`_. - -- For all other types, using OP_QUERY: clients MUST always set the ``slaveOK`` wire - protocol flag on reads to ensure that any server type can handle the - request. - -- For all other types, using OP_MSG: If no read preference is configured by the - application, or if the application read preference is Primary, then - $readPreference MUST be set to ``{ "mode": "primaryPreferred" }`` to ensure - that any server type can handle the request. - -The single server is always suitable for write operations if it is available. - -Topology types: ReplicaSetWithPrimary or ReplicaSetNoPrimary -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A deployment with topology type ReplicaSetWithPrimary or ReplicaSetNoPrimary -can have a mix of server types: RSPrimary (only in ReplicaSetWithPrimary), -RSSecondary, RSArbiter, RSOther, RSGhost, Unknown or PossiblePrimary. - -Read operations -``````````````` - -For the purpose of selecting a server for read operations, the same rules apply -to both ReplicaSetWithPrimary and ReplicaSetNoPrimary. - -To select from the topology a server that matches the user's Read Preference: - -If ``mode`` is 'primary', select the primary server. - -If ``mode`` is 'secondary' or 'nearest': - - #. Select all secondaries if ``mode`` is 'secondary', or all secondaries and - the primary if ``mode`` is 'nearest'. - #. From these, filter out servers staler than ``maxStalenessSeconds`` if it is a positive number. - #. From the remaining servers, select servers matching the ``tag_sets``. - #. From these, select one server within the latency window. - -(See `algorithm for filtering by staleness`_, `algorithm for filtering by -tag_sets`_, and `selecting servers within the latency window`_ for details -on each step, and `why is maxStalenessSeconds applied before tag_sets?`_.) - -If ``mode`` is 'secondaryPreferred', attempt the selection algorithm with -``mode`` 'secondary' and the user's ``maxStalenessSeconds`` and ``tag_sets``. If -no server matches, select the primary. - -If ``mode`` is 'primaryPreferred', select the primary if it is known, otherwise -attempt the selection algorithm with ``mode`` 'secondary' and the user's -``maxStalenessSeconds`` and ``tag_sets``. - -For all read preferences modes except 'primary', clients MUST set the ``slaveOK`` wire -protocol flag to ensure that any suitable server can handle the request. Clients -MUST NOT set the ``slaveOK`` wire protocol flag if the read preference mode is -'primary'. - -Write operations -```````````````` - -If the topology type is ReplicaSetWithPrimary, only an available primary is -suitable for write operations. - -If the topology type is ReplicaSetNoPrimary, no servers are suitable for write -operations. - -Topology type: Sharded -~~~~~~~~~~~~~~~~~~~~~~ - -A deployment of topology type Sharded contains one or more servers of type -Mongos or Unknown. - -For read operations, all servers of type Mongos are suitable; the ``mode``, -``tag_sets``, and ``maxStalenessSeconds`` read preference parameters are ignored for selecting a -server, but are passed through to mongos. See `Passing read preference to mongos`_. - -For write operations, all servers of type Mongos are suitable. - -If more than one mongos is suitable, drivers MUST randomly select a suitable -server within the latency window. - -Round Trip Times and the Latency Window ---------------------------------------- - -Calculation of Average Round Trip Times -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For every available server, clients MUST track the average RTT of server -monitoring ``ismaster`` commands. - -An Unknown server has no average RTT. When a server becomes unavailable, its -average RTT MUST be cleared. Clients MAY implement this idiomatically (e.g -nil, -1, etc.). - -When there is no average RTT for a server, the average RTT MUST be set equal to -the first RTT measurement (i.e. the first ``ismaster`` command after the -server becomes available). - -After the first measurement, average RTT MUST be computed using an -exponentially-weighted moving average formula, with a weighting factor -(``alpha``) of 0.2. If the prior average is denoted ``old_rtt``, then the new -average (``new_rtt``) is computed from a new RTT measurement (``x``) using the -following formula:: - - alpha = 0.2 - new_rtt = alpha * x + (1 - alpha) * old_rtt - -A weighting factor of 0.2 was chosen to put about 85% of the weight of the -average RTT on the 9 most recent observations. - -Selecting servers within the latency window -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Server selection results in a set of zero or more suitable servers. If more -than one server is suitable, a server MUST be selected randomly from among -those within the latency window. - -The ``localThresholdMS`` configuration parameter controls the size of the -latency window used to select a suitable server. - -The shortest average RTT from among suitable servers anchors one end of the -latency window (``A``). The other end is determined by adding -``localThresholdMS`` (``B = A + localThresholdMS``). - -A server MUST be selected randomly from among suitable servers that have an -average RTT (``RTT``) within the latency window (i.e. ``A ≤ RTT ≤ B``). - -In other words, the suitable server with the shortest average RTT is **always** -a possible choice. Other servers could be chosen if their average RTTs are no -more than ``localThresholdMS`` more than the shortest average RTT. - -Checking an Idle Socket After socketCheckIntervalMS ---------------------------------------------------- - -Only for single-threaded drivers. - -If a server is selected that has an existing connection that has been idle for -socketCheckIntervalMS, the driver MUST check the connection with the "ping" -command. If the ping succeeds, use the selected connection. If not, set the -server's type to Unknown and update the Topology Description according to the -Server Discovery and Monitoring Spec, and attempt **once** more to select a -server. - -The logic is expressed in this pseudocode. The algorithm for the "getServer" -function is suggested below, in `Single-threaded server selection -implementation`_:: - - def getConnection(criteria): - # Get a server for writes, or a server matching read prefs, by - # running the server selection algorithm. - server = getServer(criteria) - if not server: - throw server selection error - - connection = server.connection - if connection is NULL: - connect to server and return connection - else if connection has been idle < socketCheckIntervalMS: - return connection - else: - try: - use connection for "ping" command - return connection - except network error: - close connection - mark server Unknown and update Topology Description - - # Attempt *once* more to select. - server = getServer(criteria) - if not server: - throw server selection error - - connect to server and return connection - - -See `What is the purpose of socketCheckIntervalMS?`_. - -Requests and Pinning Deprecated -------------------------------- - -The prior read preference specification included the concept of a "request", -which pinned a server to a thread for subsequent, related reads. Requests -and pinning are now **deprecated**. See `What happened to pinning?`_ for -the rationale for this change. - -Drivers with an existing request API MAY continue to provide it for backwards -compatibility, but MUST document that pinning for the request does not -guarantee monotonic reads. - -Drivers MUST NOT automatically pin the client or a thread to a particular -server without an explicit ``start_request`` (or comparable) method call. - -Outside a legacy "request" API, drivers MUST use server selection for each -individual read operation. - -Reference Implementation -======================== - -The single-threaded reference implementation is the Perl master branch (work -towards v1.0.0). The multi-threaded reference implementation is TBD. - -Implementation Notes -==================== - -These are suggestions. As always, driver authors should balance cross-language -standardization with backwards compatibility and the idioms of their language. - -Modes ------ - -Modes ('primary', 'secondary', ...) are constants declared in whatever way is -idiomatic for the programming language. The constant values may be ints, -strings, or whatever. However, when attaching modes to ``$readPreference`` -camel case must be used as described above in `Passing read preference to -mongos`_. - -primaryPreferred and secondaryPreferred -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -'primaryPreferred' is equivalent to selecting a server with read preference mode -'primary' (without ``tag_sets`` or ``maxStalenessSeconds``), or, if that fails, falling back to selecting -with read preference mode 'secondary' (with ``tag_sets`` and ``maxStalenessSeconds``, if provided). - -'secondaryPreferred' is the inverse: selecting with mode 'secondary' (with -``tag_sets`` and ``maxStalenessSeconds``) and falling back to selecting with mode 'primary' (without -``tag_sets`` or ``maxStalenessSeconds``). - -Depending on the implementation, this may result in cleaner code. - -nearest -~~~~~~~ - -The term 'nearest' is unfortunate, as it implies a choice based on geographic -locality or absolute lowest latency, neither of which are true. - -Instead, and unlike the other read preference modes, 'nearest' does not favor -either primaries or secondaries; instead all servers are candidates and are -filtered by ``tag_sets`` and ``maxStalenessSeconds``. - -To always select the server with the lowest RTT, users should use mode 'nearest' -without ``tag_sets`` or ``maxStalenessSeconds`` and set ``localThresholdMS`` to zero. - -To distribute reads across all members evenly regardless of RTT, users should -use mode 'nearest' without ``tag_sets`` or ``maxStalenessSeconds`` and set ``localThresholdMS`` very high so -that all servers fall within the latency window. - -In both cases, ``tag_sets`` and ``maxStalenessSeconds`` could be used to further restrict the set of eligible -servers, if desired. - -Tag set lists -------------- - -Tag set lists can be configured in the driver in whatever way is natural for -the language. - -Multi-threaded server selection implementation ----------------------------------------------- - -The following example uses a single lock for clarity. Drivers are free to -implement whatever concurrency model best suits their design. - -Pseudocode for `multi-threaded or asynchronous server selection`_:: - - def getServer(criteria): - client.lock.acquire() - - now = gettime() - endTime = now + serverSelectionTimeoutMS - - while true: - # The topologyDescription keeps track of whether any server has an - # an invalid wire version range - if not topologyDescription.compatible: - client.lock.release() - throw invalid wire protocol range error with details - - if maxStalenessSeconds is set: - if any server's maxWireVersion < 5: - client.lock.release() - throw error - - if topologyDescription.type in (ReplicaSetWithPrimary, ReplicaSetNoPrimary): - if (maxStalenessSeconds * 1000 < heartbeatFrequencyMS + idleWritePeriodMS or - maxStalenessSeconds < smallestMaxStalenessSeconds): - client.lock.release() - throw error - - servers = all servers in topologyDescription matching criteria - - if serverSelector is not null and servers is not empty: - servers = serverSelector(servers) - - if servers is not empty: - in_window = servers within the latency window - selected = random entry from in_window - client.lock.release() - return selected - - request that all monitors check immediately - - # Wait for a new TopologyDescription. condition.wait() releases - # client.lock while waiting and reacquires it before returning. - # While a thread is waiting on client.condition, it is awakened - # early whenever a server check completes. - timeout_left = endTime - gettime() - client.condition.wait(timeout_left) - - if now after endTime: - client.lock.release() - throw server selection error - - -Single-threaded server selection implementation ------------------------------------------------ - -Pseudocode for `single-threaded server selection`_:: - - def getServer(criteria): - startTime = gettime() - loopEndTime = startTime - maxTime = startTime + serverSelectionTimeoutMS/1000 - nextUpdateTime = topologyDescription.lastUpdateTime - + heartbeatFrequencyMS/1000: - - if nextUpdateTime < startTime: - topologyDescription.stale = true - - while true: - - if topologyDescription.stale: - scanReadyTime = topologyDescription.lastUpdateTime - + minHeartbeatFrequencyMS/1000 - - if ((not serverSelectionTryOnce) && (scanReadyTime > maxTime)): - throw server selection error with details - - # using loopEndTime below is a proxy for "now" but avoids - # the overhead of another gettime() call - sleepTime = scanReadyTime - loopEndTime - - if sleepTime > 0: - sleep sleepTime - - rescan all servers - topologyDescription.lastupdateTime = gettime() - topologyDescription.stale = false - - # topologyDescription keeps a record of whether any - # server has an incompatible wire version range - if not topologyDescription.compatible: - topologyDescription.stale = true - throw invalid wire version range error with details - - if maxStalenessSeconds is set: - if any server's maxWireVersion < 5: - throw error - - if topologyDescription.type in (ReplicaSetWithPrimary, ReplicaSetNoPrimary): - if (maxStalenessSeconds * 1000 < heartbeatFrequencyMS + idleWritePeriodMS or - maxStalenessSeconds < smallestMaxStalenessSeconds): - throw error - - servers = all servers in topologyDescription matching criteria - - if serverSelector is not null and servers is not empty: - servers = serverSelector(servers) - - if servers is not empty: - in_window = servers within the latency window - return random entry from in_window - else: - topologyDescription.stale = true - - loopEndTime = gettime() - - if serverSelectionTryOnce: - if topologyDescription.lastUpdateTime > startTime: - throw server selection error with details - else if loopEndTime > maxTime: - throw server selection error with details - -.. _server selection error: - -Server Selection Errors ------------------------ - -Drivers should use server descriptions and their error attributes (if set) to -return useful error messages. - -For example, when there are no members matching the ReadPreference: - -- "No server available for query with ReadPreference primary" -- "No server available for query with ReadPreference secondary" -- "No server available for query with ReadPreference " + mode + ", tag set list " + tag_sets + ", and ``maxStalenessSeconds`` " + maxStalenessSeconds - -Or, if authentication failed: - -- "Authentication failed: [specific error message]" - -Here is a sketch of some pseudocode for handling error reporting when errors -could be different across servers:: - - if there are any available servers: - error_message = "No servers are suitable for " + criteria - else if all ServerDescriptions' errors are the same: - error_message = a ServerDescription.error value - else: - error_message = ', '.join(all ServerDescriptions' errors) - -Use of slaveOk --------------- - -There are two usages of ``slaveOK``: - -1. A driver query parameter that predated read preference modes and tag - set lists. - -2. A wire protocol flag on OP_QUERY operations - - -Using ``slaveOk`` as a query parameter is deprecated. Until it is removed, -``slaveOk`` used as a method argument or query option is considered -equivalent to a read preference ``mode`` of 'secondaryPreferred' - -The ``slaveOk`` wire protocol flag remains in the wire protocol and drivers -set this bit for each topology type as described in the specification -above. - -Cursors -------- - -Cursor operations OP_GET_MORE and OP_KILL_CURSOR do not go through the server -selection process. Cursor operations must be sent to the original server that -received the query and sent the OP_REPLY. For exhaust cursors, the same socket -must be used for OP_GET_MORE until the cursor is exhausted. - -The 'text' command and mongos ------------------------------ - -*Note*: As of MongoDB 2.6, mongos doesn't distribute the "text" command to -secondaries, see SERVER-10947_. - -However, the "text" command is deprecated in 2.6, so this command-specific -helper may become deprecated before this is fixed. - -.. _SERVER-10947: https://jira.mongodb.org/browse/SERVER-10947 - -Test Plan -========= - -The server selection test plan is given in a separate document that -describes the tests and supporting data files: `Server Selection Tests`_ - -.. _Server Selection Tests: https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection-tests.rst - -Design Rationale -================ - -Use of topology types ---------------------- - -The prior version of the read preference spec had only a loose definition of -server or topology types. The `Server Discovery and Monitoring`_ spec defines these terms -explicitly and they are used here for consistency and clarity. - -Consistency with mongos ------------------------ - -In order to ensure that behavior is consistent regardless of topology type, -read preference behaviors are limited to those that mongos can proxy. - -For example, mongos ignores read preference 'secondary' when a shard consists of -a single server. Therefore, this spec calls for topology type Single to ignore -read preferences for consistency. - -The spec has been written with the intention that it can apply to both drivers -and mongos and the term "client" has been used when behaviors should apply to -both. Behaviors that are specific to drivers are largely limited to those -for communicating with a mongos. - -New localThresholdMS configuration option name ------------------------------------------------- - -Because this does not apply **only** to secondaries and does not limit absolute -latency, the name ``secondaryAcceptableLatencyMS`` is misleading. - -The mongos name ``localThreshold`` misleads because it has nothing to do with -locality. It also doesn't include the ``MS`` units suffix for consistency with -other time-related configuration options. - -However, given a choice between the two, ``localThreshold`` is a more general -term. For drivers, we add the ``MS`` suffix for clarity about units and -consistency with other configuration options. - -Random selection within the latency window ------------------------------------------- - -When more than one server is judged to be suitable, the spec calls for random -selection to ensure a fair distribution of work among servers within the -latency window. - -It would be hard to ensure a fair round-robin approach given the potential for -servers to come and go. Making newly available servers either first or last -could lead to unbalanced work. Random selection has a better fairness -guarantee and keeps the design simpler. - -The slaveOK wire protocol flag ------------------------------- - -In server selection, there is a race condition that could exist between what -a selected server type is believed to be and what it actually is. - -The ``slaveOK`` wire protocol flag solves the race problem by communicating -to the server whether a secondary is acceptable. The server knows its type -and can return a "not master" error if ``slaveOK`` is false and the server -is a secondary. - -However, because topology type Single is used for direct connections, we want -read operations to succeed even against a secondary, so the ``slaveOK`` wire -protocol flag must be sent to mongods with topology type Single. - -(If the server type is Mongos, follow the rules for `passing read preference to -mongos`_, even for topology type Single.) - -General command method going to primary ---------------------------------------- - -The list of commands that can go to secondaries changes over time and depends -not just on the command but on parameters. For example, the ``mapReduce`` -command may or may not be able to be run on secondaries depending on the value -of the ``out`` parameter. - -It significantly simplifies implementation for the general command method -always to go to the primary unless a explicit read preference is set and rely -on users of the general command method to provide a read preference appropriate -to the command. - -The command-specific helpers will need to implement a check of read preferences -against the semantics of the command and its parameters, but keeping this logic -close to the command rather than in a generic method is a better design than -either delegating this check to the generic method, duplicating the logic in -the generic method, or coupling both to another validation method. - -Average round trip time calculation ------------------------------------ - -Using an exponentially-weighted moving average avoids having to store and -rotate an arbitrary number of RTT observations. All observations count towards -the average. The weighting makes recent observations count more heavily while -smoothing volatility. - -Verbose errors --------------- - -Error messages should be sufficiently verbose to allow users and/or support -engineers to determine the reasons for server selection failures from log -or other error messages. - -"Try once" mode ---------------- - -Single-threaded drivers in languages like PHP and Perl are typically deployed -as many processes per application server. Each process must independently -discover and monitor the MongoDB deployment. - -When no suitable server is available (due to a partition or misconfiguration), -it is better for each request to fail as soon as its process detects a -problem, instead of waiting and retrying to see if the deployment recovers. - -Minimizing response latency is important for maximizing request-handling -capacity and for user experience (e.g. a quick fail message instead of a slow -web page). - -However, when a request arrives and the topology information is already stale, -or no suitable server is known, -making a single attempt to update the topology to service the request is -acceptable. - -A user of a single-threaded driver who prefers resilience in the face of topology problems, -rather than short response times, -can turn the "try once" mode off. -Then driver rescans the topology every minHeartbeatFrequencyMS -until a suitable server is found or the serverSelectionTimeoutMS expires. - -What is the purpose of socketCheckIntervalMS? ---------------------------------------------- - -Single-threaded clients need to make a compromise: if they check servers too -frequently it slows down regular operations, but if they check too rarely they -cannot proactively avoid errors. - -Errors are more disruptive for single-threaded clients than for multi-threaded. -If one thread in a multi-threaded process encounters an error, it warns the -other threads not to use the disconnected server. But single-threaded clients -are deployed as many independent processes per application server, and each -process must throw an error until all have discovered that a server is down. - -The compromise specified here balances the cost of frequent checks against the -disruption of many errors. The client preemptively checks individual sockets -that have not been used in the last `socketCheckIntervalMS`_, which is more -frequent by default than `heartbeatFrequencyMS` defined in the Server Discovery -and Monitoring Spec. - -The client checks the socket with a "ping" command, rather than "ismaster", -because it is not checking the server's full state as in the Server Discovery -and Monitoring Spec, it is only verifying that the connection is still open. We -might also consider a `select` or `poll` call to check if the socket layer -considers the socket closed, without requiring a round-trip to the server. -However, this technique usually will not detect an uncleanly shutdown server or -a network outage. - -Backwards Compatibility -======================= - -In general, backwards breaking changes have been made in the name of -consistency with mongos and avoiding misleading users about monotonicity. - -* Features removed: - - - Automatic pinning (see `What happened to pinning?`_) - - - Auto retry (replaced by the general server selection algorithm) - - - mongos "high availability" mode (effectively, mongos pinning) - -* Other features and behaviors have changed explicitly - - - Ignoring read preferences for topology type Single - - - Default read preference for the generic command method - -* Changes with grandfather clauses - - - Alternate names for ``localThresholdMS`` - - - Pinning for legacy request APIs - -* Internal changes with little user-visibility - - - Clarifying calculation of average RTT - -Questions and Answers -===================== - -What happened to pinning? -------------------------- - -The prior read preference spec, which was implemented in the versions of the -drivers and mongos released concomitantly with MongoDB 2.2, stated that a -thread / client should remain pinned to an RS member as long as that member -matched the current mode, tags, and acceptable latency. This increased the -odds that reads would be monotonic (assuming no rollback), -but had the following surprising consequence: - -1. Thread / client reads with mode 'secondary' or 'secondaryPreferred', gets - pinned to a secondary -2. Thread / client reads with mode 'primaryPreferred', driver / mongos sees that - the pinned member (a secondary) matches the mode (which *allows* for a - secondary) and reads from secondary, even though the primary is available and - preferable - -The old spec also had the swapped problem, reading from the primary with -'secondaryPreferred', except for mongos which was changed at the last minute -before release with SERVER-6565_ ("Do not use primary if secondaries are -available for slaveOk"). - -This left application developers with two problems: - -1. 'primaryPreferred' and 'secondaryPreferred' acted surprisingly and - unpredictably within requests -2. There was no way to specify a common need: read from a secondary if possible - with 'secondaryPreferred', then from primary if possible with 'primaryPreferred', - all within a request. Instead an application developer would have to do the - second read with 'primary', which would unpin the thread but risk unavailability - if only secondaries were up. - -Additionally, mongos 2.4 introduced the releaseConnectionsAfterResponse option -(RCAR), mongos 2.6 made it the default and mongos 2.8 will remove the ability -to turn it off. This means that pinning to a mongos offers no guarantee that -connections to shards are pinned. Since we can't provide the same guarantees -for replica sets and sharded clusters, we removed automatic pinning entirely -and deprecated "requests". See SERVER-11956_ and SERVER-12273_. - -Regardless, even for replica sets, pinning offers no monotonicity because of -the ever-present possibility of rollbacks. Through MongoDB 2.6, secondaries -did not close sockets on rollback, so a rollback could happen between any two -queries without any indication to the driver. - -Therefore, an inconsistent feature that doesn't actually do what people think -it does has no place in the spec and has been removed. Should the server -eventually implement some form of "sessions", this spec will need to be revised -accordingly. - -.. _SERVER-6565: https://jira.mongodb.org/browse/SERVER-6565 -.. _SERVER-11956: https://jira.mongodb.org/browse/SERVER-11956 -.. _SERVER-12273: https://jira.mongodb.org/browse/SERVER-12273 - -Why change from mongos High Availablity (HA) to random selection? ---------------------------------------------------------------------- - -Mongos HA has similar problems with pinning, in that one can wind up pinned -to a high-latency mongos even if a lower-latency mongos later becomes -available. - -Random selection within the latency window avoids this problem and makes server -selection exactly analogous to having multiple suitable servers from a replica -set. This is easier to explain and implement. - -What happened to auto-retry? ----------------------------- - -The old auto-retry mechanism was closely connected to server pinning, which has -been removed. It also mandated exactly three attempts to carry out a query on -different servers, with no way to disable or adjust that value, and only for -the first query within a request. - -To the extent that auto-retry was trying to compensate for unavailable servers, -the Server Discovery and Monitoring spec and new server selection algorithm -provide a more robust and configurable way to direct *all* queries to available -servers. - -After a server is selected, several error conditions could still occur that -make the selected server unsuitable for sending the operation, such as: - - - the server could have shutdown the socket (e.g. a primary stepping down), - - - a connection pool could be empty, requiring new connections; those - connections could fail to connect or could fail the server handshake - -Once an operation is sent over the wire, several additional error conditions -could occur, such as: - - - a socket timeout could occur before the server responds - - - the server might send an RST packet, indicating the socket was already closed - - - for write operations, the server might return a "not master" error - -This specification does not require nor prohibit drivers from attempting -automatic recovery for various cases where it might be considered reasonable to -do so, such as: - - - repeating server selection if, after selection, a socket is determined to - be unsuitable before a message is sent on it - - - for a read operation, after a socket error, selecting a new server - meeting the read preference and resending the query - - - for a write operation, after a "not master" error, selecting a new server - (to locate the primary) and resending the write operation - -Driver-common rules for retrying operations (and configuring such retries) -could be the topic of a different, future specification. - -Why is maxStalenessSeconds applied before tag_sets? ---------------------------------------------------- - -The intention of read preference's list of tag sets is to allow a user to prefer -the first tag set but fall back to members matching later tag sets. In order to -know whether to fall back or not, we must first filter by all other criteria. - -Say you have two secondaries: - - - Node 1, tagged `{'tag': 'value1'}`, estimated staleness 5 minutes - - Node 2, tagged `{'tag': 'value2'}`, estimated staleness 1 minute - -And a read preference: - - - mode: "secondary" - - maxStalenessSeconds: 120 (2 minutes) - - tag_sets: `[{'tag': 'value1'}, {'tag': 'value2'}]` - -If tag sets were applied before maxStalenessSeconds, we would select Node 1 since it -matches the first tag set, then filter it out because it is too stale, and be -left with no eligible servers. - -The user's intent in specifying two tag sets was to fall back to the second set -if needed, so we filter by maxStalenessSeconds first, then tag_sets, and select -Node 2. - -References -========== - -- `Server Discovery and Monitoring`_ specification -- `Driver Authentication`_ specification - -.. _Server Discovery and Monitoring: https://github.com/mongodb/specifications/tree/master/source/server-discovery-and-monitoring -.. _heartbeatFrequencyMS: https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#heartbeatfrequencyms -.. _Max Staleness: https://github.com/mongodb/specifications/tree/master/source/max-staleness -.. _idleWritePeriodMS: https://github.com/mongodb/specifications/blob/master/source/max-staleness/max-staleness.rst#idlewriteperiodms -.. _Driver Authentication: https://github.com/mongodb/specifications/blob/master/source/auth - -Changes -======= - -2017-11-10: Added application-configurated server selector. - -2015-06-26: Updated single-threaded selection logic with "stale" and serverSelectionTryOnce. - -2015-08-10: Updated single-threaded selection logic to ensure a scan always -happens at least once under serverSelectionTryOnce if selection fails. -Removed the general selection algorithm and put full algorithms for each of -the single- and multi-threaded sections. Added a requirement that -single-threaded drivers document selection time expectations. - -2016-07-21: Updated for Max Staleness support. - -2016-08-03: Clarify selection algorithm, in particular that maxStalenessMS -comes before tag_sets. - -2016-10-24: Rename option from "maxStalenessMS" to "maxStalenessSeconds". - -2016-10-25: Change minimum maxStalenessSeconds value from 2 * heartbeatFrequencyMS -to heartbeatFrequencyMS + idleWritePeriodMS (with proper conversions of course). - -2016-11-01: Update formula for secondary staleness estimate with the -equivalent, and clearer, expression of this formula from the Max Staleness Spec - -2016-11-21: Revert changes that would allow idleWritePeriodMS to change in the -future, require maxStalenessSeconds to be at least 90. - -2017-06-07: Clarify socketCheckIntervalMS behavior, single-threaded drivers -must retry selection after checking an idle socket and discovering it is broken. - -2017-11-12: Specify read preferences for OP_MSG with direct connection, and -delete obsolete comment direct connections to secondaries getting "not master" -errors by design. - -2018-01-22: Clarify that $query wrapping is only for OP_QUERY - -2018-01-22: Clarify that $out on aggregate follows the "$out Aggregation -Pipeline Operator" spec and warns if read preference is not primary. - -2018-01-29: Remove reference to '$out Aggregation spec'. Clarify runCommand -selection rules. - -.. [#] mongos 3.4 refuses to connect to mongods with maxWireVersion < 5, - so it does no additional wire version checks related to maxStalenessSeconds. +.. note:: + This specification has been converted to Markdown and renamed to + `server-selection.md `_. diff --git a/source/server-selection/tests/README.md b/source/server-selection/tests/README.md new file mode 100644 index 0000000000..e32e5244e1 --- /dev/null +++ b/source/server-selection/tests/README.md @@ -0,0 +1,84 @@ +# Server Selection Tests + +This directory contains platform-independent tests that drivers can use to prove their conformance to the Server +Selection spec. The tests are provided in both YAML and JSON formats, and drivers may test against whichever format is +more convenient for them. + +## Version + +Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +## Test Format and Use + +There are two types of tests for the server selection spec, tests for round trip time (RTT) calculation, and tests for +server selection logic. + +Drivers should be able to test their server selection logic without any network I/O, by parsing topology descriptions +and read preference documents from the test files and passing them into driver code. Parts of the server selection code +may need to be mocked or subclassed to achieve this. + +### RTT Calculation Tests + +These YAML files contain the following keys: + +- `avg_rtt_ms`: a server's previous average RTT, in milliseconds +- `new_rtt_ms`: a new RTT value for this server, in milliseconds +- `new_avg_rtt`: this server's newly-calculated average RTT, in milliseconds + +For each file, create a server description object initialized with `avg_rtt_ms`. Parse `new_rtt_ms`, and ensure that the +new RTT value for the mocked server description is equal to `new_avg_rtt`. + +If driver architecture doesn't easily allow construction of server description objects in isolation, unit testing the +EWMA algorithm using these inputs and expected outputs is acceptable. + +### Server Selection Logic Tests + +These YAML files contain the following setup for each test: + +- `topology_description`: the state of a mocked cluster +- `operation`: the kind of operation to perform, either read or write +- `read_preference`: a read preference document + +For each file, create a new TopologyDescription object initialized with the values from `topology_description`. Create a +ReadPreference object initialized with the values from `read_preference`. + +Together with "operation", pass the newly-created TopologyDescription and ReadPreference to server selection, and ensure +that it selects the correct subset of servers from the TopologyDescription. Each YAML file contains a key for these +stages of server selection: + +- `suitable_servers`: the set of servers in topology_description that are suitable, as per the Server Selection spec, + given operation and read_preference +- `in_latency_window`: the set of suitable_servers that fall within the latency window + +Drivers implementing server selection MUST test that their implementation correctly returns the set of servers in +`in_latency_window`. Drivers SHOULD also test against `suitable_servers` if possible. + +### Selection Within Latency Window YAML Tests + +These tests verify that servers select servers from within the latency window correctly. These tests MUST only be +implemented by multi-threaded or async drivers. + +Each YAML file for these tests has the following format: + +- `topology_description`: the state of a mocked cluster + +- `mocked_topology_state`: array of objects containing the operationCounts for each server included in + `topology_description`. Each element will have all of the following fields: + + - `address`: a unique address identifying this server + - `operation_count`: the `operationCount` for this server + +- `iterations`: the number of selections that should be run as part of this test + +- `outcome`: an object describing the expected outcome of the selections + + - `tolerance`: the maximum difference between an observed frequency and an expected one + - `expected_frequencies`: a document whose keys are the server addresses from the `in_window` array and values are + numbers in `[0, 1]` indicating the frequency at which the server should have been selected. + +For each file, create a new TopologyDescription object initialized with the values from `topology_description` and +populate the operationCounts based on the information provided in `mocked_topology_state`. Then, repeatedly select a +server for a read operation using the mocked TopologyDescription and a "nearest" ReadPreference `iterations` times. Once +`iterations` selections have been made, verify that each server was selected at a frequency within `outcome.tolerance` +of the expected frequency contained in `outcome.expected_frequencies` for that server. If the expected frequency for a +given server is 1 or 0, then the observed frequency MUST be exactly equal to the expected one. diff --git a/source/server-selection/tests/README.rst b/source/server-selection/tests/README.rst deleted file mode 100644 index 2c9550a226..0000000000 --- a/source/server-selection/tests/README.rst +++ /dev/null @@ -1,67 +0,0 @@ -====================== -Server Selection Tests -====================== - -This directory contains platform-independent tests that drivers can use -to prove their conformance to the Server Selection spec. The tests -are provided in both YAML and JSON formats, and drivers may test against -whichever format is more convenient for them. - -Version -------- - -Files in the "specifications" repository have no version scheme. They are not -tied to a MongoDB server version. - -Test Format and Use -------------------- - -There are two types of tests for the server selection spec, tests for -round trip time (RTT) calculation, and tests for server selection logic. - -Drivers should be able to test their server selection logic -without any network I/O, by parsing topology descriptions and read preference -documents from the test files and passing them into driver code. Parts of the -server selection code may need to be mocked or subclassed to achieve this. - -RTT Calculation Tests ->>>>>>>>>>>>>>>>>>>>> - -These YAML files contain the following keys: - -- ``avg_rtt_ms``: a server's previous average RTT, in milliseconds -- ``new_rtt_ms``: a new RTT value for this server, in milliseconds -- ``new_avg_rtt``: this server's newly-calculated average RTT, in milliseconds - -For each file, create a server description object initialized with ``avg_rtt_ms``. -Parse ``new_rtt_ms``, and ensure that the new RTT value for the mocked server -description is equal to ``new_avg_rtt``. - -If driver architecture doesn't easily allow construction of server description -objects in isolation, unit testing the EWMA algorithm using these inputs -and expected outputs is acceptable. - -Server Selection Logic Tests ->>>>>>>>>>>>>>>>>>>>>>>>>>>> - -These YAML files contain the following setup for each test: - -- ``topology_description``: the state of a mocked cluster -- ``operation``: the kind of operation to perform, either read or write -- ``read_preference``: a read preference document - -For each file, create a new TopologyDescription object initialized with the values -from ``topology_description``. Create a ReadPreference object initialized with the -values from ``read_preference``. - -Together with "operation", pass the newly-created TopologyDescription and ReadPreference -to server selection, and ensure that it selects the correct subset of servers from -the TopologyDescription. Each YAML file contains a key for these stages of server selection: - -- ``suitable_servers``: the set of servers in topology_description that are suitable, as - per the Server Selection spec, given operation and read_preference -- ``in_latency_window``: the set of suitable_servers that fall within the latency window - -Drivers implementing server selection MUST test that their implementation -correctly returns the set of servers in ``in_latency_window``. Drivers SHOULD also test -against ``suitable_servers`` if possible. diff --git a/source/server-selection/tests/in_window/equilibrium.json b/source/server-selection/tests/in_window/equilibrium.json new file mode 100644 index 0000000000..c5f177d49b --- /dev/null +++ b/source/server-selection/tests/in_window/equilibrium.json @@ -0,0 +1,46 @@ +{ + "description": "When in equilibrium selection is evenly distributed", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 5 + }, + { + "address": "b:27017", + "operation_count": 5 + }, + { + "address": "c:27017", + "operation_count": 5 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.33, + "b:27017": 0.33, + "c:27017": 0.33 + } + } +} diff --git a/source/server-selection/tests/in_window/equilibrium.yml b/source/server-selection/tests/in_window/equilibrium.yml new file mode 100644 index 0000000000..a79fa21b1d --- /dev/null +++ b/source/server-selection/tests/in_window/equilibrium.yml @@ -0,0 +1,27 @@ +description: When in equilibrium selection is evenly distributed +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos + - address: c:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 5 + - address: b:27017 + operation_count: 5 + - address: c:27017 + operation_count: 5 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.33 + b:27017: 0.33 + c:27017: 0.33 diff --git a/source/server-selection/tests/in_window/many-choices.json b/source/server-selection/tests/in_window/many-choices.json new file mode 100644 index 0000000000..7e940513ef --- /dev/null +++ b/source/server-selection/tests/in_window/many-choices.json @@ -0,0 +1,106 @@ +{ + "description": "Selections from many choices occur at correct frequencies", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "d:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "e:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "f:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "g:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "i:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 0 + }, + { + "address": "b:27017", + "operation_count": 5 + }, + { + "address": "c:27017", + "operation_count": 5 + }, + { + "address": "d:27017", + "operation_count": 10 + }, + { + "address": "e:27017", + "operation_count": 10 + }, + { + "address": "f:27017", + "operation_count": 20 + }, + { + "address": "g:27017", + "operation_count": 20 + }, + { + "address": "h:27017", + "operation_count": 50 + }, + { + "address": "i:27017", + "operation_count": 60 + } + ], + "iterations": 10000, + "outcome": { + "tolerance": 0.03, + "expected_frequencies": { + "a:27017": 0.22, + "b:27017": 0.18, + "c:27017": 0.18, + "d:27017": 0.125, + "e:27017": 0.125, + "f:27017": 0.074, + "g:27017": 0.074, + "h:27017": 0.0277, + "i:27017": 0 + } + } +} diff --git a/source/server-selection/tests/in_window/many-choices.yml b/source/server-selection/tests/in_window/many-choices.yml new file mode 100644 index 0000000000..6b3292129d --- /dev/null +++ b/source/server-selection/tests/in_window/many-choices.yml @@ -0,0 +1,63 @@ +description: Selections from many choices occur at correct frequencies +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos + - address: c:27017 + avg_rtt_ms: 35 + type: Mongos + - address: d:27017 + avg_rtt_ms: 35 + type: Mongos + - address: e:27017 + avg_rtt_ms: 35 + type: Mongos + - address: f:27017 + avg_rtt_ms: 35 + type: Mongos + - address: g:27017 + avg_rtt_ms: 35 + type: Mongos + - address: h:27017 + avg_rtt_ms: 35 + type: Mongos + - address: i:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 0 + - address: b:27017 + operation_count: 5 + - address: c:27017 + operation_count: 5 + - address: d:27017 + operation_count: 10 + - address: e:27017 + operation_count: 10 + - address: f:27017 + operation_count: 20 + - address: g:27017 + operation_count: 20 + - address: h:27017 + operation_count: 50 + - address: i:27017 + operation_count: 60 +iterations: 10000 +outcome: + tolerance: 0.03 + expected_frequencies: + a:27017: 0.22 + b:27017: 0.18 + c:27017: 0.18 + d:27017: 0.125 + e:27017: 0.125 + f:27017: 0.074 + g:27017: 0.074 + h:27017: 0.0277 + i:27017: 0 diff --git a/source/server-selection/tests/in_window/one-least-two-tied.json b/source/server-selection/tests/in_window/one-least-two-tied.json new file mode 100644 index 0000000000..ed7526e716 --- /dev/null +++ b/source/server-selection/tests/in_window/one-least-two-tied.json @@ -0,0 +1,46 @@ +{ + "description": "Least operations gets most selections, two tied share the rest", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 16 + }, + { + "address": "b:27017", + "operation_count": 10 + }, + { + "address": "c:27017", + "operation_count": 16 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.165, + "b:27017": 0.66, + "c:27017": 0.165 + } + } +} diff --git a/source/server-selection/tests/in_window/one-least-two-tied.yml b/source/server-selection/tests/in_window/one-least-two-tied.yml new file mode 100644 index 0000000000..e05bdfaf70 --- /dev/null +++ b/source/server-selection/tests/in_window/one-least-two-tied.yml @@ -0,0 +1,27 @@ +description: Least operations gets most selections, two tied share the rest +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos + - address: c:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 16 + - address: b:27017 + operation_count: 10 + - address: c:27017 + operation_count: 16 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.165 + b:27017: 0.66 + c:27017: 0.165 diff --git a/source/server-selection/tests/in_window/rs-equilibrium.json b/source/server-selection/tests/in_window/rs-equilibrium.json new file mode 100644 index 0000000000..61c6687e50 --- /dev/null +++ b/source/server-selection/tests/in_window/rs-equilibrium.json @@ -0,0 +1,46 @@ +{ + "description": "When in equilibrium selection is evenly distributed (replica set)", + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "RSPrimary" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "RSSecondary" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 6 + }, + { + "address": "b:27017", + "operation_count": 6 + }, + { + "address": "c:27017", + "operation_count": 6 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.33, + "b:27017": 0.33, + "c:27017": 0.33 + } + } +} diff --git a/source/server-selection/tests/in_window/rs-equilibrium.yml b/source/server-selection/tests/in_window/rs-equilibrium.yml new file mode 100644 index 0000000000..0d8006a2d4 --- /dev/null +++ b/source/server-selection/tests/in_window/rs-equilibrium.yml @@ -0,0 +1,27 @@ +description: When in equilibrium selection is evenly distributed (replica set) +topology_description: + type: ReplicaSetWithPrimary + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: RSPrimary + - address: b:27017 + avg_rtt_ms: 35 + type: RSSecondary + - address: c:27017 + avg_rtt_ms: 35 + type: RSSecondary +mocked_topology_state: + - address: a:27017 + operation_count: 6 + - address: b:27017 + operation_count: 6 + - address: c:27017 + operation_count: 6 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.33 + b:27017: 0.33 + c:27017: 0.33 diff --git a/source/server-selection/tests/in_window/rs-three-choices.json b/source/server-selection/tests/in_window/rs-three-choices.json new file mode 100644 index 0000000000..3fdc15205c --- /dev/null +++ b/source/server-selection/tests/in_window/rs-three-choices.json @@ -0,0 +1,46 @@ +{ + "description": "Selections from three servers occur at proper distributions (replica set)", + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "RSPrimary" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "RSSecondary" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "RSSecondary" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 3 + }, + { + "address": "b:27017", + "operation_count": 6 + }, + { + "address": "c:27017", + "operation_count": 20 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.66, + "b:27017": 0.33, + "c:27017": 0 + } + } +} diff --git a/source/server-selection/tests/in_window/rs-three-choices.yml b/source/server-selection/tests/in_window/rs-three-choices.yml new file mode 100644 index 0000000000..514f7e66b3 --- /dev/null +++ b/source/server-selection/tests/in_window/rs-three-choices.yml @@ -0,0 +1,27 @@ +description: Selections from three servers occur at proper distributions (replica set) +topology_description: + type: ReplicaSetWithPrimary + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: RSPrimary + - address: b:27017 + avg_rtt_ms: 35 + type: RSSecondary + - address: c:27017 + avg_rtt_ms: 35 + type: RSSecondary +mocked_topology_state: + - address: a:27017 + operation_count: 3 + - address: b:27017 + operation_count: 6 + - address: c:27017 + operation_count: 20 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.66 + b:27017: 0.33 + c:27017: 0 diff --git a/source/server-selection/tests/in_window/three-choices.json b/source/server-selection/tests/in_window/three-choices.json new file mode 100644 index 0000000000..7b5b414549 --- /dev/null +++ b/source/server-selection/tests/in_window/three-choices.json @@ -0,0 +1,46 @@ +{ + "description": "Selections from three servers occur at proper distributions", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 3 + }, + { + "address": "b:27017", + "operation_count": 6 + }, + { + "address": "c:27017", + "operation_count": 20 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.66, + "b:27017": 0.33, + "c:27017": 0 + } + } +} diff --git a/source/server-selection/tests/in_window/three-choices.yml b/source/server-selection/tests/in_window/three-choices.yml new file mode 100644 index 0000000000..ffc9f53ea6 --- /dev/null +++ b/source/server-selection/tests/in_window/three-choices.yml @@ -0,0 +1,27 @@ +description: Selections from three servers occur at proper distributions +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos + - address: c:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 3 + - address: b:27017 + operation_count: 6 + - address: c:27017 + operation_count: 20 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.66 + b:27017: 0.33 + c:27017: 0 diff --git a/source/server-selection/tests/in_window/two-choices.json b/source/server-selection/tests/in_window/two-choices.json new file mode 100644 index 0000000000..2c7a605d8d --- /dev/null +++ b/source/server-selection/tests/in_window/two-choices.json @@ -0,0 +1,36 @@ +{ + "description": "Better of two choices always selected", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 0 + }, + { + "address": "b:27017", + "operation_count": 5 + } + ], + "iterations": 100, + "outcome": { + "tolerance": 0, + "expected_frequencies": { + "a:27017": 1, + "b:27017": 0 + } + } +} diff --git a/source/server-selection/tests/in_window/two-choices.yml b/source/server-selection/tests/in_window/two-choices.yml new file mode 100644 index 0000000000..ef763d649e --- /dev/null +++ b/source/server-selection/tests/in_window/two-choices.yml @@ -0,0 +1,21 @@ +description: Better of two choices always selected +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 0 + - address: b:27017 + operation_count: 5 +iterations: 100 +outcome: + tolerance: 0.0 + expected_frequencies: + a:27017: 1 + b:27017: 0 diff --git a/source/server-selection/tests/in_window/two-least.json b/source/server-selection/tests/in_window/two-least.json new file mode 100644 index 0000000000..73214fc647 --- /dev/null +++ b/source/server-selection/tests/in_window/two-least.json @@ -0,0 +1,46 @@ +{ + "description": "Two tied for least operations share all selections", + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "b:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + }, + { + "address": "c:27017", + "avg_rtt_ms": 35, + "type": "Mongos" + } + ] + }, + "mocked_topology_state": [ + { + "address": "a:27017", + "operation_count": 10 + }, + { + "address": "b:27017", + "operation_count": 10 + }, + { + "address": "c:27017", + "operation_count": 16 + } + ], + "iterations": 2000, + "outcome": { + "tolerance": 0.05, + "expected_frequencies": { + "a:27017": 0.5, + "b:27017": 0.5, + "c:27017": 0 + } + } +} diff --git a/source/server-selection/tests/in_window/two-least.yml b/source/server-selection/tests/in_window/two-least.yml new file mode 100644 index 0000000000..ba4bf39ee1 --- /dev/null +++ b/source/server-selection/tests/in_window/two-least.yml @@ -0,0 +1,27 @@ +description: Two tied for least operations share all selections +topology_description: + type: Sharded + servers: + - address: a:27017 + avg_rtt_ms: 35 + type: Mongos + - address: b:27017 + avg_rtt_ms: 35 + type: Mongos + - address: c:27017 + avg_rtt_ms: 35 + type: Mongos +mocked_topology_state: + - address: a:27017 + operation_count: 10 + - address: b:27017 + operation_count: 10 + - address: c:27017 + operation_count: 16 +iterations: 2000 +outcome: + tolerance: 0.05 + expected_frequencies: + a:27017: 0.5 + b:27017: 0.5 + c:27017: 0 diff --git a/source/server-selection/tests/logging/load-balanced.json b/source/server-selection/tests/logging/load-balanced.json new file mode 100644 index 0000000000..5855c4e991 --- /dev/null +++ b/source/server-selection/tests/logging/load-balanced.json @@ -0,0 +1,107 @@ +{ + "description": "server-selection-logging", + "schemaVersion": "1.13", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "uriOptions": { + "heartbeatFrequencyMS": 500 + }, + "observeLogMessages": { + "serverSelection": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "server-selection" + } + } + ], + "tests": [ + { + "description": "A successful operation - load balanced cluster", + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "LoadBalancer" + } + } + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-selection/tests/logging/load-balanced.yml b/source/server-selection/tests/logging/load-balanced.yml new file mode 100644 index 0000000000..ec3bc61028 --- /dev/null +++ b/source/server-selection/tests/logging/load-balanced.yml @@ -0,0 +1,60 @@ +description: "server-selection-logging" + +schemaVersion: "1.13" + +runOnRequirements: + - topologies: + - load-balanced + +createEntities: + - client: + id: &client client + uriOptions: + heartbeatFrequencyMS: 500 + observeLogMessages: + serverSelection: debug + observeEvents: + - serverDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName server-selection + +tests: + - description: "A successful operation - load balanced cluster" + operations: + # ensure we've discovered the entire topology before starting. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: LoadBalancer + count: 1 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } diff --git a/source/server-selection/tests/logging/operation-id.json b/source/server-selection/tests/logging/operation-id.json new file mode 100644 index 0000000000..72ebff60d8 --- /dev/null +++ b/source/server-selection/tests/logging/operation-id.json @@ -0,0 +1,418 @@ +{ + "description": "operation-id", + "schemaVersion": "1.14", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 500, + "appName": "loggingClient", + "serverSelectionTimeoutMS": 2000 + }, + "observeLogMessages": { + "serverSelection": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "server-selection" + } + }, + { + "client": { + "id": "failPointClient" + } + } + ], + "_yamlAnchors": { + "namespace": "logging-tests.server-selection" + }, + "tests": [ + { + "description": "Successful bulkWrite operation: log messages have operationIds", + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "x": 1 + } + } + } + ] + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "insert" + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "insert" + } + } + ] + } + ] + }, + { + "description": "Failed bulkWrite operation: log messages have operationIds", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "ismaster" + ], + "appName": "loggingClient", + "closeConnection": true + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "bulkWrite", + "object": "collection", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "x": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "insert" + } + }, + { + "level": "info", + "component": "serverSelection", + "data": { + "message": "Waiting for suitable server to become available", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "insert" + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection failed", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "insert" + } + } + ] + } + ] + }, + { + "description": "Successful client bulkWrite operation: log messages have operationIds", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "logging-tests.server-selection", + "document": { + "x": 1 + } + } + } + ] + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "bulkWrite" + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "bulkWrite" + } + } + ] + } + ] + }, + { + "description": "Failed client bulkWrite operation: log messages have operationIds", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "ismaster" + ], + "appName": "loggingClient", + "closeConnection": true + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "logging-tests.server-selection", + "document": { + "x": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "bulkWrite" + } + }, + { + "level": "info", + "component": "serverSelection", + "data": { + "message": "Waiting for suitable server to become available", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "bulkWrite" + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection failed", + "operationId": { + "$$type": [ + "int", + "long" + ] + }, + "operation": "bulkWrite" + } + } + ] + } + ] + } + ] +} diff --git a/source/server-selection/tests/logging/operation-id.yml b/source/server-selection/tests/logging/operation-id.yml new file mode 100644 index 0000000000..e5732893fa --- /dev/null +++ b/source/server-selection/tests/logging/operation-id.yml @@ -0,0 +1,223 @@ +description: "operation-id" + +schemaVersion: "1.14" + +runOnRequirements: + - topologies: + - single + +createEntities: + - client: + id: &client client + uriOptions: + retryWrites: false # so we don't retry after hitting a failpoint + heartbeatFrequencyMS: 500 + appName: &appName loggingClient + serverSelectionTimeoutMS: 2000 # avoid hanging for 30s in test expecting SS failure due to timeout + observeLogMessages: + serverSelection: debug + observeEvents: + - serverDescriptionChangedEvent + - topologyDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName server-selection + - client: + id: &failPointClient failPointClient + +_yamlAnchors: + namespace: &namespace "logging-tests.server-selection" + +tests: + - description: "Successful bulkWrite operation: log messages have operationIds" + operations: + # ensure we've discovered the server so it is immediately available + # and no extra "waiting for suitable server" messages are emitted. + # expected topology events reflect initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { x: 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + operationId: { $$type: [int, long] } + operation: insert + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + operationId: { $$type: [int, long] } + operation: insert + + - description: "Failed bulkWrite operation: log messages have operationIds" + runOnRequirements: + - minServerVersion: "4.4" # failCommand appname support + operations: + # fail all hello/legacy hello commands for the main client. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: ["hello", "ismaster"] + appName: *appName + closeConnection: true + # wait until we've marked the server unknown due + # to a failed heartbeat. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: bulkWrite + object: *collection + arguments: + requests: + - insertOne: + document: { x: 1 } + expectError: + isClientError: true # server selection timeout + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + operationId: { $$type: [int, long] } + operation: insert + - level: info + component: serverSelection + data: + message: "Waiting for suitable server to become available" + operationId: { $$type: [int, long] } + operation: insert + - level: debug + component: serverSelection + data: + message: "Server selection failed" + operationId: { $$type: [int, long] } + operation: insert + + - description: "Successful client bulkWrite operation: log messages have operationIds" + runOnRequirements: + - minServerVersion: "8.0" # required for bulkWrite command + serverless: forbid + operations: + # ensure we've discovered the server so it is immediately available + # and no extra "waiting for suitable server" messages are emitted. + # expected topology events reflect initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: *namespace + document: { x: 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + operationId: { $$type: [int, long] } + operation: bulkWrite + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + operationId: { $$type: [int, long] } + operation: bulkWrite + + - description: "Failed client bulkWrite operation: log messages have operationIds" + runOnRequirements: + - minServerVersion: "8.0" # required for bulkWrite command + serverless: forbid + operations: + # fail all hello/legacy hello commands for the main client. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: ["hello", "ismaster"] + appName: *appName + closeConnection: true + # wait until we've marked the server unknown due + # to a failed heartbeat. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: *namespace + document: { x: 1 } + expectError: + isClientError: true # server selection timeout + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + operationId: { $$type: [int, long] } + operation: bulkWrite + - level: info + component: serverSelection + data: + message: "Waiting for suitable server to become available" + operationId: { $$type: [int, long] } + operation: bulkWrite + - level: debug + component: serverSelection + data: + message: "Server selection failed" + operationId: { $$type: [int, long] } + operation: bulkWrite diff --git a/source/server-selection/tests/logging/replica-set.json b/source/server-selection/tests/logging/replica-set.json new file mode 100644 index 0000000000..5eba784bf2 --- /dev/null +++ b/source/server-selection/tests/logging/replica-set.json @@ -0,0 +1,228 @@ +{ + "description": "replica-set-logging", + "schemaVersion": "1.14", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 500, + "serverSelectionTimeoutMS": 2000 + }, + "observeLogMessages": { + "serverSelection": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "server-selection" + } + }, + { + "client": { + "id": "failPointClient" + } + }, + { + "collection": { + "id": "unsatisfiableRPColl", + "database": "database", + "collectionName": "unsatisfiableRPColl", + "collectionOptions": { + "readPreference": { + "mode": "Secondary", + "tagSets": [ + { + "nonexistenttag": "a" + } + ] + } + } + } + } + ], + "tests": [ + { + "description": "A successful operation", + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 4 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Server selection fails due to unsatisfiable read preference", + "runOnRequirements": [ + { + "minServerVersion": "4.0" + } + ], + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 4 + } + }, + { + "name": "find", + "object": "unsatisfiableRPColl", + "arguments": { + "filter": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "find", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "info", + "component": "serverSelection", + "data": { + "message": "Waiting for suitable server to become available", + "selector": { + "$$exists": true + }, + "operation": "find", + "topologyDescription": { + "$$exists": true + }, + "remainingTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection failed", + "selector": { + "$$exists": true + }, + "operation": "find", + "topologyDescription": { + "$$exists": true + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-selection/tests/logging/replica-set.yml b/source/server-selection/tests/logging/replica-set.yml new file mode 100644 index 0000000000..3ee8b92d94 --- /dev/null +++ b/source/server-selection/tests/logging/replica-set.yml @@ -0,0 +1,126 @@ +description: "replica-set-logging" + +schemaVersion: "1.14" + +runOnRequirements: + - topologies: + - replicaset + +createEntities: + - client: + id: &client client + uriOptions: + retryWrites: false # so we don't retry after hitting a failpoint + heartbeatFrequencyMS: 500 + serverSelectionTimeoutMS: 2000 # avoid hanging for 30s in test expecting SS failure due to timeout + observeLogMessages: + serverSelection: debug + observeEvents: + - serverDescriptionChangedEvent + - topologyDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName server-selection + - client: + id: &failPointClient failPointClient + - collection: + id: &unsatisfiableRPColl unsatisfiableRPColl + database: *database + collectionName: &unsatisfiableRPColl unsatisfiableRPColl + collectionOptions: + readPreference: + mode: Secondary + tagSets: + - { nonexistenttag : a } + +tests: + - description: "A successful operation" + operations: + # ensure we've discovered the primary before starting, so we don't get an unexpected "waiting" message + # when server selection happens for the insert, or have extra logs from the driver performing server + # expected events are initial cluster type change from unknown to ReplicaSet, and connect events for each of 3 servers. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 4 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - description: "Server selection fails due to unsatisfiable read preference" + runOnRequirements: + - minServerVersion: "4.0" # min version for failCommand on mongod + operations: + # ensure we've discovered the primary before starting, so we don't get an unexpected "waiting" message + # when server selection happens for the insert, or have extra logs from the driver performing server + # expected events are initial cluster type change from unknown to ReplicaSet, and connect events for each of 3 servers. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 4 + # the `find` should never succeed due to the unsatisfiable read preference. + - name: find + object: *unsatisfiableRPColl + arguments: + filter: { x : 1 } + expectError: + isClientError: true # server selection error + expectLogMessages: + # the topology won't change during this time, so although we exhaust + # serverSelectionTimeoutMS we only expect a single message. + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: find + topologyDescription: { $$exists: true } + - level: info + component: serverSelection + data: + message: "Waiting for suitable server to become available" + selector: { $$exists: true } + operation: find + topologyDescription: { $$exists: true } + remainingTimeMS: { $$type: [int, long] } + - level: debug + component: serverSelection + data: + message: "Server selection failed" + selector: { $$exists: true } + operation: find + topologyDescription: { $$exists: true } + failure: { $$exists: true } \ No newline at end of file diff --git a/source/server-selection/tests/logging/sharded.json b/source/server-selection/tests/logging/sharded.json new file mode 100644 index 0000000000..d42fba9100 --- /dev/null +++ b/source/server-selection/tests/logging/sharded.json @@ -0,0 +1,237 @@ +{ + "description": "server-selection-logging", + "schemaVersion": "1.14", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 500, + "appName": "loggingClient", + "serverSelectionTimeoutMS": 2000 + }, + "observeLogMessages": { + "serverSelection": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ], + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "server-selection" + } + }, + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + } + ], + "tests": [ + { + "description": "A successful operation", + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Failure due to unreachable server", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "ismaster" + ], + "appName": "loggingClient", + "closeConnection": true + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "info", + "component": "serverSelection", + "data": { + "message": "Waiting for suitable server to become available", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "remainingTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection failed", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-selection/tests/logging/sharded.yml b/source/server-selection/tests/logging/sharded.yml new file mode 100644 index 0000000000..931f5517fa --- /dev/null +++ b/source/server-selection/tests/logging/sharded.yml @@ -0,0 +1,129 @@ +description: "server-selection-logging" + +schemaVersion: "1.14" + +runOnRequirements: + - topologies: + - sharded + +createEntities: + - client: + id: &client client + uriOptions: + retryWrites: false # so we don't retry after hitting a failpoint + heartbeatFrequencyMS: 500 + appName: &appName loggingClient + serverSelectionTimeoutMS: 2000 # avoid hanging for 30s in test expecting SS failure due to timeout + observeLogMessages: + serverSelection: debug + observeEvents: + - serverDescriptionChangedEvent + - topologyDescriptionChangedEvent + useMultipleMongoses: false + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName server-selection + - client: + id: &failPointClient failPointClient + useMultipleMongoses: false + +tests: + - description: "A successful operation" + operations: + # ensure we've discovered the entire topology before starting. + # expected events are initial cluster type change from unknown to sharded, and connect events for one mongos. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - description: "Failure due to unreachable server" + runOnRequirements: + - minServerVersion: "4.4" # failCommand appname support + operations: + # fail all hello/legacy hello commands for the main client. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: ["hello", "ismaster"] + appName: *appName + closeConnection: true + # wait until we've marked the server unknown due + # to a failed heartbeat. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectError: + isClientError: true # server selection timeout + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: info + component: serverSelection + data: + message: "Waiting for suitable server to become available" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + remainingTimeMS: { $$type: [int, long] } + - level: debug + component: serverSelection + data: + message: "Server selection failed" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + failure: { $$exists: true } diff --git a/source/server-selection/tests/logging/standalone.json b/source/server-selection/tests/logging/standalone.json new file mode 100644 index 0000000000..3b3eddd841 --- /dev/null +++ b/source/server-selection/tests/logging/standalone.json @@ -0,0 +1,235 @@ +{ + "description": "standalone-logging", + "schemaVersion": "1.14", + "runOnRequirements": [ + { + "topologies": [ + "single" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 500, + "appName": "loggingClient", + "serverSelectionTimeoutMS": 2000 + }, + "observeLogMessages": { + "serverSelection": "debug" + }, + "observeEvents": [ + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "logging-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "server-selection" + } + }, + { + "client": { + "id": "failPointClient" + } + } + ], + "tests": [ + { + "description": "A successful operation", + "operations": [ + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 2 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection succeeded", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + }, + { + "description": "Failure due to unreachable server", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "hello", + "ismaster" + ], + "appName": "loggingClient", + "closeConnection": true + } + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverDescriptionChangedEvent": { + "newDescription": { + "type": "Unknown" + } + } + }, + "count": 1 + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection started", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + } + } + }, + { + "level": "info", + "component": "serverSelection", + "data": { + "message": "Waiting for suitable server to become available", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "remainingTimeMS": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "serverSelection", + "data": { + "message": "Server selection failed", + "selector": { + "$$exists": true + }, + "operation": "insert", + "topologyDescription": { + "$$exists": true + }, + "failure": { + "$$exists": true + } + } + } + ] + } + ] + } + ] +} diff --git a/source/server-selection/tests/logging/standalone.yml b/source/server-selection/tests/logging/standalone.yml new file mode 100644 index 0000000000..fe3121032d --- /dev/null +++ b/source/server-selection/tests/logging/standalone.yml @@ -0,0 +1,128 @@ +description: "standalone-logging" + +schemaVersion: "1.14" + +runOnRequirements: + - topologies: + - single + +createEntities: + - client: + id: &client client + uriOptions: + retryWrites: false # so we don't retry after hitting a failpoint + heartbeatFrequencyMS: 500 + appName: &appName loggingClient + serverSelectionTimeoutMS: 2000 # avoid hanging for 30s in test expecting SS failure due to timeout + observeLogMessages: + serverSelection: debug + observeEvents: + - serverDescriptionChangedEvent + - topologyDescriptionChangedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName logging-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName server-selection + - client: + id: &failPointClient failPointClient + +tests: + - description: "A successful operation" + operations: + # ensure we've discovered the server so it is immediately available + # and no extra "waiting for suitable server" messages are emitted. + # expected topology events reflect initial server discovery and server connect event. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 2 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: debug + component: serverSelection + data: + message: "Server selection succeeded" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + serverHost: { $$type: string } + serverPort: { $$type: [int, long] } + + - description: "Failure due to unreachable server" + runOnRequirements: + - minServerVersion: "4.4" # failCommand appname support + operations: + # fail all hello/legacy hello commands for the main client. + - name: failPoint + object: testRunner + arguments: + client: *failPointClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: ["hello", "ismaster"] + appName: *appName + closeConnection: true + # wait until we've marked the server unknown due + # to a failed heartbeat. + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + serverDescriptionChangedEvent: + newDescription: + type: Unknown + count: 1 + - name: insertOne + object: *collection + arguments: + document: { x : 1 } + expectError: + isClientError: true # server selection timeout + expectLogMessages: + - client: *client + messages: + - level: debug + component: serverSelection + data: + message: "Server selection started" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + - level: info + component: serverSelection + data: + message: "Waiting for suitable server to become available" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + remainingTimeMS: { $$type: [int, long] } + - level: debug + component: serverSelection + data: + message: "Server selection failed" + selector: { $$exists: true } + operation: insert + topologyDescription: { $$exists: true } + failure: { $$exists: true } diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json b/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json new file mode 100644 index 0000000000..76fa336d55 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.yml b/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.yml new file mode 100644 index 0000000000..2e9a16d5e0 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Nearest.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.json b/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.json new file mode 100644 index 0000000000..5a4a0aa93a --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.json @@ -0,0 +1,30 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.yml b/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.yml new file mode 100644 index 0000000000..72ea728d6e --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Primary.yml @@ -0,0 +1,14 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: read +read_preference: + mode: Primary +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json b/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json new file mode 100644 index 0000000000..9aa151cd06 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.yml b/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.yml new file mode 100644 index 0000000000..96e6ff54c2 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/PrimaryPreferred.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json b/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json new file mode 100644 index 0000000000..c49e30370b --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.yml b/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.yml new file mode 100644 index 0000000000..2f027d2dc7 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/Secondary.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json b/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json new file mode 100644 index 0000000000..18e46877b4 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.yml b/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.yml new file mode 100644 index 0000000000..f3da0ec4b0 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/read/SecondaryPreferred.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json b/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json new file mode 100644 index 0000000000..e52e343332 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.yml b/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.yml new file mode 100644 index 0000000000..8e7b59dc4a --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Nearest.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: write +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.json b/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.json new file mode 100644 index 0000000000..9061b25208 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.json @@ -0,0 +1,30 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Primary" + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.yml b/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.yml new file mode 100644 index 0000000000..457eb53e58 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Primary.yml @@ -0,0 +1,14 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: write +read_preference: + mode: Primary +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json b/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json new file mode 100644 index 0000000000..5c94dc410d --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.yml b/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.yml new file mode 100644 index 0000000000..2a078d1f40 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/PrimaryPreferred.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: write +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json b/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json new file mode 100644 index 0000000000..5493867e12 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.yml b/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.yml new file mode 100644 index 0000000000..dc67dbc942 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/Secondary.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: write +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json b/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json new file mode 100644 index 0000000000..f7905f1d5f --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.json @@ -0,0 +1,35 @@ +{ + "topology_description": { + "type": "LoadBalanced", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "suitable_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ], + "in_latency_window": [ + { + "address": "g:27017", + "avg_rtt_ms": 0, + "type": "LoadBalancer" + } + ] +} diff --git a/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.yml b/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.yml new file mode 100644 index 0000000000..77026a67c0 --- /dev/null +++ b/source/server-selection/tests/server_selection/LoadBalanced/write/SecondaryPreferred.yml @@ -0,0 +1,16 @@ +topology_description: + type: LoadBalanced + servers: + - &1 + address: g:27017 + avg_rtt_ms: 0 + type: LoadBalancer +operation: write +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..f569c39d6c --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.yml new file mode 100644 index 0000000000..9297b98a32 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearest.yml @@ -0,0 +1,26 @@ +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.json new file mode 100644 index 0000000000..5a9e8797e4 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml new file mode 100644 index 0000000000..04fe852851 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml @@ -0,0 +1,27 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..8ec76964e9 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.json @@ -0,0 +1,39 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [], + "in_latency_window": [] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.yml new file mode 100644 index 0000000000..6d29358f3c --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimary.yml @@ -0,0 +1,22 @@ +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: read +read_preference: + mode: Primary +deprioritized_servers: +- *1 +suitable_servers: [] +in_latency_window: [] diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..4ab48d0b66 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.yml new file mode 100644 index 0000000000..cb08766a3d --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferred.yml @@ -0,0 +1,26 @@ +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json new file mode 100644 index 0000000000..086532e710 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml new file mode 100644 index 0000000000..fed9cd5d65 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml @@ -0,0 +1,27 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 # Deprioritized, but only server matching the tag set. +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..202a277487 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.yml new file mode 100644 index 0000000000..08ca00c0ab --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondary.yml @@ -0,0 +1,26 @@ +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json new file mode 100644 index 0000000000..18926581c3 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml new file mode 100644 index 0000000000..cd98970bd5 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml @@ -0,0 +1,27 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..d37e5545da --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.yml new file mode 100644 index 0000000000..0ae92459ae --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferred.yml @@ -0,0 +1,26 @@ +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json new file mode 100644 index 0000000000..ab51345353 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json @@ -0,0 +1,62 @@ +{ + "topology_description": { + "type": "ReplicaSetNoPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml new file mode 100644 index 0000000000..9863598343 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetNoPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml @@ -0,0 +1,27 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json new file mode 100644 index 0000000000..9f415203b3 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.json @@ -0,0 +1,84 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.yml new file mode 100644 index 0000000000..f63605889e --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllPrimaryPreferred.yml @@ -0,0 +1,34 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - {} +deprioritized_servers: +- *1 +- *2 +- *3 +suitable_servers: +- *3 +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json new file mode 100644 index 0000000000..f8d321b067 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.json @@ -0,0 +1,100 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.yml new file mode 100644 index 0000000000..37e9e055ca --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedAllSecondaryPreferred.yml @@ -0,0 +1,36 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - {} +deprioritized_servers: +- *1 +- *2 +- *3 +suitable_servers: +- *1 +- *2 +in_latency_window: +- *1 +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..5b9d0fa538 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.json @@ -0,0 +1,78 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.yml new file mode 100644 index 0000000000..3dcb9bffdc --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearest.yml @@ -0,0 +1,33 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 26 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *3 +- *2 +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.json new file mode 100644 index 0000000000..021f361480 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.json @@ -0,0 +1,70 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml new file mode 100644 index 0000000000..a8d9e7c3ba --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestOnlyMatchingTags.yml @@ -0,0 +1,34 @@ +# Test when the deprioritized server is the only server with matching tags. +# Serves as a regression test for CDRIVER-6248. +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo + - &3 + address: a:27017 + avg_rtt_ms: 26 + type: RSPrimary + tags: + data_center: tokyo +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.json new file mode 100644 index 0000000000..f1560bc117 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.json @@ -0,0 +1,78 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 25, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 50, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 25, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 25, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.yml new file mode 100644 index 0000000000..134b12907f --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedNearestStateChange.yml @@ -0,0 +1,41 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 25 + type: RSPrimary + tags: + data_center: nyc + +operation: read + +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc + +deprioritized_servers: + - address: "b:27017" + avg_rtt_ms: 50 + type: RSPrimary + tags: + data_center: nyc + +suitable_servers: + - *2 + - *3 + +in_latency_window: + - *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..a1ef5b200f --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.json @@ -0,0 +1,65 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.yml new file mode 100644 index 0000000000..a4c09ba3d4 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimary.yml @@ -0,0 +1,30 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: Primary +deprioritized_servers: +- *3 +suitable_servers: +- *3 +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..7b6da395c5 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,84 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + {} + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.yml new file mode 100644 index 0000000000..83551a51bb --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferred.yml @@ -0,0 +1,34 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - {} +deprioritized_servers: +- *3 +suitable_servers: +- *1 +- *2 +in_latency_window: +- *1 +- *2 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json new file mode 100644 index 0000000000..4002907b3b --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.json @@ -0,0 +1,70 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml new file mode 100644 index 0000000000..7eca837779 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedPrimaryPreferredOnlyMatchingTags.yml @@ -0,0 +1,34 @@ +# Test when the deprioritized server is the only server with matching tags. +# Serves as a regression test for CDRIVER-6248. +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: tokyo + - &2 + address: c:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: tokyo + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - { data_center: nyc } +deprioritized_servers: +- *3 +suitable_servers: +- *3 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..f3dc03db09 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.json @@ -0,0 +1,86 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.yml new file mode 100644 index 0000000000..cfcedf3a57 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondary.yml @@ -0,0 +1,34 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 26 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +- *2 +suitable_servers: +- *1 +- *2 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json new file mode 100644 index 0000000000..2de5bdd4c7 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.json @@ -0,0 +1,70 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml new file mode 100644 index 0000000000..5855c9ecee --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryOnlyMatchingTags.yml @@ -0,0 +1,33 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo + - &3 + address: a:27017 + avg_rtt_ms: 26 + type: RSPrimary + tags: + data_center: tokyo +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 # Deprioritized, but only server matching the tag set. +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..1fd6c654f0 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,78 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.yml new file mode 100644 index 0000000000..1db29405f9 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferred.yml @@ -0,0 +1,33 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: nyc +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +- *2 +suitable_servers: +- *3 +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json new file mode 100644 index 0000000000..7e1f39a606 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.json @@ -0,0 +1,70 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "tokyo" + } + }, + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "tokyo" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "tokyo" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary", + "tags": { + "data_center": "tokyo" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml new file mode 100644 index 0000000000..bdc1a3242d --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/DeprioritizedSecondaryPreferredOnlyMatchingTags.yml @@ -0,0 +1,33 @@ +# Test when the deprioritized server is the only server with matching tags. +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - &2 + address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: tokyo + - &3 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + tags: + data_center: tokyo +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *3 # Primary selected since tags do not apply to primary selection. +in_latency_window: +- *3 diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.json new file mode 100644 index 0000000000..8ec8049efe --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.json @@ -0,0 +1,41 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSPrimary" + }, + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + }, + {} + ] + }, + "suitable_servers": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary" + } + ], + "in_latency_window": [ + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary" + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.yml new file mode 100644 index 0000000000..d25d535da6 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_empty_tags.yml @@ -0,0 +1,27 @@ +# Attempt to select the secondary using an empty tag set. Expect empty tag set to match. +# This is a regression test for RUST-2363. +--- +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: a:27017 + avg_rtt_ms: 5 + type: RSPrimary + # No "tags". + - &2 + address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + # No "tags" +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc # Does not match. + - {} # Empty tag set. +suitable_servers: +- *2 +in_latency_window: +- *2 + diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..4669ee57d5 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,70 @@ +{ + "topology_description": { + "type": "ReplicaSetWithPrimary", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "b:27017", + "avg_rtt_ms": 5, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + }, + { + "address": "c:27017", + "avg_rtt_ms": 100, + "type": "RSSecondary", + "tags": { + "data_center": "nyc" + } + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 26, + "type": "RSPrimary", + "tags": { + "data_center": "nyc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.yml b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.yml new file mode 100644 index 0000000000..71521cee62 --- /dev/null +++ b/source/server-selection/tests/server_selection/ReplicaSetWithPrimary/write/DeprioritizedSecondaryPreferred.yml @@ -0,0 +1,30 @@ +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: a:27017 + avg_rtt_ms: 26 + type: RSPrimary + tags: + data_center: nyc + - address: b:27017 + avg_rtt_ms: 5 + type: RSSecondary + tags: + data_center: nyc + - address: c:27017 + avg_rtt_ms: 100 + type: RSSecondary + tags: + data_center: nyc +operation: write +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.json b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.json new file mode 100644 index 0000000000..1aa730cd21 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.yml b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.yml new file mode 100644 index 0000000000..27fd8fc693 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedNearest.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 10 + type: Mongos +operation: read +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.json b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.json new file mode 100644 index 0000000000..8629c84a2b --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.json @@ -0,0 +1,42 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.yml b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.yml new file mode 100644 index 0000000000..a7bfee07d8 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimary.yml @@ -0,0 +1,20 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: read +read_preference: + mode: Primary +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..800264cfde --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.yml b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.yml new file mode 100644 index 0000000000..3452acdeff --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedPrimaryPreferred.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: read +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.json b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.json new file mode 100644 index 0000000000..42d1e227f1 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.yml b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.yml new file mode 100644 index 0000000000..5db7667238 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondary.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: read +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..eaab0b3af5 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.yml b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.yml new file mode 100644 index 0000000000..a07e44b55f --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/read/DeprioritizedSecondaryPreferred.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.json b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.json new file mode 100644 index 0000000000..5cc2cc5477 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Nearest", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 10, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.yml b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.yml new file mode 100644 index 0000000000..af8ca190d6 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedNearest.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 10 + type: Mongos +operation: write +read_preference: + mode: Nearest + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.json b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.json new file mode 100644 index 0000000000..a936658939 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.json @@ -0,0 +1,42 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Primary" + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.yml b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.yml new file mode 100644 index 0000000000..7fc405c3f2 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimary.yml @@ -0,0 +1,20 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: write +read_preference: + mode: Primary +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json new file mode 100644 index 0000000000..5dc3fa292d --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "PrimaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.yml b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.yml new file mode 100644 index 0000000000..cf2359cc44 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedPrimaryPreferred.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: write +read_preference: + mode: PrimaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.json b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.json new file mode 100644 index 0000000000..8b5cc5251c --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Secondary", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.yml b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.yml new file mode 100644 index 0000000000..06888b0bb7 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondary.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: write +read_preference: + mode: Secondary + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json new file mode 100644 index 0000000000..dcff289cc2 --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.json @@ -0,0 +1,47 @@ +{ + "topology_description": { + "type": "Sharded", + "servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + }, + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "g:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "suitable_servers": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ], + "in_latency_window": [ + { + "address": "h:27017", + "avg_rtt_ms": 5, + "type": "Mongos" + } + ] +} diff --git a/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.yml b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.yml new file mode 100644 index 0000000000..8c1abf2cec --- /dev/null +++ b/source/server-selection/tests/server_selection/Sharded/write/DeprioritizedSecondaryPreferred.yml @@ -0,0 +1,22 @@ +topology_description: + type: Sharded + servers: + - &1 + address: g:27017 + avg_rtt_ms: 5 + type: Mongos + - &2 + address: h:27017 + avg_rtt_ms: 5 + type: Mongos +operation: write +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *2 +in_latency_window: +- *2 diff --git a/source/server-selection/tests/server_selection/Single/read/Deprioritized.json b/source/server-selection/tests/server_selection/Single/read/Deprioritized.json new file mode 100644 index 0000000000..c76fc6c771 --- /dev/null +++ b/source/server-selection/tests/server_selection/Single/read/Deprioritized.json @@ -0,0 +1,54 @@ +{ + "topology_description": { + "type": "Single", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/Single/read/Deprioritized.yml b/source/server-selection/tests/server_selection/Single/read/Deprioritized.yml new file mode 100644 index 0000000000..463a7a5535 --- /dev/null +++ b/source/server-selection/tests/server_selection/Single/read/Deprioritized.yml @@ -0,0 +1,20 @@ +topology_description: + type: Single + servers: + - &1 + address: a:27017 + avg_rtt_ms: 5 + type: Standalone + tags: + data_center: dc +operation: read +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/Single/write/Deprioritized.json b/source/server-selection/tests/server_selection/Single/write/Deprioritized.json new file mode 100644 index 0000000000..099076f2da --- /dev/null +++ b/source/server-selection/tests/server_selection/Single/write/Deprioritized.json @@ -0,0 +1,54 @@ +{ + "topology_description": { + "type": "Single", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "SecondaryPreferred", + "tag_sets": [ + { + "data_center": "nyc" + } + ] + }, + "deprioritized_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "suitable_servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ], + "in_latency_window": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "Standalone", + "tags": { + "data_center": "dc" + } + } + ] +} diff --git a/source/server-selection/tests/server_selection/Single/write/Deprioritized.yml b/source/server-selection/tests/server_selection/Single/write/Deprioritized.yml new file mode 100644 index 0000000000..08780151c9 --- /dev/null +++ b/source/server-selection/tests/server_selection/Single/write/Deprioritized.yml @@ -0,0 +1,20 @@ +topology_description: + type: Single + servers: + - &1 + address: a:27017 + avg_rtt_ms: 5 + type: Standalone + tags: + data_center: dc +operation: write +read_preference: + mode: SecondaryPreferred + tag_sets: + - data_center: nyc +deprioritized_servers: +- *1 +suitable_servers: +- *1 +in_latency_window: +- *1 diff --git a/source/server-selection/tests/server_selection/Unknown/read/ghost.json b/source/server-selection/tests/server_selection/Unknown/read/ghost.json new file mode 100644 index 0000000000..76d3d774e8 --- /dev/null +++ b/source/server-selection/tests/server_selection/Unknown/read/ghost.json @@ -0,0 +1,18 @@ +{ + "topology_description": { + "type": "Unknown", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSGhost" + } + ] + }, + "operation": "read", + "read_preference": { + "mode": "Nearest" + }, + "suitable_servers": [], + "in_latency_window": [] +} diff --git a/source/server-selection/tests/server_selection/Unknown/read/ghost.yml b/source/server-selection/tests/server_selection/Unknown/read/ghost.yml new file mode 100644 index 0000000000..2929a7bc0c --- /dev/null +++ b/source/server-selection/tests/server_selection/Unknown/read/ghost.yml @@ -0,0 +1,11 @@ +topology_description: + type: Unknown + servers: + - address: a:27017 + avg_rtt_ms: 5 + type: RSGhost +operation: read +read_preference: + mode: Nearest +suitable_servers: [] +in_latency_window: [] diff --git a/source/server-selection/tests/server_selection/Unknown/write/ghost.json b/source/server-selection/tests/server_selection/Unknown/write/ghost.json new file mode 100644 index 0000000000..65caa4cd0a --- /dev/null +++ b/source/server-selection/tests/server_selection/Unknown/write/ghost.json @@ -0,0 +1,18 @@ +{ + "topology_description": { + "type": "Unknown", + "servers": [ + { + "address": "a:27017", + "avg_rtt_ms": 5, + "type": "RSGhost" + } + ] + }, + "operation": "write", + "read_preference": { + "mode": "Nearest" + }, + "suitable_servers": [], + "in_latency_window": [] +} diff --git a/source/server-selection/tests/server_selection/Unknown/write/ghost.yml b/source/server-selection/tests/server_selection/Unknown/write/ghost.yml new file mode 100644 index 0000000000..ac794aa530 --- /dev/null +++ b/source/server-selection/tests/server_selection/Unknown/write/ghost.yml @@ -0,0 +1,11 @@ +topology_description: + type: Unknown + servers: + - address: a:27017 + avg_rtt_ms: 5 + type: RSGhost +operation: write +read_preference: + mode: Nearest +suitable_servers: [] +in_latency_window: [] diff --git a/source/server-selection/tojson.sh b/source/server-selection/tojson.sh deleted file mode 100644 index 01462695e6..0000000000 --- a/source/server-selection/tojson.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -for i in `find . -iname '*.yml'`; do - echo "${i%.*}" - jwc yaml2json $i > ${i%.*}.json -done diff --git a/source/server_write_commands.rst b/source/server_write_commands.rst index 50a893956b..72b08c4575 100644 --- a/source/server_write_commands.rst +++ b/source/server_write_commands.rst @@ -1,507 +1,4 @@ -============================ -Write Commands Specification -============================ -:date: May 14, 2014 -:version: 0.9 -:status: Approved - -.. contents:: - --------- - -Goals ------ - -* Method to do writes (insert/update/delete) that declares the write concern up front -* Support for batch operations -* As efficient as possible -* getLastError is disallowed after anything except old style write ops - -Non-Goals ---------- - -* We're not specifying nor building any generic mechanism for feature discovery here -* We're not specifying nor building any generic way to be backwards compatible - -Requests Format ---------------- - -We use a very similar request format for all insert, update, and delete commands, described -below. A request contains parameters on how to apply the batch items and, of course, the batch -items themselves. - -Generic Fields -~~~~~~~~~~~~~~ - -* ````: mandatory, with a string type, where can be ``insert``, - ``update``, or ``delete`` and the content string is a valid collection name against which the - write should be directed. The must be the first key in the document. - - example: :: - - db.runCommand{ { update: "users", ... - -* ``writeConcern``: optional, with a valid write concern BSONObj type, which contains the - journaling, replication, and/or time out parameters that will be used at the end of the batch - application. The default write concern for a mongod server can be set as a replica set - configuration option, or if there is no replica set default the fallback is ``{ w: 1 }``. - - example: :: - - ..., writeConcern: { w: 2 }, ... - - Note that the write concern will be issued at the end of the batch application. - - We also support the "unusual" write concern: ``{ w : 0 }``, which means the user is uninterested - in the result of the write at this time. In this protocol, there is no provision to ignore the - response packet. The protocol, however, sends a very condensed response when it sees that - write concern (e.g. omits everything but the ``ok`` field). - -* ``ordered``: optional, with a boolean type. If true, applies the batch's items in the same - order the items appear, ie. sequentially. If ``ordered`` is false, the server applies the - batch items in no particular order -- possibly, in parallel. The default value is true, - sequential. - - example: :: - - ..., ordered: false, ... - -* ``metadata``: RESERVED FOR MONGOS USE, future use. - -* ``failFast``: NOT YET USED, RESERVED FOR FUTURE USE. Optional, with a boolean type. If false, allows - the batch to continue processing even if some elements in the batch have errors. If true, - the batch will stop on first error(s) it detects (write concern will not be applied). Defaults - to true for ordered batches, and false for unordered batches. - -Batch Items Format -~~~~~~~~~~~~~~~~~~ - -We describe the array of batch items to be applied according to which type of write operation -we'd like to perform. - -.. _insert: - -* For inserts, ``documents`` array: mandatory, with an array of objects type. Objects must be - valid for insertion. - - example: :: - - { insert: "coll", - documents: [ , , ... ], - ... - } - -.. _update: - -* For updates, an ``updates`` array: mandatory, with an array of update objects type. Update - objects must contain the query expression ``q``, an update expression ``u`` fields, and, - optionally, a boolean ``multi`` if several documents may be changed, and a boolean ``upsert`` - if updates can become inserts. Both optional fields default to false. - - example: :: - - { update: "coll", - updates: [ - { q : , u : , multi : , upsert : }, - ... - ], - ... - } - -.. _delete: - -* for deletes a ``deletes`` array: mandatory, with an array of delete object type. A delete - - example: :: - - { delete: "coll", - deletes : [ - { q : , limit : }, - ... - ], - ... - } - - Note that, to avoid accidentally deleting more documents than intended, we force the ``limit`` - field to be present all the time. When all documents that satisfy ``q`` should be - deleted set ``limit`` to zero, as opposed to being omitted. - - Note: The only valid values for ``limit`` is 1 and 0. - -Request Size Limits -~~~~~~~~~~~~~~~~~~~ - -Supporting unlimited batch sizes poses two problems - the BSONObj internal size limit is 16MB + 16KB -(for command overhead), and a small write operation may have a much larger response. In order to -ensure a batch can be correctly processed, two limits must be respected. - -Both of these limits can be found using isMaster(): - -* ``maxBsonObjectSize`` : currently 16MB, this is the maximum size of writes (excluding command overhead) - that should be sent to the server. Documents to be inserted, query documents for updates and - deletes, and update expression documents must be <= this size. Once these documents have been - assembled into a write command the total size may exceed ``maxBsonObjectSize`` by a maximum of - 16KB, allowing users to insert documents up to ``maxBsonObjectSize``. - -* ``maxWriteBatchSize`` : currently 1000, this is the maximum number of inserts, updates, or deletes that - can be included in a write batch. If more than this number of writes are included, the server cannot - guarantee space in the response document to reply to the batch. - -If the batch is too large in size or bytes, the command may fail. - -Response Format ---------------- - -There are two types of responses to any command: - -- a ``command failure``, which indicates the command itself did not complete successfully. Example - command failures include failure to authorize, failure to parse, operation aborted by user, - and unexpected errors during execution (these should be very rare). - -- successful command execution, which for write commands may include write errors. - -Command Failure Fields -~~~~~~~~~~~~~~~~~~~~~~ - -All commands have the same format when they fail unexpectedly: - -``{ ok : 0, code : , errmsg : }`` - -When a batch write command fails this way, like other commands, no guarantees are made about the -state of the writes which were sent. Particular error codes may indicate more about what occurred, -but those codes are outside the scope of this spec. - -General Response Fields -~~~~~~~~~~~~~~~~~~~~~~~ - -Again, like other commands, batch write commands return ``{ ok : 1, ... }`` when they complete -successfully. Importantly, successful execution of a batch write command may include reporting of -unsuccessful writes (write errors) and write concern application (write concern error). - -The main body of a successful response is below: - -.. _ok: - -* ``ok``: Mandatory field, (double)"1" if operation was executed. Does not mean successfully. - For example, duplicate key error will still set ok = 1 - -.. _n: - -* ``n``: Mandatory field, with a positive numeric type or zero. This field contains the aggregated - number of documents successfully affected by the entire write command. This includes the number of - documents inserted, upserted, updated, and deleted. We do not report on the individual number of - documents affected by each batch item. If the application would wish so, then the application - should issue one-item batches. - -.. _writeErrors: - -* ``writeErrors``: Optional field, an array of write errors. For every batch write that had an error, there - is one BSON error document in the array describing the error. - (See the `Error Document`_ section.) - -.. _writeConcernError: - -* ``writeConcernError``: Optional field, which may contain a BSON error document indicating an error occurred while - applying the write concern (or an error indicating that the write concern was not applied). - (See the `Error Document`_ section.) - -Situational Fields -~~~~~~~~~~~~~~~~~~ - -We use the fields above for all responses, regardless of the request type. But some -request types require additional response information, as described below. - -.. _nModified: - -* ``nModified``: Optional field, with a positive numeric type or zero. Zero is the default value. This - field is only and always present for batch updates. ``nModified`` is the physical number of documents - affected by an update, while ``n`` is the logical number of documents matched by the update's query. - For example, if we have 100 documents like :: - - { bizName: "McD", employees: ["Alice", "Bob", "Carol"] } - - and we are adding a single new employee using $addToSet for each business document, ``n`` is useful to - ensure all businesses have been updated, and ``nModified`` is useful to know which businesses actually - added a new employee. - -.. _upserted: - -* ``upserted``: Optional field, with an array type. If any upserts occurred in the batch, - the array contains a BSON document listing the ``index`` and ``_id`` of the newly - upserted document in the database. - -.. _lastOp: - -* ``lastOp``: MONGOD ONLY. Optional field, with a timestamp type, indicating the latest opTime on the - server after all documents were processed. - -* ``electionId``: MONGOD ONLY. Optional ObjectId field representing the last primary election Id. - -Error Document -~~~~~~~~~~~~~~ - -For a write error or a write concern error, the following fields will appear in the error -document: - -.. _code: - -* ``code``: Mandatory field with integer format. Contains a numeric code corresponding to a certain - type of error. - -.. _errInfo: - -* ``errInfo``: Optional field, with a BSONObj format. This field contains structured information - about an error that can be processed programmatically. For example, if a request returns with a - shard version error, we may report the proper shard version as a sub-field here. For another example, - if a write concern timeout occurred, the information previously reported on ``wtimeout`` would be - reported here. - The format of this field depends on the code above. - -.. _errmsg: - -* ``errmsg``: Mandatory field, containing a human-readable version of the error. - -.. _index: - -* ``index``: WRITE ERROR ONLY. The index of the erroneous batch item relative to request batch order. - Batch items indexes start with 0. - - -Examples --------- - -Successful case -~~~~~~~~~~~~~~~ - -Note that ok: 1 by itself does **not** mean that an insert, update, or delete was executed -successfully, -just that the batch was processed successfully. -``ok``: 1 merely means "all operations executed". -``n`` reports how many items from that batch were affected by the operation. - -Insert ------- - - Request: :: - - { insert: "coll", documents: [ {a: 1} ] } - - Response: :: - - { "ok" : 1, "n" : 1 } - - - Request: :: - - { insert: "coll", documents: [ {a: 1}, {b: 2}, {c: 3}, {d: 4} ] } - - Response: :: - - { "ok" : 1, "n" : 4 } - - -Delete ------- - - Request: :: - - { delete: "coll", deletes: [ { q: {b: 2}, limit: 1} ] } - - Response: :: - - { "ok" : 1, "n" : 1 } - - - Request: :: - - { - delete: "coll", - deletes: - [ - { - q: {a: 1}, - limit: 0 - }, - { - q: {c: 3}, - limit: 1 - } - ] - } - - Response: :: - - { "ok" : 1, "n" : 3 } - - - -Update ------- - - Request: :: - - { - update: "coll", - "updates": - [ - { - q: { d: 4 }, - u: { $set: {d: 5} } - } - ] - } - - Response: :: - - { "ok" : 1, "nModified" : 1, "n" : 1 } - - -Checking if command failed -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To check if a write command _failed_ - -:: - - if (ok == 0) { - // The command itself failed (authentication failed.., syntax error) - } else if (writeErrors is array) { - // Couldn't write the data (duplicate key.., out of disk space..) - } else if (writeConcernError is object) { - // Operation took to long on secondary, hit wtimeout ..., - } - -Command failure to parse or authenticate -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Request: :: - - { update: "coll", - updates: [ - { q: {a:1}, x: {$set: {b: 2} } }, - { q: {a:2}, u: {$set: {c: 2} } } - ] - } - - Response: :: - - { ok: 0, - code: , - errmsg: "Failed to parse batched update request, missing update expression 'u' field" - } - - { ok: 0, - code: , - errmsg: "Not authorized to perform update" - } - -Note that no information is given about command execution - if this was performed against a mongos, for example, -the batch may or may not have been partially applied - there is no programmatic way to determine this. - -Write concern failure -~~~~~~~~~~~~~~~~~~~~~ - - Request: :: - - { insert: "coll", documents: [ {a: 1}, {a:2} ], writeConcern: {w: 3, wtimeout: 100} } - - Response: :: - - { ok: 1, - n: 2, - writeConcernError: { - code : , - errInfo: { wtimeout : true }, - errmsg: "Could not replicate operation within requested timeout" - } - } - -Mixed failures -~~~~~~~~~~~~~~ - - Request: :: - - db.coll.ensureIndex( {a:1}, {unique: true} ) - { insert: "coll", - documents: [ - { a: 1 }, - { a: 1 }, - { a: 2 } - ], - ordered: false, - writeConcern: { w: 3, wtimeout: 100 } - } - - Response: :: - - { ok: 1, - n: 2, - writeErrors: [ - { index: 1, - code: , - errmsg: "Attempt to insert duplicate key when unique index is present" - } - ], - writeConcernError: { - code: , - errInfo : { wtimeout : true }, - errmsg: "Could not replicate operation within requested timeout" - } - } - -Note that the field ``n`` in the response came back with 2, even though there are three items -in the batch. This means that there must be an entry in ``writeErrors`` for the item that -failed. Note also that the request turned off ``ordered``, so the write concern error -was hit when trying to replicate batch items 0 and 2. - -Just to illustrate the support for ``{w:0}``, here's how the -response would look, had the request asked for that write concern. - - Response: :: - - { ok: 1 } - -FAQ ---- - -Can a driver still use the OP_INSERT, OP_DELETE, OP_UPDATE? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Yes, a 2.6 server will still support those. But it is unlikely that a 2.8 server would. Of course, when talking to older servers, the usual op codes will continue working the same. An older server is one that reports ``isMaster.maxWireVersion`` to be less than 2 or does not include the field. - -The rationale here is that we may choose to divert all the write traffic to the new -protocol. (This depends on the having the overhead to issue a batch with one item very low.) - -Can an application still issue requests with write concerns {w: 0}? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Yes. The drivers are still required to serve a {w:0} write concern by returning the -control to the application as soon as possible. But a driver should send the request to -the server via a write command and should, therefore, take the corresponding response off the -wire -- even if the caller is not interested in that result. - - -What happens if a driver receives a write request against an old server? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It must convert that request into write operations + gle's and use the old op codes. - -Are we discontinuing the use of getLastError? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Yes but as of 2.6 the existing getLastError behavior is supported for backward compatibility. - -Changelog ---------- - -v0.8 -~~~~ -* First public version - -v0.9 -~~~~ -* Removed text related to bulk operations; see the Bulk API spec for bulk details -* Clarified some paragraphs; re-ordered the response field sections - -.. LocalWords: boolean ie +.. note:: + This specification has been converted to Markdown and renamed to + `server_write_commands.md `_. diff --git a/source/server_write_commands/server_write_commands.md b/source/server_write_commands/server_write_commands.md new file mode 100644 index 0000000000..28ad613bc0 --- /dev/null +++ b/source/server_write_commands/server_write_commands.md @@ -0,0 +1,508 @@ +# Write Commands Specification + +- Status: Accepted +- Minimum Server Version: 2.6 + +______________________________________________________________________ + +## Goals + +- Method to do writes (insert/update/delete) that declares the write concern up front +- Support for batch operations +- As efficient as possible +- getLastError is disallowed after anything except old style write ops + +## Non-Goals + +- We're not specifying nor building any generic mechanism for feature discovery here +- We're not specifying nor building any generic way to be backwards compatible + +## Requests Format + +We use a very similar request format for all insert, update, and delete commands, described below. A request contains +parameters on how to apply the batch items and, of course, the batch items themselves. + +### Generic Fields + +- ``: mandatory, with a string type, where `` can be `insert`, `update`, or `delete` and the content + string is a valid collection name against which the write should be directed. The `` must be the first key + in the document. For example: + +```javascript +db.runCommand{ { update: "users", ... +``` + +- `writeConcern`: optional, with a valid write concern BSONObj type, which contains the journaling, replication, and/or + time out parameters that will be used at the end of the batch application. The default write concern for a mongod + server can be set as a replica set configuration option, or if there is no replica set default the fallback is + `{ w: 1 }`. For example: + +```javascript +..., writeConcern: { w: 2 }, ... +``` + +Note that the write concern will be issued at the end of the batch application. + +We also support the "unusual" write concern: `{ w : 0 }`, which means the user is uninterested in the result of the +write at this time. In this protocol, there is no provision to ignore the response packet. The protocol, however, sends +a very condensed response when it sees that write concern (e.g. omits everything but the `ok` field). + +- `ordered`: optional, with a boolean type. If true, applies the batch's items in the same order the items appear, ie. + sequentially. If `ordered` is false, the server applies the batch items in no particular order -- possibly, in + parallel. The default value is true, sequential. For example: + +```javascript +..., ordered: false, ... +``` + +- `metadata`: RESERVED FOR MONGOS USE, future use. + +- `failFast`: NOT YET USED, RESERVED FOR FUTURE USE. Optional, with a boolean type. If false, allows the batch to + continue processing even if some elements in the batch have errors. If true, the batch will stop on first error(s) + it detects (write concern will not be applied). Defaults to true for ordered batches, and false for unordered + batches. + +### Batch Items Format + +We describe the array of batch items to be applied according to which type of write operation we'd like to perform. + + + +- For inserts, `documents` array: mandatory, with an array of objects type. Objects must be valid for insertion. For + example: + +```javascript +{ insert: "coll", + documents: [ , , ... ], + ... +} +``` + + + +- For updates, an `updates` array: mandatory, with an array of update objects type. Update objects must contain the + query expression `q`, an update expression `u` fields, and, optionally, a boolean `multi` if several documents may + be changed, and a boolean `upsert` if updates can become inserts. Both optional fields default to false. For + example: + +```javascript +{ update: "coll", + updates: [ + { q : , u : , multi : , upsert : }, + ... + ], + ... +} +``` + + + +- for deletes a `deletes` array: mandatory, with an array of delete object type. For example: + +```javascript +{ delete: "coll", + deletes : [ + { q : , limit : }, + ... + ], + ... +} +``` + +Note that, to avoid accidentally deleting more documents than intended, we force the `limit` field to be present all the +time. When all documents that satisfy `q` should be deleted set `limit` to zero, as opposed to being omitted. + +Note: The only valid values for `limit` is 1 and 0. + +### Request Size Limits + +Supporting unlimited batch sizes poses two problems - the BSONObj internal size limit is 16 MiB + 16 KiB (for command +overhead), and a small write operation may have a much larger response. In order to ensure a batch can be correctly +processed, two limits must be respected. + +Both of these limits can be found using hello(): + +- `maxBsonObjectSize` : currently 16 MiB, this is the maximum size of writes (excluding command overhead) that should be + sent to the server. Documents to be inserted, query documents for updates and deletes, and update expression + documents must be \<= this size. Once these documents have been assembled into a write command the total size may + exceed `maxBsonObjectSize` by a maximum of 16 KiB, allowing users to insert documents up to `maxBsonObjectSize`. +- `maxWriteBatchSize` : this is the maximum number of inserts, updates, or deletes that can be included in a write + batch. If more than this number of writes are included, the server cannot guarantee space in the response document + to reply to the batch. + +If the batch is too large in size or bytes, the command may fail. + +## Response Format + +There are two types of responses to any command: + +- a `command failure`, which indicates the command itself did not complete successfully. Example command failures + include failure to authorize, failure to parse, operation aborted by user, and unexpected errors during execution + (these should be very rare). +- successful command execution, which for write commands may include write errors. + +### Command Failure Fields + +All commands have the same format when they fail unexpectedly: + +`{ ok : 0, code : , errmsg : }` + +When a batch write command fails this way, like other commands, no guarantees are made about the state of the writes +which were sent. Particular error codes may indicate more about what occurred, but those codes are outside the scope of +this spec. + +### General Response Fields + +Again, like other commands, batch write commands return `{ ok : 1, ... }` when they complete successfully. Importantly, +successful execution of a batch write command may include reporting of unsuccessful writes (write errors) and write +concern application (write concern error). + +The main body of a successful response is below: + + + +- `ok`: Mandatory field, (double)"1" if operation was executed. Does not mean successfully. For example, duplicate key + error will still set ok = 1 + + + +- `n`: Mandatory field, with a positive numeric type or zero. This field contains the aggregated number of documents + successfully affected by the entire write command. This includes the number of documents inserted, upserted, + updated, and deleted. We do not report on the individual number of documents affected by each batch item. If the + application would wish so, then the application should issue one-item batches. + + + +- `writeErrors`: Optional field, an array of write errors. For every batch write that had an error, there is one BSON + error document in the array describing the error. (See the [Error Document](#error-document) section.) + + + +- `writeConcernError`: Optional field, which may contain a BSON error document indicating an error occurred while + applying the write concern (or an error indicating that the write concern was not applied). (See the + [Error Document](#error-document) section.) + +### Situational Fields + +We use the fields above for all responses, regardless of the request type. But some request types require additional +response information, as described below. + + + +- `nModified`: Optional field, with a positive numeric type or zero. Zero is the default value. This field is only and + always present for batch updates. `nModified` is the physical number of documents affected by an update, while `n` + is the logical number of documents matched by the update's query. For example, if we have 100 documents like : + +```javascript +{ bizName: "McD", employees: ["Alice", "Bob", "Carol"] } +``` + +and we are adding a single new employee using `$addToSet` for each business document, `n` is useful to ensure all +businesses have been updated, and `nModified` is useful to know which businesses actually added a new employee. + + + +- `upserted`: Optional field, with an array type. If any upserts occurred in the batch, the array contains a BSON + document listing the `index` and `_id` of the newly upserted document in the database. + + + +- `lastOp`: MONGOD ONLY. Optional field, with a timestamp type, indicating the latest opTime on the server after all + documents were processed. +- `electionId`: MONGOD ONLY. Optional ObjectId field representing the last primary election Id. + +### Error Document + +For a write error or a write concern error, the following fields will appear in the error document: + + + +- `code`: Mandatory field with integer format. Contains a numeric code corresponding to a certain type of error. + + + +- `errInfo`: Optional field, with a BSONObj format. This field contains structured information about an error that can + be processed programmatically. For example, if a request returns with a shard version error, we may report the + proper shard version as a sub-field here. For another example, if a write concern timeout occurred, the information + previously reported on `wtimeout` would be reported here. The format of this field depends on the code above. + + + +- `errmsg`: Mandatory field, containing a human-readable version of the error. + + + +- `index`: WRITE ERROR ONLY. The index of the erroneous batch item relative to request batch order. Batch items indexes + start with 0. + +## Examples + +### Successful case + +Note that `ok: 1` by itself does **not** mean that an insert, update, or delete was executed successfully, just that the +batch was processed successfully. `ok: 1` merely means "all operations executed". `n` reports how many items from that +batch were affected by the operation. + +## Insert + +Request: + +```javascript +{ insert: "coll", documents: [ {a: 1} ] } +``` + +Response: + +```javascript +{ "ok" : 1, "n" : 1 } +``` + +Request: + +```javascript +{ insert: "coll", documents: [ {a: 1}, {b: 2}, {c: 3}, {d: 4} ] } +``` + +Response: + +```javascript +{ "ok" : 1, "n" : 4 } +``` + +## Delete + +Request: + +```javascript +{ delete: "coll", deletes: [ { q: {b: 2}, limit: 1} ] } +``` + +Response: + +```javascript +{ "ok" : 1, "n" : 1 } +``` + +Request: + +```javascript +{ + delete: "coll", + deletes: + [ + { + q: {a: 1}, + limit: 0 + }, + { + q: {c: 3}, + limit: 1 + } + ] +} +``` + +Response: + +```javascript +{ "ok" : 1, "n" : 3 } +``` + +## Update + +Request: + +```javascript +{ + update: "coll", + "updates": + [ + { + q: { d: 4 }, + u: { $set: {d: 5} } + } + ] +} +``` + +Response: + +```javascript +{ "ok" : 1, "nModified" : 1, "n" : 1 } +``` + +### Checking if command failed + +To check if a write command failed: + +```python +if (ok == 0) { + // The command itself failed (authentication failed.., syntax error) +} else if (writeErrors is array) { + // Couldn't write the data (duplicate key.., out of disk space..) +} else if (writeConcernError is object) { + // Operation took to long on secondary, hit wtimeout ..., +} +``` + +### Command failure to parse or authenticate + +Request: + +```javascript +{ update: "coll", + updates: [ + { q: {a:1}, x: {$set: {b: 2} } }, + { q: {a:2}, u: {$set: {c: 2} } } + ] +} +``` + +Response: + +```javascript +{ ok: 0, + code: , + errmsg: "Failed to parse batched update request, missing update expression 'u' field" +} + +{ ok: 0, + code: , + errmsg: "Not authorized to perform update" +} +``` + +Note that no information is given about command execution - if this was performed against a mongos, for example, the +batch may or may not have been partially applied - there is no programmatic way to determine this. + +### Write concern failure + +Request: + +```javascript +{ insert: "coll", documents: [ {a: 1}, {a:2} ], writeConcern: {w: 3, wtimeout: 100} } +``` + +Response: + +```javascript +{ ok: 1, + n: 2, + writeConcernError: { + code : , + errInfo: { wtimeout : true }, + errmsg: "Could not replicate operation within requested timeout" + } +} +``` + +### Mixed failures + +Request: + +```javascript +db.coll.ensureIndex( {a:1}, {unique: true} ) +{ insert: "coll", + documents: [ + { a: 1 }, + { a: 1 }, + { a: 2 } + ], + ordered: false, + writeConcern: { w: 3, wtimeout: 100 } +} +``` + +Response: + +```javascript +{ ok: 1, + n: 2, + writeErrors: [ + { index: 1, + code: , + errmsg: "Attempt to insert duplicate key when unique index is present" + } + ], + writeConcernError: { + code: , + errInfo : { wtimeout : true }, + errmsg: "Could not replicate operation within requested timeout" + } +} +``` + +Note that the field `n` in the response came back with 2, even though there are three items in the batch. This means +that there must be an entry in `writeErrors` for the item that failed. Note also that the request turned off `ordered`, +so the write concern error was hit when trying to replicate batch items 0 and 2. + +Just to illustrate the support for `{w:0}`, here's how the response would look, had the request asked for that write +concern. + +Response: + +```javascript +{ ok: 1 } +``` + +## FAQ + +### Why are `_id` values generated client-side by default for new documents? + +Though drivers may expose configuration options to prevent this behavior, by default a new `ObjectId` value will be +created client-side before an `insert` operation. + +This design decision primarily stems from the fact that MongoDB is a distributed database and the typical unique +auto-incrementing scalar value most RDBMS' use for generating a primary key would not be robust enough, necessitating +the need for a more robust data type (`ObjectId` in this case). These `_id` values can be generated either on the client +or the server, however when done client-side a new document's `_id` value is immediately available for use without the +need for a network round trip. + +Prior to MongoDB 3.6, an `insert` operation would use the `OP_INSERT` opcode of the wire protocol to send the operation, +and retrieve the results subsequently with a `getLastError` command. If client-side `_id` values were omitted, this +command response wouldn't contain the server-created `_id` values for new documents. Following MongoDB 3.6 when all +commands would be issued using the `OP_MSG` wire protocol opcode (`insert` included), the response to the command still +wouldn't contain the `_id` values for inserted documents. + +### Can a driver still use the `OP_INSERT`, `OP_DELETE`, `OP_UPDATE`? + +The +[legacy opcodes were removed in MongoDB 6.0](https://www.mongodb.com/docs/manual/release-notes/6.0-compatibility/#legacy-opcodes-removed). +As of MongoDB 3.6 these opcodes were superseded by +[OP_MSG](https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/#op_msg), however all server versions up +until 6.0 continued to support the legacy opcodes. + +### Can an application still issue requests with write concerns {w: 0}? + +Yes. The drivers are still required to serve a {w:0} write concern by returning the control to the application as soon +as possible. But a driver should send the request to the server via a write command and should, therefore, take the +corresponding response off the wire -- even if the caller is not interested in that result. + +### What happens if a driver receives a write request against an old server? + +It must convert that request into write operations + gle's and use the old op codes. + +### Are we discontinuing the use of getLastError? + +Yes but as of 2.6 the existing getLastError behavior is supported for backward compatibility. Note also that +`getLastError` was removed in server version 5.1. + +## Changelog + +- 2024-11-11: Add note that getLastError has been removed from the server. + +- 2024-07-31: Migrated from reStructuredText to Markdown. + +- 2024-06-04: Add FAQ entry outlining client-side `_id` value generation Update FAQ to indicate legacy opcodes were + removed + +- 2022-10-05: Revise spec front matter and reformat changelog. + +- 2022-07-25: Remove outdated value for `maxWriteBatchSize` + +- 2021-04-22: Updated to use hello command + +- 2014-05-15: Removed text related to bulk operations; see the Bulk API spec for bulk details. Clarified some + paragraphs; re-ordered the response field sections. + +- 2014-05-14: First public version diff --git a/source/serverless-testing/README.md b/source/serverless-testing/README.md new file mode 100644 index 0000000000..66c7b73496 --- /dev/null +++ b/source/serverless-testing/README.md @@ -0,0 +1,114 @@ +# Atlas Serverless Tests + +- Status: Obsolete +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Obsolescence Notice + +This document is obsolete and there is no longer a requirement for drivers to test against Atlas Serverless. The +specification is preserved for historical purposes only. + +## Introduction + +This file describes a subset of existing tests that drivers MUST use to assert compatibility with Atlas Serverless. + +## Serverless Configuration + +These tests MUST be run against a live Atlas Serverless instance. A new instance MUST be created each time the test +suite is run, and that instance MUST be used for all of the tests required by this specification. Once the tests are +finished, the instance MUST be deleted regardless of the outcome of the tests. The +[serverless directory in the drivers-evergreen-tools repository](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/serverless) +contains scripts for creating and deleting Atlas Serverless instances, and the `config.yml` contains an example +Evergreen configuration that uses them to run the tests. It can take up to 15 minutes or so to provision a new Atlas +Serverless instance, so it is recommended to create one manually via the scripts in drivers-evergreen-tools that can be +reused for the initial implementation of the tests before moving to Evergreen patches. + +Drivers MUST use the +[create-instance.sh](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/serverless/create-instance.sh) +script in `drivers-evergreen-tools` to create a new Atlas Serverless instance for Evergreen tasks. The script writes a +URI for the newly created instance to a YAML expansions file: + +- `SERVERLESS_URI` An SRV connection string to a load balancer fronting a single Atlas Serverless proxy. + +The `expansions.update` Evergreen command can be used to read this file and copy the URI into a `SERVERLESS_URI` +environment variable. + +## Test Runner Configuration + +All tests MUST be run with wire protocol compression and authentication enabled. + +In contrast to the [Load Balancer testing](../load-balancers/tests/README.md), which has separate URIs for load +balancers fronting a single or multiple servers, there is only a single URI for Atlas Serverless testing (i.e. +`SERVERLESS_URI`). + +The TXT record for `SERVERLESS_URI` already specifies `loadBalanced=true` so drivers need not add that. + +`SERVERLESS_URI` does not include authentication credentials. Drivers MUST specify a username and password (see: +[Required Variables](#required-variables)) when connecting to `SERVERLESS_URI`. + +Drivers MUST use `SERVERLESS_URI` to configure both internal clients and clients under test (as described in the +[Unified Test Format spec](../unified-test-format/unified-test-format.md)). + +### Required Variables + +Managing the Atlas Serverless instances and connecting to them requires a few variables to be specified. The variables +marked "private" are confidential and MUST be specified as private Evergreen variables or used only in private Evergreen +projects. If using a public Evergreen project, xtrace MUST be disabled when using these variables to help prevent +accidental leaks. + +- `${SERVERLESS_DRIVERS_GROUP}`: Contains the ID of the Atlas group dedicated to drivers testing of Atlas Serverless. + The backing multi-tenant MongoDB (MTM) MUST have the `SINGLE_TARGET_SERVERLESS_DEPLOYMENT` feature flag enabled + ([CLOUDP-117288](https://jira.mongodb.org/browse/CLOUDP-117288)). +- `${SERVERLESS_API_PUBLIC_KEY}`: The public key required to use the Atlas API for managing Atlas Serverless instances. +- `${SERVERLESS_API_PRIVATE_KEY}`: (private) The private key required to use the Atlas API for managing Atlas Serverless + instances. +- `${SERVERLESS_ATLAS_USER}`: (private) The SCRAM username used to authenticate to any Atlas Serverless instance created + in the drivers testing Atlas group. +- `${SERVERLESS_ATLAS_PASSWORD}`: (private) The SCRAM password used to authenticate to any Atlas Serverless instance + created in the drivers testing Atlas group. + +## Existing Spec Tests + +Unified spec tests from all specifications MUST be run against Atlas Serverless. Since schema version 1.4, unified tests +can specify Atlas Serverless compatibility in their `runOnRequirements`. + +Any prose and legacy spec tests defined in the following specifications MUST be included in a driver's Atlas Serverless +testing suite: + +- CRUD +- Load Balancer +- Retryable Reads +- Retryable Writes +- Sessions +- Transactions (excluding convenient API) + - Note: the killAllSessions command is not supported on Serverless, so the transactions tests may hang if an + individual test leaves a transaction open when it finishes + ([CLOUDP-84298](https://jira.mongodb.org/browse/CLOUDP-84298)). +- Versioned/Stable API +- Client Side Encryption + - Drivers MUST test with a version of the `crypt_shared` shared library that matches the MongoDB Server version + running in Serverless. See + [Using crypt_shared](../client-side-encryption/client-side-encryption.md#enabling-crypt_shared). + +Note that the legacy JSON/YAML test formats for these specifications were updated to include a new `runOnRequirement` +specifically for Atlas Serverless testing. To ensure these requirements are enforced properly, the runner MUST be +informed that it is running against an Atlas Serverless instance through some indicator (e.g. an environment variable). + +Note that since Atlas Serverless testing uses a load balancer fronting a single Atlas Serverless proxy, the connection +string cannot be modified for `useMultipleMongoses`. Drivers SHOULD ignore `useMultipleMongoses` for purposes of Atlas +Serverless testing. + +## Other Tests + +Any other existing tests for cursor behavior that a driver may have implemented independently of any spec requirements +SHOULD also be included in the driver's Atlas Serverless testing suite. Note that change streams are not supported by +the proxy, so their spec and prose tests MUST be skipped. + +## Changelog + +- 2024-09-02: Migrated from reStructuredText to Markdown. +- 2022-10-05: Add spec front matter +- 2022-04-22: Testing uses a load balancer fronting a single proxy. +- 2021-08-25: Update tests for load balanced serverless instances. diff --git a/source/sessions/driver-sessions.md b/source/sessions/driver-sessions.md new file mode 100644 index 0000000000..556ee721d3 --- /dev/null +++ b/source/sessions/driver-sessions.md @@ -0,0 +1,969 @@ +# Sessions Specification + +- Status: Accepted +- Minimum Server Version: 3.6 + +______________________________________________________________________ + +## Abstract + +Version 3.6 of the server introduces the concept of logical sessions for clients. A session is an abstract concept that +represents a set of sequential operations executed by an application that are related in some way. This specification is +limited to how applications start and end sessions. Other specifications define various ways in which sessions are used +(e.g. causally consistent reads, retryable writes, or transactions). + +This specification also discusses how drivers participate in distributing the cluster time throughout a deployment, a +process known as "gossipping the cluster time". While gossipping the cluster time is somewhat orthogonal to sessions, +any driver that implements sessions MUST also implement gossipping the cluster time, so it is included in this +specification. + +## Definitions + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Terms + +**ClientSession** + +The driver object representing a client session and the operations that can be performed on it. Depending on the +language a driver is written in this might be an interface or a class. See also `ServerSession`. + +**Deployment** + +A set of servers that are all part of a single MongoDB cluster. We avoid the word "cluster" because some people +interpret "cluster" to mean "sharded cluster". + +**Explicit session** + +A session that was started explicitly by the application by calling `startSession` and passed as an argument to an +operation. + +**MongoClient** + +The root object of a driver's API. MAY be named differently in some drivers. + +**Implicit session** + +A session that was started implicitly by the driver because the application called an operation without providing an +explicit session. + +**MongoCollection** + +The driver object representing a collection and the operations that can be performed on it. MAY be named differently in +some drivers. + +**MongoDatabase** + +The driver object representing a database and the operations that can be performed on it. MAY be named differently in +some drivers. + +**ServerSession** + +The driver object representing a server session. This type is an implementation detail and does not need to be public. +See also `ClientSession`. + +**Server session ID** + +A server session ID is a token used to identify a particular server session. A driver can ask the server for a session +ID using the `startSession` command or it can generate one locally (see Generating a Session ID locally). + +**Session** + +A session is an abstract concept that represents a set of sequential operations executed by an application that are +related in some way. Other specifications define the various ways in which operations can be related, but examples +include causally consistent reads and retryable writes. + +**Topology** + +The current configuration and state of a deployment. + +**Unacknowledged writes** + +Unacknowledged writes are write operations that are sent to the server without waiting for a reply acknowledging the +write. See the "When using unacknowledged writes" section below for information on how unacknowledged writes interact +with sessions. + +**Network error** + +Any network exception writing to or reading from a socket (e.g. a socket timeout or error). + +**SDAM** + +An abbreviated form of "Server Discovery and Monitoring", specification defined in +[Server Discovery and Monitoring Specification](../server-discovery-and-monitoring/server-discovery-and-monitoring.md). + +## Specification + +Drivers currently have no concept of a session. The driver API will be expanded to provide a way for applications to +start and end sessions and to execute operations in the context of a session. The goal is to expand the API in a way +that introduces no backward breaking changes. Existing applications that don't use sessions don't need to be changed, +and new applications that don't need sessions can continue to be written using the existing API. + +To use sessions an application will call new (or overloaded) methods that take a session parameter. + +## Naming variations + +This specification defines names for new methods and types. To the extent possible, these names SHOULD be used by +drivers. However, where a driver and/or language's naming conventions differ, those naming conventions SHOULD be used. +For example, a driver might name a method `StartSession` or `start_session` instead of `startSession`, or might name a +type `client_session` instead of `ClientSession`. + +## High level summary of the API changes for sessions + +This section is just a high level summary of the new API. Details are provided further on. + +Applications start a new session like this: + +```typescript +options = new SessionOptions(/* various settings */); +session = client.startSession(options); +``` + +The `SessionOptions` will be individually defined in several other specifications. It is expected that the set of +`SessionOptions` will grow over time as sessions are used for new purposes. + +Applications use a session by passing it as an argument to operation methods. For example: + +```typescript +collection.InsertOne(session /* etc. */) +collection.UpdateOne(session /* etc. */) +``` + +Applications end a session like this: + +```typescript +session.endSession() +``` + +This specification does not deal with multi-document transactions, which are covered in +[their own specification](../transactions/transactions.md). + +## MongoClient changes + +`MongoClient` interface summary + +```java +class SessionOptions { + // various other options as defined in other specifications +} + +interface MongoClient { + ClientSession startSession(SessionOptions options); + // other existing members of MongoClient +} +``` + +Each new member is documented below. + +While it is not part of the public API, `MongoClient` also needs a private (or internal) `clusterTime` member +(containing either a BSON document or null) to record the highest `clusterTime` observed in a deployment (as described +below in [Gossipping the cluster time](#gossipping-the-cluster-time)). + +### startSession + +The `startSession` method starts a new `ClientSession` with the provided options. + +It MUST NOT be possible to change the options provided to `startSession` after `startSession` has been called. This can +be accomplished by making the `SessionOptions` class immutable or using some equivalent mechanism that is idiomatic for +your language. + +It is valid to call `startSession` with no options set. This will result in a `ClientSession` that has no effect on the +operations performed in the context of that session, other than to include a session ID in commands sent to the server. + +The `SessionOptions` MAY be a strongly typed class in some drivers, or MAY be a loosely typed dictionary in other +drivers. Drivers MUST define `SessionOptions` in such a way that new options can be added in a backward compatible way +(it is acceptable for backward compatibility to be at the source level). + +A `ClientSession` MUST be associated with a `ServerSession` at the time `startSession` is called. As an implementation +optimization drivers MUST reuse `ServerSession` instances across multiple `ClientSession` instances subject to the rule +that a server session MUST NOT be used by two `ClientSession` instances at the same time (see the Server Session Pool +section). Additionally, a `ClientSession` may only ever be associated with one `ServerSession` for its lifetime. + +Drivers MUST NOT check for session support in `startSession`. Instead, if sessions are not supported, the error MUST be +reported the first time the session is used for an operation (See +[How to Tell Whether a Connection Supports Sessions](#how-to-tell-whether-a-connection-supports-sessions)). + +### Explicit vs implicit sessions + +An explicit session is one started explicitly by the application by calling `startSession`. An implicit session is one +started implicitly by the driver because the application called an operation without providing an explicit session. +Internally, a driver must be able to distinguish between explicit and implicit sessions, but no public API for this is +necessary because an application will never see an implicit session. + +The motivation for starting an implicit session for all methods that don't take an explicit session parameter is to make +sure that all commands that are sent to the server are tagged with a session ID. This improves the ability of an +operations team to monitor (and kill if necessary) long running operations. Tagging an operation with a session ID is +specially useful if a deployment wide operation needs to be killed. + +### Authentication + +When using authentication, using a session requires that only a single user be authenticated. Drivers that still support +authenticating multiple users at once MAY continue to do so, but MUST NOT allow sessions to be used under such +circumstances. + +If `startSession` is called when multiple users are authenticated drivers MUST raise an error with the error message +"Cannot call startSession when multiple users are authenticated." + +If a driver allows authentication to be changed on the fly (presumably few still do) the driver MUST either prevent +`ClientSession` instances from being used with a connection that doesn't have matching authentication or MUST return an +error if such use is attempted. + +## ClientSession + +`ClientSession` instances are not thread safe or fork safe. They can only be used by one thread or process at a time. + +Drivers MUST document the thread-safety and fork-safety limitations of sessions. Drivers MUST NOT attempt to detect +simultaneous use by multiple threads or processes (see Q&A for the rationale). + +ClientSession interface summary: + +```java +interface ClientSession { + MongoClient client; + Optional clusterTime; + SessionOptions options; + BsonDocument sessionId; + + void advanceClusterTime(BsonDocument clusterTime); + void endSession(); +} +``` + +While it is not part of the public API, a `ClientSession` also has a private (or internal) reference to a +`ServerSession`. + +Each member is documented below. + +### client + +This property returns the `MongoClient` that was used to start this `ClientSession`. + +### clusterTime + +This property returns the most recent cluster time seen by this session. If no operations have been executed using this +session this value will be null unless `advanceClusterTime` has been called. This value will also be null when a cluster +does not report cluster times. + +When a driver is gossiping the cluster time it should send the more recent `clusterTime` of the `ClientSession` and the +`MongoClient`. + +### options + +This property returns the `SessionOptions` that were used to start this `ClientSession`. + +### sessionId + +This property returns the session ID of this session. Note that since `ServerSessions` are pooled, different +`ClientSession` instances can have the same session ID, but never at the same time. + +### advanceClusterTime + +This method advances the `clusterTime` for a session. If the new `clusterTime` is greater than the session's current +`clusterTime` then the session's `clusterTime` MUST be advanced to the new `clusterTime`. If the new `clusterTime` is +less than or equal to the session's current `clusterTime` then the session's `clusterTime` MUST NOT be changed. + +This method MUST NOT advance the `clusterTime` in `MongoClient` because we have no way of verifying that the supplied +`clusterTime` is valid. If the `clusterTime` in `MongoClient` were set to an invalid value all future operations with +this `MongoClient` would result in the server returning an error. The `clusterTime` in `MongoClient` should only be +advanced with a `$clusterTime` received directly from a server. + +### endSession + +This method ends a `ClientSession`. + +In languages that have idiomatic ways of disposing of resources, drivers SHOULD support that in addition to or instead +of `endSession`. For example, in the .NET driver `ClientSession` would implement `IDisposable` and the application could +choose to call `session.Dispose` or put the session in a using statement instead of calling `session.endSession`. If +your language has an idiomatic way of disposing resources you MAY choose to implement that in addition to or instead of +`endSession`, whichever is more idiomatic for your language. + +A driver MUST allow multiple calls to `endSession` (or `Dispose`). All calls after the first one are ignored. + +Conceptually, calling `endSession` implies ending the corresponding server session (by calling the `endSessions` +command). As an implementation detail drivers SHOULD cache server sessions for reuse (see Server Session Pool). + +Once a `ClientSession` has ended, drivers MUST report an error if any operations are attempted with that +`ClientSession`. + +## ServerSession + +A `ServerSession` is the driver object that tracks a server session. This object is an implementation detail and does +not need to be public. Drivers may store this information however they choose; this data structure is defined here +merely to describe the operation of the server session pool. + +ServerSession interface summary + +```java +interface ServerSession { + BsonDocument sessionId; + DateTime lastUse; +} +``` + +### sessionId + +This property returns the server session ID. + +### lastUse + +The driver MUST update the value of this property with the current DateTime every time the server session ID is sent to +the server. This allows the driver to track with reasonable accuracy the server's view of when a server session was last +used. + +### Creating a ServerSession + +When a driver needs to create a new `ServerSession` instance the only information it needs is the session ID to use for +the new session. It can either get the session ID from the server by running the `startSession` command, or it can +generate it locally. + +In either case, the lastUse field of the `ServerSession` MUST be set to the current time when the `ServerSession` is +created. + +### Generating a session ID locally + +Running the `startSession` command to get a session ID for a new session requires a round trip to the server. As an +optimization the server allows drivers to generate new session IDs locally and to just start using them. When a server +sees a new session ID that it has never seen before it simply assumes that it is a new session. + +A session ID is a `BsonDocument` that has the following form: + +```typescript +interface SessionId { + id: UUID +} +``` + +Where the UUID is encoded as a BSON binary value of subtype 4. + +The id field of the session ID is a version 4 UUID that must comply with the format described in RFC 4122. Section 4.4 +describes an algorithm for generating correctly-versioned UUIDs from a pseudo-random number generator. + +If a driver is unable to generate a version 4 UUID it MAY instead run the `startSession` command and let the server +generate the session ID. + +## MongoDatabase changes + +All `MongoDatabase` methods that talk to the server MUST send a session ID with the command when connected to a +deployment that supports sessions so that the server can associate the operation with a session ID. + +### New database methods that take an explicit session + +All `MongoDatabase` methods that talk to the server SHOULD be overloaded to take an explicit session parameter. (See +[why is session an explicit parameter?](#why-is-session-an-explicit-parameter).) + +When overloading methods to take a session parameter, the session parameter SHOULD be the first parameter. If +overloading is not possible for your language, it MAY be in a different position or MAY be embedded in an options +structure. + +Methods that have a session parameter MUST check that the session argument is not null and was created by the same +`MongoClient` that this `MongoDatabase` came from and report an error if they do not match. + +### Existing database methods that start an implicit session + +When an existing `MongoDatabase` method that does not take a session is called, the driver MUST behave as if a new +`ClientSession` was started just for this one operation and ended immediately after this operation completes. The actual +implementation will likely involve calling `client.startSession`, but that is not required by this spec. Regardless, +please consult the startSession section to replicate the required steps for creating a session. The driver MUST NOT use +the session if the checked out connection does not support sessions (see +[How to Tell Whether a Connection Supports Sessions](#how-to-tell-whether-a-connection-supports-sessions)) and, in all +cases, MUST NOT consume a server session id until after the connection is checked out and session support is confirmed. + +## MongoCollection changes + +All `MongoCollection` methods that talk to the server MUST send a session ID with the command when connected to a +deployment that supports sessions so that the server can associate the operation with a session ID. + +### New collection methods that take an explicit session + +All `MongoCollection` methods that talk to the server, with the exception of `estimatedDocumentCount`, SHOULD be +overloaded to take an explicit session parameter. (See +[why is session an explicit parameter?](#why-is-session-an-explicit-parameter).) + +When overloading methods to take a session parameter, the session parameter SHOULD be the first parameter. If +overloading is not possible for your language, it MAY be in a different position or MAY be embedded in an options +structure. + +Methods that have a session parameter MUST check that the session argument is not null and was created by the same +`MongoClient` that this `MongoCollection` came from and report an error if they do not match. + +The `estimatedDocumentCount` helper does not support an explicit session parameter. The underlying command, `count`, is +not supported in a transaction, so supporting an explicit session would likely confuse application developers. The +helper returns an estimate of the documents in a collection and causal consistency is unlikely to improve the accuracy +of the estimate. + +### Existing collection methods that start an implicit session + +When an existing `MongoCollection` method that does not take a session is called, the driver MUST behave as if a new +`ClientSession` was started just for this one operation and ended immediately after this operation completes. The actual +implementation will likely involve calling `client.startSession`, but that is not required by this spec. Regardless, +please consult the startSession section to replicate the required steps for creating a session. The driver MUST NOT use +the session if the checked out connection does not support sessions (see +[How to Tell Whether a Connection Supports Sessions](#how-to-tell-whether-a-connection-supports-sessions)) and, in all +cases, MUST NOT consume a server session id until after the connection is checked out and session support is confirmed. + +## Sessions and Cursors + +When an operation using a session returns a cursor, all subsequent `GETMORE` commands for that cursor MUST be run using +the same session ID. + +If a driver decides to run a `KILLCURSORS` command on the cursor, it also MAY be run using the same session ID. See the +Exceptions below for when it is permissible to not include a session ID in a `KILLCURSORS` command. + +## Sessions and Connections + +To reduce the number of `ServerSessions` created, the driver MUST only obtain an implicit session's `ServerSession` +after it successfully checks out a connection. A driver SHOULD NOT attempt to release the acquired session before +connection check in. + +Explicit sessions MAY be changed to allocate a server session similarly. + +## How to Tell Whether a Connection Supports Sessions + +A driver can determine whether a connection supports sessions by checking whether the `logicalSessionTimeoutMinutes` +property of the establishing handshake response has a value or not. If it has a value, sessions are supported. + +In the case of an explicit session, if sessions are not supported, the driver MUST raise an error. In the case of an +implicit session, if sessions are not supported, the driver MUST ignore the session. + +### Possible race condition when checking for session support + +There is a possible race condition that can happen between the time the driver checks whether sessions are supported and +subsequently sends a command to the server: + +- The server might have supported sessions at the time the connection was first opened (and reported a value for + logicalSessionTimeoutMinutes in the initial response to the [handshake](../mongodb-handshake/handshake.md)), but + have subsequently been downgraded to not support sessions. The server does not close the socket in this scenario, so + the driver will conclude that the server at the other end of this connection supports sessions. + +There is nothing that the driver can do about this race condition, and the server will just return an error in this +scenario. + +## Sending the session ID to the server on all commands + +When connected to a server that supports sessions a driver MUST append the session ID to every command it sends to the +server (with the exceptions noted in the following section). It does this by adding a top level `lsid` field to the +command sent to the server. A driver MUST do this without modifying any data supplied by the application (e.g. the +command document passed to runCommand).: + +```typescript +interface ExampleCommandWithLSID { + foo: 1; + lsid: SessionId; +} +``` + +## Exceptions to sending the session ID to the server on all commands + +There are some exceptions to the rule that a driver MUST append the session ID to every command it sends to the server. + +### When opening and authenticating a connection + +A driver MUST NOT append a session ID to any command sent during the process of opening and authenticating a connection. + +### When monitoring the state of a deployment + +A driver MAY omit a session ID in hello and legacy hello commands sent solely for the purposes of monitoring the state +of a deployment. + +### When sending a parallelCollectionScan command + +Sessions are designed for sequential operations and `parallelCollectionScan` is designed for parallel operation. Because +these are fundamentally incompatible goals, drivers MUST NOT append session ID to the `parallelCollectionScan` command +so that the resulting cursors have no associated session ID and thus can be used in parallel. + +### When sending a killCursors command + +A driver MAY omit a session ID in `killCursors` commands for two reasons. First, `killCursors` is only ever sent to a +particular server, so operation teams wouldn't need the `lsid` for cluster-wide killOp. An admin can manually kill the +op with its operation id in the case that it is slow. Secondly, some drivers have a background cursor reaper to kill +cursors that aren't exhausted and closed. Due to GC semantics, it can't use the same `lsid` for `killCursors` as was +used for a cursor's `find` and `getMore`, so there's no point in using any `lsid` at all. + +### When multiple users are authenticated and the session is implicit + +The driver MUST NOT send a session ID from an implicit session when multiple users are authenticated. If possible the +driver MUST NOT start an implicit session when multiple users are authenticated. Alternatively, if the driver cannot +determine whether multiple users are authenticated at the point in time that an implicit session is started, then the +driver MUST ignore any implicit sessions that subsequently end up being used on a connection that has multiple users +authenticated. + +### When using unacknowledged writes + +A session ID MUST NOT be used simultaneously by more than one operation. Since drivers don't wait for a response for an +unacknowledged write a driver would not know when the session ID could be reused. In theory a driver could use a new +session ID for each unacknowledged write, but that would result in many orphaned sessions building up at the server. + +Therefore drivers MUST NOT send a session ID with unacknowledged writes under any circumstances: + +- For unacknowledged writes with an explicit session, drivers SHOULD raise an error. If a driver allows users to provide + an explicit session with an unacknowledged write (e.g. for backwards compatibility), the driver MUST NOT send the + session ID. +- For unacknowledged writes without an explicit session, drivers SHOULD NOT use an implicit session. If a driver creates + an implicit session for unacknowledged writes without an explicit session, the driver MUST NOT send the session ID. + +Drivers MUST document the behavior of unacknowledged writes for both explicit and implicit sessions. + +### When wrapping commands in a `$query` field + +If the driver is wrapping the command in a `$query` field for non-OP_MSG messages in order to pass a readPreference to a +mongos (see +[ReadPreference and Mongos](../find_getmore_killcursors_commands/find_getmore_killcursors_commands.md#readpreference-and-mongos)), +the driver SHOULD NOT add the `lsid` as a top-level field, and MUST add the `lsid` as a field of the `$query` + +```typescript +// Wrapped command: +interface WrappedCommandExample { + $query: { + find: { foo: 1 } + }, + $readPreference: {} +} + +// Correct application of lsid +interface CorrectLSIDUsageExample { + $query: { + find: { foo: 1 }, + lsid: SessionId + }, + $readPreference: {} +} + +// Incorrect application of lsid +interface IncorrectLSIDUsageExample { + $query: { + find: { foo: 1 } + }, + $readPreference: {}, + lsid: SessionId +} +``` + +## Server Commands + +### startSession + +The `startSession` server command has the following format: + +```typescript +interface StartSessionCommand { + startSession: 1; + $clusterTime?: ClusterTime; +} +``` + +The `$clusterTime` field should only be sent when gossipping the cluster time. See the section "Gossipping the cluster +time" for information on `$clusterTime`. + +The `startSession` command MUST be sent to the `admin` database. + +The server response has the following format: + +```typescript +interface StartSessionResponse { + ok: 1; + id: BsonDocument; +} +``` + +In case of an error, the server response has the following format: + +```typescript +interface StartSessionError { + ok: 0; + errmsg: string; + code: number; +} +``` + +When connected to a replica set the `startSession` command MUST be sent to the primary if the primary is available. The +`startSession` command MAY be sent to a secondary if there is no primary available at the time the `startSession` +command needs to be run. + +Drivers SHOULD generate session IDs locally if possible instead of running the `startSession` command, since running the +command requires a network round trip. + +### endSessions + +The `endSessions` server command has the following format: + +```typescript +interface EndSessionCommand { + endSessions: Array; + $clusterTime?: ClusterTime; +} +``` + +The `$clusterTime` field should only be sent when gossipping the cluster time. See the section of "Gossipping the +cluster time" for information on `$clusterTime`. + +The `endSessions` command MUST be sent to the `admin` database. + +The server response has the following format: + +```typescript +interface EndSessionResponse { + ok: 1; +} +``` + +In case of an error, the server response has the following format: + +```typescript +interface EndSessionError { + ok: 0; + errmsg: string; + code: number; +} +``` + +Drivers MUST ignore any errors returned by the `endSessions` command. + +The `endSessions` command MUST be run once when the `MongoClient` instance is shut down. If the number of sessions is +very large the `endSessions` command SHOULD be run multiple times to end 10,000 sessions at a time (in order to avoid +creating excessively large commands). + +When connected to a sharded cluster the `endSessions` command can be sent to any mongos. When connected to a replica set +the `endSessions` command MUST be sent to the primary if the primary is available, otherwise it MUST be sent to any +available secondary. + +## Server Session Pool + +Conceptually, each `ClientSession` can be thought of as having a new corresponding `ServerSession`. However, starting a +server session might require a round trip to the server (which can be avoided by generating the session ID locally) and +ending a session requires a separate round trip to the server. Drivers can operate more efficiently and put less load on +the server if they cache `ServerSession` instances for reuse. To this end drivers MUST implement a server session pool +containing `ServerSession` instances available for reuse. A `ServerSession` pool MUST belong to a `MongoClient` instance +and have the same lifetime as the `MongoClient` instance. + +When a new implicit `ClientSession` is started it MUST NOT attempt to acquire a server session from the server session +pool immediately. When a new explicit `ClientSession` is started it MAY attempt to acquire a server session from the +server session pool immediately. See the algorithm below for the steps to follow when attempting to acquire a +`ServerSession` from the server session pool. + +Note that `ServerSession` instances acquired from the server session pool might have as little as one minute left before +becoming stale and being discarded server side. Drivers MUST document that if an application waits more than one minute +after calling `startSession` to perform operations with that session it risks getting errors due to the server session +going stale before it was used. + +A server session is considered stale by the server when it has not been used for a certain amount of time. The default +amount of time is 30 minutes, but this value is configurable on the server. Servers that support sessions will report +this value in the `logicalSessionTimeoutMinutes` field of the reply to the hello and legacy hello commands. The smallest +reported timeout is recorded in the `logicalSessionTimeoutMinutes` property of the `TopologyDescription`. See the Server +Discovery And Monitoring specification for details. + +When a `ClientSession` is ended it MUST return the server session to the server session pool. See the algorithm below +for the steps to follow when returning a `ServerSession` instance to the server session pool. + +The server session pool has no maximum size. The pool only shrinks when a server session is acquired for use or +discarded. + +When a `MongoClient` instance is closed the driver MUST proactively inform the server that the pooled server sessions +will no longer be used by sending one or more `endSessions` commands to the server. + +The server session pool is modeled as a double ended queue. The algorithms below require the ability to add and remove +`ServerSession` instances from the front of the queue and to inspect and possibly remove `ServerSession` instances from +the back of the queue. The front of the queue holds `ServerSession` instances that have been released recently and +should be the first to be reused. The back of the queue holds `ServerSession` instances that have not been used recently +and that potentially will be discarded if they are not used again before they expire. + +An implicit session MUST be returned to the pool immediately following the completion of an operation. When an implicit +session is associated with a cursor for use with `getMore` operations, the session MUST be returned to the pool +immediately following a `getMore` operation that indicates that the cursor has been exhausted. In particular, it MUST +not wait until all documents have been iterated by the application or until the application disposes of the cursor. For +language runtimes that provide the ability to attach finalizers to objects that are run prior to garbage collection, the +cursor class SHOULD return an implicit session to the pool in the finalizer if the cursor has not already been +exhausted. + +If a driver supports process forking, the session pool needs to be cleared on one side of the forked processes (just +like sockets need to reconnect). Drivers MUST provide a way to clear the session pool without sending `endSessions`. +Drivers MAY make this automatic when the process ID changes. If they do not, they MUST document how to clear the session +pool wherever they document fork support. After clearing the session pool in this way, drivers MUST ensure that sessions +already checked out are not returned to the new pool. + +If a driver has a server session pool and a network error is encountered when executing any command with a +`ClientSession`, the driver MUST mark the associated `ServerSession` as dirty. Dirty server sessions are discarded when +returned to the server session pool. It is valid for a dirty session to be used for subsequent commands (e.g. an +implicit retry attempt, a later command in a bulk write, or a later operation on an explicit session), however, it MUST +remain dirty for the remainder of its lifetime regardless if later commands succeed. + +### Algorithm to acquire a ServerSession instance from the server session pool + +1. If the server session pool is empty create a new `ServerSession` and use it +2. Otherwise remove a `ServerSession` from the front of the queue and examine it: + - If the driver is in load balancer mode, use this `ServerSession`. + - If it has at least one minute left before becoming stale use this `ServerSession` + - If it has less than one minute left before becoming stale discard it (let it be garbage collected) and return to + step 1. + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#session-expiration) for details on session +expiration. + +### Algorithm to return a ServerSession instance to the server session pool + +1. Before returning a server session to the pool a driver MUST first check the server session pool for server sessions + at the back of the queue that are about to expire (meaning they will expire in less than one minute). A driver MUST + stop checking server sessions once it encounters a server session that is not about to expire. Any server sessions + found that are about to expire are removed from the end of the queue and discarded (or allowed to be garbage + collected) +2. Then examine the server session that is being returned to the pool and: + - If this session is marked dirty (i.e. it was involved in a network error) discard it (let it be garbage collected) + - If it will expire in less than one minute discard it (let it be garbage collected) + - If it won't expire for at least one minute add it to the front of the queue + +## Gossipping the cluster time + +Drivers MUST gossip the cluster time when connected to a deployment that uses cluster times. However, drivers MUST NOT +gossip cluster time on SDAM commands. + +Gossipping the cluster time is a process in which the driver participates in distributing the logical cluster time in a +deployment. Drivers learn the current cluster time (from a particular server's perspective) in responses they receive +from servers. Drivers in turn forward the highest cluster time they have seen so far to any server they subsequently +send commands to. + +A driver detects that it MUST participate in gossipping the cluster time when it sees a `$clusterTime` in a response +received from a server. + +### Receiving the current cluster time + +Drivers MUST examine all non-SDAM responses from the server commands to see if they contain a top level field named +`$clusterTime` formatted as follows: + +```typescript +interface ClusterTime { + clusterTime: Timestamp; + signature: { + hash: Binary; + keyId: Int64; + }; +} + +interface AnyServerResponse { + // ... other properties ... + $clusterTime: ClusterTime; +} +``` + +Whenever a driver receives a cluster time from a server it MUST compare it to the current highest seen cluster time for +the deployment. If the new cluster time is higher than the highest seen cluster time it MUST become the new highest seen +cluster time. Two cluster times are compared using only the BsonTimestamp value of the `clusterTime` embedded field (be +sure to include both the timestamp and the increment of the BsonTimestamp in the comparison). The signature field does +not participate in the comparison. + +### Sending the highest seen cluster time + +Whenever a driver sends a non-SDAM command to a server it MUST include the highest seen cluster time in a top level +field called `$clusterTime`, in the same format as it was received in (but see Gossipping with mixed server versions +below). + +### How to compute the `$clusterTime` to send to a server + +When sending `$clusterTime` to the server the driver MUST send the greater of the `clusterTime` values from +`MongoClient` and `ClientSession`. Normally a session's `clusterTime` will be less than or equal to the `clusterTime` in +`MongoClient`, but it could be greater than the `clusterTime` in `MongoClient` if `advanceClusterTime` was called with a +`clusterTime` that came from somewhere else. + +A driver MUST NOT use the `clusterTime` of a `ClientSession` anywhere else except when executing an operation with this +session. This rule protects the driver from the scenario where `advanceClusterTime` was called with an invalid +`clusterTime` by limiting the resulting server errors to the one session. The `clusterTime` of a `MongoClient` MUST NOT +be advanced by any `clusterTime` other than a `$clusterTime` received directly from a server. + +The safe way to compute the `$clusterTime` to send to a server is: + +1. When the `ClientSession` is first started its `clusterTime` is set to null. + +2. When the driver sends `$clusterTime` to the server it should send the greater of the `ClientSession` `clusterTime` + and the `MongoClient` `clusterTime` (either one could be null). + +3. When the driver receives a `$clusterTime` from the server it should advance both the `ClientSession` and the + `MongoClient` `clusterTime`. The `clusterTime` of a `ClientSession` can also be advanced by calling + `advanceClusterTime`. + +This sequence ensures that if the `clusterTime` of a `ClientSession` is invalid only that one session will be affected. +The `MongoClient` `clusterTime` is only updated with `$clusterTime` values known to be valid because they were received +directly from a server. + +### Tracking the highest seen cluster time does not require checking the deployment topology or the server version + +Drivers do not need to check the deployment topology or the server version they are connected to in order to track the +highest seen `$clusterTime`. They simply need to check for the presence of the `$clusterTime` field in responses +received from servers. + +### Gossipping with mixed server versions + +Drivers MUST check that the server they are sending a command to supports `$clusterTime` before adding `$clusterTime` to +the command. A server supports `$clusterTime` when the `maxWireVersion` >= 6. + +This supports the (presumably short lived) scenario where not all servers have been upgraded to 3.6. + +### Do not gossip on SDAM commands + +Drivers MUST NOT gossip cluster time on SDAM commands. In earlier versions of this spec, drivers did gossip cluster time +on SDAM commands, however it was discovered that doing so provides little benefit and can result in a loss of +availability. For example, if the driver attempts to connect to a member of a different replica set, the driver can end +up with an invalid cluster time. Worse, this invalid cluster time may remain the highest for an indefinite time since +clocks between different clusters are not synced. This results in all operations failing until the application is +restarted. To fix this issue we decided it was more robust to stop gossiping on SDAM commands altogether. + +## Test Plan + +See the [README](tests/README.md) for tests. + +## Motivation + +Drivers currently have no concept of a session. The driver API needs to be extended to support sessions. + +## Design Rationale + +The goal is to modify the driver API in such a way that existing programs that don't use sessions continue to compile +and run correctly. This goal is met by defining new methods (or overloads) that take a session parameter. An application +does not need to be modified unless it wants to take advantage of the new features supported by sessions. + +## Backwards Compatibility + +The API changes to support sessions extend the existing API but do not introduce any backward breaking changes. Existing +programs that don't use sessions continue to compile and run correctly. + +## Reference Implementation (always required) + +A reference implementation must be completed before any spec is given status "Final", but it need not be completed +before the spec is "Accepted". While there is merit to the approach of reaching consensus on the specification and +rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to +resolving many discussions of spec details. A final reference implementation must include test code and documentation. + +The C and C# drivers will do initial POC implementations. + +## Future work (optional) + +Use this section to discuss any possible work for a future spec. This could cover issues where no consensus could be +reached but that don't block this spec, changes that were rejected due to unclear use cases, etc. + +## Open questions + +## Q&A + +### Why do we say drivers MUST NOT attempt to detect unsafe multi-threaded or multi-process use of `ClientSession`? + +Because doing so would provide an illusion of safety. It doesn't make these instances thread safe. And even if when +testing an application no such exceptions are encountered, that doesn't prove anything. The application might still be +using the instances in a thread-unsafe way and just didn't happen to do so during a test run. The final argument is that +checking this would require overhead that doesn't provide any clear benefit. + +### Why is session an explicit parameter? + +A previous draft proposed that ClientSession would be a MongoClient-like object added to the object hierarchy: + +```javascript +session = client.startSession(...) +database = session.getDatabase(...) // database is associated with session +collection = database.getCollection(...) // collection is associated with session +// operations on collection implicitly use session +collection.insertOne({}) +session.endSession() +``` + +The central feature of this design is that a MongoCollection (or database, or perhaps a GridFS object) is associated +with a session, which is then an implied parameter to any operations executed using that MongoCollection. + +This API was rejected, with the justification that a ClientSession does not naturally belong to the state of a +MongoCollection. MongoCollection has up to now been a stable long-lived object that could be widely shared, and in most +drivers it is thread safe. Once we associate a ClientSession with it, the MongoCollection object becomes short-lived and +is no longer thread safe. It is a bad sign that MongoCollection's thread safety and lifetime vary depending on how its +parent MongoDatabase is created. + +Instead, we require users to pass session as a parameter to each function: + +```javascript +session = client.startSession(...) +database = client.getDatabase(...) +collection = database.getCollection(...) +// users must explicitly pass session to operations +collection.insertOne(session, {}) +session.endSession() +``` + +### Why does a network error cause the `ServerSession` to be discarded from the pool? + +When a network error is encountered when executing an operation with a `ClientSession`, the operation may be left +running on the server. Re-using this `ServerSession` can lead to parallel operations which violates the rule that a +session must be used sequentially. This results in multiple problems: + +1. killSessions to end an earlier operation would surprisingly also end a later operation. +2. An otherwise unrelated operation that just happens to use that same server session will potentially block waiting for + the previous operation to complete. For example, a transactional write will block a subsequent transactional write. + +### Why do automatic retry attempts re-use a dirty implicit session? + +The retryable writes spec requires that both the original and retry attempt use the same server session. The server will +block the retry attempt until the initial attempt completes at which point the retry attempt will continue executing. + +For retryable reads that use an implicit session, drivers could choose to use a new server session for the retry attempt +however this would lose the information that these two reads are related. + +### Why don't drivers run the endSessions command to cleanup dirty server sessions? + +Drivers do not run the endSessions command when discarding a dirty server session because disconnects should be +relatively rare and the server won't normally accumulate a large number of abandoned dirty sessions. Any abandoned +sessions will be automatically cleaned up by the server after the configured `logicalSessionTimeoutMinutes`. + +### Why must drivers wait to consume a server session until after a connection is checked out? + +The problem that may occur is when the number of concurrent application requests are larger than the number of available +connections, the driver may generate many more implicit sessions than connections. For example with maxPoolSize=1 and +100 threads, 100 implicit sessions may be created. This increases the load on the server since session state is cached +in memory. In the worst case this kind of workload can hit the session limit and trigger TooManyLogicalSessions. + +In order to address this, drivers MUST NOT consume a server session id until after the connection is checked out. This +change will limit the number of "in use" server sessions to no greater than an application's maxPoolSize. + +The language here is specific about obtaining a server session as opposed to creating the implicit session to permit +drivers to take an implementation approach where the implicit session creation logic largely remains unchanged. Implicit +session creation can be left as is, as long as the underlying server resource isn't allocated until it is needed and, +known it will be used, after connection checkout succeeds. + +It is still possible that via explicit sessions or cursors, which hold on to the session they started with, a driver +could over allocate sessions. But those scenarios are extenuating and outside the scope of solving in this spec. + +### Why should drivers NOT attempt to release a serverSession before checking back in the operation's connection? + +There are a variety of cases, such as retryable operations or cursor creating operations, where a `serverSession` must +remain acquired by the `ClientSession` after an operation is attempted. Attempting to account for all these scenarios +has risks that do not justify the potential guaranteed `ServerSession` allocation limiting. + +## Changelog + +- 2025-02-24: Drivers MUST NOT gossip $clusterTime on SDAM commands. +- 2024-05-08: Migrated from reStructuredText to Markdown. +- 2017-09-13: If causalConsistency option is omitted assume true +- 2017-09-16: Omit session ID when opening and authenticating a connection +- 2017-09-18: Drivers MUST gossip the cluster time when they see a `$clusterTime`. +- 2017-09-19: How to safely use initialClusterTime +- 2017-09-29: Add an exception to the rule that `KILLCURSORS` commands always require a session id +- 2017-10-03: startSession and endSessions commands MUST be sent to the admin database +- 2017-10-03: Fix format of endSessions command +- 2017-10-04: Added advanceClusterTime +- 2017-10-06: Added descriptions of explicit and implicit sessions +- 2017-10-17: Implicit sessions MUST NOT be used when multiple users authenticated +- 2017-10-19: Possible race conditions when checking whether a deployment supports sessions +- 2017-11-21: Drivers MUST NOT send a session ID for unacknowledged writes +- 2018-01-10: Note that MongoClient must retain highest clusterTime +- 2018-01-10: Update test plan for drivers without APM +- 2018-01-11: Clarify that sessions require replica sets or sharded clusters +- 2018-02-20: Add implicit/explicit session tests +- 2018-02-20: Drivers SHOULD error if unacknowledged writes are used with sessions +- 2018-05-23: Drivers MUST not use session ID with parallelCollectionScan +- 2018-06-07: Document that estimatedDocumentCount does not support explicit sessions +- 2018-07-19: Justify why session must be an explicit parameter to each function +- 2018-10-11: Session pools must be cleared in child process after fork +- 2019-05-15: A ServerSession that is involved in a network error MUST be discarded +- 2019-10-22: Drivers may defer checking if a deployment supports sessions until the first +- 2021-04-08: Updated to use hello and legacy hello +- 2021-04-08: Adding in behaviour for load balancer mode. +- 2020-05-26: Simplify logic for determining sessions support +- 2022-01-28: Implicit sessions MUST obtain server session after connection checkout succeeds +- 2022-03-24: ServerSession Pooling is required and clarifies session acquisition bounding +- 2022-06-13: Move prose tests to test README and apply new ordering +- 2022-10-05: Remove spec front matter +- 2023-02-24: Defer checking for session support until after connection checkout diff --git a/source/sessions/driver-sessions.rst b/source/sessions/driver-sessions.rst index 0c495b46a7..9cd2e55e32 100644 --- a/source/sessions/driver-sessions.rst +++ b/source/sessions/driver-sessions.rst @@ -1,1196 +1,4 @@ -============================= -Driver Sessions Specification -============================= -:Spec Title: Driver Sessions Specification (See the registry of specs) -:Spec Version: 1.4 -:Author: Robert Stam -:Spec Lead: A\. Jesse Jiryu Davis -:Advisory Group: Jeremy Mikola, Jeff Yemin, Samantha Ritter -:Approver(s): A\. Jesse Jiryu Davis, Jeff Yemin, Bernie Hackett, David Golden, Eliot -:Informed: drivers@, Bryan Reinero, Christopher Hendel -:Status: Accepted (Could be Draft, Accepted, Rejected, Final, or Replaced) -:Type: Standards -:Minimum Server Version: 3.6 (The minimum server version this spec applies to) -:Last Modified: 19-July-2018 - -.. contents:: - --------- - -Abstract -======== - -Version 3.6 of the server introduces the concept of logical sessions for -clients. A session is an abstract concept that represents a set of sequential -operations executed by an application that are related in some way. This -specification is limited to how applications start and end sessions. Other -specifications define various ways in which sessions are used (e.g. causally -consistent reads, retryable writes, or transactions). - -This specification also discusses how drivers participate in distributing the -cluster time throughout a deployment, a process known as "gossipping the -cluster time". While gossipping the cluster time is somewhat orthogonal to -sessions, any driver that implements sessions MUST also implement gossipping -the cluster time, so it is included in this specification. - -This feature is only available in replica sets and sharded clusters. - -Definitions -=========== - -META ----- - -The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, -“SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be -interpreted as described in `RFC 2119 `_. - -Terms ------ - -ClientSession - The driver object representing a client session and the operations that can - be performed on it. Depending on the language a driver is written in this - might be an interface or a class. See also ``ServerSession``. - -Deployment - A set of servers that are all part of a single MongoDB cluster. We avoid the - word "cluster" because some people interpret "cluster" to mean "sharded cluster". - -Explicit session - A session that was started explicitly by the application by calling ``startSession`` - and passed as an argument to an operation. - -MongoClient - The root object of a driver's API. MAY be named differently in some drivers. - -Implicit session - A session that was started implicitly by the driver because the application - called an operation without providing an explicit session. - -MongoCollection - The driver object representing a collection and the operations that can be - performed on it. MAY be named differently in some drivers. - -MongoDatabase - The driver object representing a database and the operations that can be - performed on it. MAY be named differently in some drivers. - -ServerSession - The driver object representing a server session. This type is an - implementation detail and does not need to be public. See also - ``ClientSession``. - -Server session ID - A server session ID is a token used to identify a particular server - session. A driver can ask the server for a session ID using the - ``startSession`` command or it can generate one locally (see Generating a - Session ID locally). - -Session - A session is an abstract concept that represents a set of sequential - operations executed by an application that are related in some way. Other - specifications define the various ways in which operations can be related, - but examples include causally consistent reads and retryable writes. - -Topology - The current configuration and state of a deployment. - -Unacknowledged writes - Unacknowledged writes are write operations that are sent to the server - without waiting for a reply acknowledging the write. See the "When using - unacknowledged writes" section below for information on how unacknowledged - writes interact with sessions. - -Specification -============= - -Drivers currently have no concept of a session. The driver API will be expanded -to provide a way for applications to start and end sessions and to execute -operations in the context of a session. The goal is to expand the API in a way -that introduces no backward breaking changes. Existing applications that don't -use sessions don't need to be changed, and new applications that don't need -sessions can continue to be written using the existing API. - -To use sessions an application will call new (or overloaded) methods that take -a session parameter. - -Naming variations -================= - -This specification defines names for new methods and types. To the extent -possible, these names SHOULD be used by drivers. However, where a driver and/or -language's naming conventions differ, those naming conventions SHOULD be used. -For example, a driver might name a method ``StartSession`` or ``start_session`` instead -of ``startSession``, or might name a type ``client_session`` instead of ``ClientSession``. - -High level summary of the API changes for sessions -================================================== - -This section is just a high level summary of the new API. Details are provided -further on. - -Applications start a new session like this: - -.. code:: typescript - - options = new SessionOptions(...); - session = client.startSession(options); - -The ``SessionOptions`` will be individually defined in several other -specifications. It is expected that the set of ``SessionOptions`` will grow over -time as sessions are used for new purposes. - -Applications use a session by passing it as an argument to operation methods. -For example: - -.. code:: typescript - - collection.InsertOne(session, ...) - collection.UpdateOne(session, ...) - -Applications end a session like this: - -.. code:: typescript - - session.endSession() - -While this specification does not deal with multi-document transactions (which -don't even exist yet), it is expected that when they are implemented they will -be based upon sessions. We can speculate that in the future we might have some -additional transaction-related methods for sessions such as: - -.. code:: typescript - - transaction = session.beginTransaction() - transaction.commit() - transaction.abort() - -However, multi-document transactions are out of scope for this specification. - -MongoClient changes -=================== - -``MongoClient`` interface summary - -.. code:: typescript - - class SessionOptions { - // various other options as defined in other specifications - } - - interface MongoClient { - ClientSession startSession(SessionOptions options); - - // other existing members of MongoClient - } - -Each new member is documented below. - -While it is not part of the public API, ``MongoClient`` also needs a private -(or internal) ``clusterTime`` member (containing either a BSON document or -null) to record the highest ``clusterTime`` observed in a deployment (as -described below in `Gossipping the cluster time`_). - -startSession ------------- - -The ``startSession`` method starts a new ``ClientSession`` with the provided options. - -It MUST NOT be possible to change the options provided to ``startSession`` after -``startSession`` has been called. This can be accomplished by making the -``SessionOptions`` class immutable or using some equivalent mechanism that is -idiomatic for your language. - -It is valid to call ``startSession`` with no options set. This will result in a -``ClientSession`` that has no effect on the operations performed in the context of -that session, other than to include a session ID in commands sent to the -server. - -The ``SessionOptions`` MAY be a strongly typed class in some drivers, or MAY be a -loosely typed dictionary in other drivers. Drivers MUST define ``SessionOptions`` -in such a way that new options can be added in a backward compatible way (it is -acceptable for backward compatibility to be at the source level). - -A ``ClientSession`` MUST be associated with a ``ServerSession`` at the time -``startSession`` is called. As an implementation optimization drivers SHOULD reuse -``ServerSession`` instances across multiple ``ClientSession`` instances subject to the rule that a server -session MUST NOT be used by two ``ClientSession`` instances at the same time (see the Server -Session Pool section). - -``startSession`` MUST report an error if sessions are not supported by the -deployment (see How to Check Whether a Deployment Supports Sessions). - -Explicit vs implicit sessions ------------------------------ - -An explicit session is one started explicitly by the application by calling -``startSession``. An implicit session is one started implicitly by the driver -because the application called an operation without providing an explicit -session. Internally, a driver must be able to distinguish between explicit and -implicit sessions, but no public API for this is necessary because an -application will never see an implicit session. - -The motivation for starting an implicit session for all methods that don't -take an explicit session parameter is to make sure that all commands that are -sent to the server are tagged with a session ID. This improves the ability of -an operations team to monitor (and kill if necessary) long running operations. -Tagging an operation with a session ID is specially useful if a deployment wide -operation needs to be killed. - -Authentication --------------- - -When using authentication, using a session requires that only a single user be -authenticated. Drivers that still support authenticating multiple users at once -MAY continue to do so, but MUST NOT allow sessions to be used under such -circumstances. - -If ``startSession`` is called when multiple users are authenticated drivers MUST -raise an error with the error message "Cannot call startSession when multiple -users are authenticated." - -If a driver allows authentication to be changed on the fly (presumably few -still do) the driver MUST either prevent ``ClientSession`` instances from being used with a -connection that doesn't have matching authentication or MUST return an error if -such use is attempted. - -ClientSession -============= - -``ClientSession`` instances are not thread safe. They can only be used by one -thread at a time. - -Drivers MUST document the thread-safety limitations of sessions. Drivers MUST -NOT attempt to detect simultaneous use by multiple threads (see Q&A for the -rationale). - -ClientSession interface summary - -.. code:: typescript - - interface ClientSession { - MongoClient client; - Optional clusterTime; - SessionOptions options; - BsonDocument sessionId; - - void advanceClusterTime(BsonDocument clusterTime); - void endSession(); - } - -While it is not part of the public API, a ``ClientSession`` also has a private (or -internal) reference to a ``ServerSession``. - -Each member is documented below. - -client ------- - -This property returns the ``MongoClient`` that was used to start this -``ClientSession``. - -clusterTime ------------ - -This property returns the most recent cluster time seen by this session. If no -operations have been executed using this session this value will be null unless -``advanceClusterTime`` has been called. This value will also be null when a -cluster does not report cluster times. - -When a driver is gossiping the cluster time it should send the more recent -``clusterTime`` of the ``ClientSession`` and the ``MongoClient``. - -options -------- - -This property returns the ``SessionOptions`` that were used to start this -``ClientSession``. - -sessionId ---------- - -This property returns the session ID of this session. Note that if server -sessions are pooled, different ``ClientSession`` instances will have the same session ID, -but never at the same time. - -advanceClusterTime ------------------- - -This method advances the ``clusterTime`` for a session. If the new -``clusterTime`` is greater than the session's current ``clusterTime`` then the -session's ``clusterTime`` MUST be advanced to the new ``clusterTime``. If the -new ``clusterTime`` is less than or equal to the session's current -``clusterTime`` then the session's ``clusterTime`` MUST NOT be changed. - -This method MUST NOT advance the ``clusterTime`` in ``MongoClient`` because we -have no way of verifying that the supplied ``clusterTime`` is valid. If the -``clusterTime`` in ``MongoClient`` were set to an invalid value all future -operations with this ``MongoClient`` would result in the server returning an -error. The ``clusterTime`` in ``MongoClient`` should only be advanced with a -``$clusterTime`` received directly from a server. - -endSession ----------- - -This method ends a ``ClientSession``. - -In languages that have idiomatic ways of disposing of resources, drivers SHOULD -support that in addition to or instead of ``endSession``. For example, in the .NET -driver ``ClientSession`` would implement ``IDisposable`` and the application could -choose to call ``session.Dispose`` or put the session in a using statement instead -of calling ``session.endSession``. If your language has an idiomatic way of -disposing resources you MAY choose to implement that in addition to or instead -of ``endSession``, whichever is more idiomatic for your language. - -A driver MUST allow multiple calls to ``endSession`` (or ``Dispose``). All calls after -the first one are ignored. - -Conceptually, calling ``endSession`` implies ending the corresponding server -session (by calling the ``endSessions`` command). As an implementation detail -drivers SHOULD cache server sessions for reuse (see Server Session Pool). - -Once a ``ClientSession`` has ended, drivers MUST report an error if any operations -are attempted with that ``ClientSession``. - -ServerSession -============= - -A ``ServerSession`` is the driver object that tracks a server session. This object -is an implementation detail and does not need to be public. Drivers may store -this information however they choose; this data structure is defined here -merely to describe the operation of the server session pool. - -ServerSession interface summary - -.. code:: typescript - - interface ServerSession { - BsonDocument sessionId; - DateTime lastUse; - } - -sessionId ---------- - -This property returns the server session ID. - -lastUse -------- - -The driver MUST update the value of this property with the current DateTime -every time the server session ID is sent to the server. This allows the driver -to track with reasonable accuracy the server's view of when a server session -was last used. - -Creating a ServerSession ------------------------- - -When a driver needs to create a new ``ServerSession`` instance the only information -it needs is the session ID to use for the new session. It can either get the -session ID from the server by running the ``startSession`` command, or it can -generate it locally. - -In either case, the lastUse field of the ``ServerSession`` MUST be set to the -current time when the ``ServerSession`` is created. - -Generating a session ID locally -------------------------------- - -Running the ``startSession`` command to get a session ID for a new session requires -a round trip to the server. As an optimization the server allows drivers to -generate new session IDs locally and to just start using them. When a server -sees a new session ID that it has never seen before it simply assumes that it -is a new session. - -A session ID is a ``BsonDocument`` that has the following form: - -.. code:: typescript - - { id : } - -Where the UUID is encoded as a BSON binary value of subtype 4. - -The id field of the session ID is a version 4 UUID that must comply with the -format described in RFC 4122. Section 4.4 describes an algorithm for generating -correctly-versioned UUIDs from a pseudo-random number generator. - -If a driver is unable to generate a version 4 UUID it MAY instead run the -``startSession`` command and let the server generate the session ID. - -MongoDatabase changes -===================== - -All ``MongoDatabase`` methods that talk to the server MUST send a session ID -with the command when connected to a deployment that supports sessions so that -the server can associate the operation with a session ID. - -New database methods that take an explicit session --------------------------------------------------- - -All ``MongoDatabase`` methods that talk to the server SHOULD be overloaded to -take an explicit session parameter. (See `why is session an explicit parameter?`_.) - -When overloading methods to take a session parameter, the session parameter -SHOULD be the first parameter. If overloading is not possible for your -language, it MAY be in a different position or MAY be embedded in an options -structure. - -Methods that have a session parameter MUST check that the session argument is -not null and was created by the same ``MongoClient`` that this ``MongoDatabase`` came -from and report an error if they do not match. - -Existing database methods that start an implicit session --------------------------------------------------------- - -When an existing ``MongoDatabase`` method that does not take a session is called, -the driver MUST check whether the deployment supports sessions (See How to -Check Whether a Deployment Supports Session). If sessions are supported, the -driver MUST behave as if a new ``ClientSession`` was started just for this one -operation and ended immediately after this operation completes. The actual -implementation will likely involve calling ``client.startSession``, but that is not -required by this spec. - -MongoCollection changes -======================= - -All ``MongoCollection`` methods that talk to the server MUST send a session ID -with the command when connected to a deployment that supports sessions so that -the server can associate the operation with a session ID. - -New collection methods that take an explicit session ----------------------------------------------------- - -All ``MongoCollection`` methods that talk to the server, with the exception of -`estimatedDocumentCount`, SHOULD be overloaded to take an explicit session -parameter. (See `why is session an explicit parameter?`_.) - -When overloading methods to take a session parameter, the session parameter -SHOULD be the first parameter. If overloading is not possible for your -language, it MAY be in a different position or MAY be embedded in an options -structure. - -Methods that have a session parameter MUST check that the session argument is -not null and was created by the same ``MongoClient`` that this ``MongoCollection`` came -from and report an error if they do not match. - -The `estimatedDocumentCount` helper does not support an explicit session -parameter. The underlying command, `count`, is not supported in a transaction, -so supporting an explicit session would likely confuse application developers. -The helper returns an estimate of the documents in a collection and -causal consistency is unlikely to improve the accuracy of the estimate. - -Existing collection methods that start an implicit session ----------------------------------------------------------- - -When an existing ``MongoCollection`` method that does not take a session is called, -the driver MUST check whether the deployment supports sessions (See How to -Check Whether a Deployment Supports Session). If sessions are supported, the -driver MUST behave as if a new ``ClientSession`` was started just for this one -operation and ended immediately after this operation completes. The actual -implementation will likely involve calling ``client.startSession``, but that is not -required by this spec. - -Sessions and Cursors -==================== - -When an operation using a session returns a cursor, all subsequent ``GETMORE`` -commands for that cursor MUST be run using the same session ID. - -If a driver decides to run a ``KILLCURSORS`` command on the cursor, it also MAY be -run using the same session ID. See the Exceptions below for when it is permissible to not -include a session ID in a ``KILLCURSORS`` command. - -How to Check Whether a Deployment Supports Sessions -=================================================== - -Standalone servers do not support sessions. - -A driver can determine whether a replica set or sharded cluster deployment supports sessions by checking -whether the ``logicalSessionTimeoutMinutes`` property of the ``TopologyDescription`` -has a value or not. If it has a value the deployment supports sessions. -However, in order for this determination to be valid, the driver MUST be -connected to at least one server of a type that is `data-bearing -`_. -Therefore, the detailed steps to determine whether sessions are supported are: - -1. If the ``TopologyDescription`` and connection type indicate that - -* the driver is not connected to any servers, OR -* is not a direct connection AND is not connected to a data-bearing server - -then a driver must do a server selection for any server whose type is data-bearing. -Server selection will either time out or result in a ``TopologyDescription`` that -includes at least one connected, data-bearing server - -2. Having verified in step 1 that the ``TopologyDescription`` includes at least -one connected server a driver can now determine whether sessions are supported -by inspecting the ``TopologyType`` and ``logicalSessionTimeoutMinutes`` property - -Possible race conditions when checking whether a deployment supports sessions ------------------------------------------------------------------------------ - -There are some possible race conditions that can happen between the time the -driver checks whether sessions are supported and subsequently sends a command -to the server: - -* The TopologyDescription might be stale and no longer be accurate because it - has been a few seconds since the last heartbeat - -* The TopologyDescription might be accurate at the time the driver checks - whether sessions are supported, but by the time the driver sends a command to - the server it might no longer be accurate - -* The TopologyDescription might be based on connections to a subset of the - servers and it is possible that as the driver connects to more servers the - driver might discover that sessions aren't supported after all - -* The server might have supported sessions at the time the connection was first - opened (and reported a value for logicalSessionTimeoutMinutes in the initial - response to ismaster), but have subsequently been downgraded to not support - sessions. The server does not close the socket in this scenario, and the driver - will forever conclude that the server at the other end of this connection - supports sessions. This scenario will only be a problem until the next heartbeat - against that server. - -These race conditions are particulary insidious when the driver decides to -start an implicit session based on the conclusion that sessions are supported. -We don't want existing applications that don't use explicit sessions to fail -when using implicit sessions. - -To handle these race conditions, the driver MUST ignore any implicit session if -at the point it is sending a command to a specific server it turns out that -that particular server doesn't support sessions after all. This handles the -first three race conditions. There is nothing that the driver can do about the -final race condition, and the server will just return an error in this -scenario. - -Sending the session ID to the server on all commands -==================================================== - -When connected to a server that supports sessions a driver MUST append the -session ID to every command it sends to the server (with the exceptions noted -in the following section). It does this by adding a -top level ``lsid`` field to the command sent to the server. A driver MUST do this -without modifying any data supplied by the application (e.g. the command -document passed to runCommand).: - -.. code:: typescript - - { commandName: ..., lsid : { id : } } - -Exceptions to sending the session ID to the server on all commands -================================================================== - -There are some exceptions to the rule that a driver MUST append the session ID to -every command it sends to the server. - -When opening and authenticating a connection --------------------------------------------- - -A driver MUST NOT append a session ID to any command sent during the process of -opening and authenticating a connection. - -When monitoring the state of a deployment ------------------------------------------ - -A driver MAY omit a session ID in isMaster commands sent solely for the purposes -of monitoring the state of a deployment. - -When sending a parallelCollectionScan command ---------------------------------------------- - -Sessions are designed for sequential operations and ``parallelCollectionScan`` -is designed for parallel operation. Because these are fundamentally -incompatible goals, drivers MUST NOT append session ID to the -``parallelCollectionScan`` command so that the resulting cursors have -no associated session ID and thus can be used in parallel. - -When sending a killCursors command ----------------------------------- - -A driver MAY omit a session ID in ``killCursors`` commands for two reasons. -First, ``killCursors`` is only ever sent to a particular server, so operation teams -wouldn't need the ``lsid`` for cluster-wide killOp. An admin can manually kill the op with -its operation id in the case that it is slow. Secondly, some drivers have a background -cursor reaper to kill cursors that aren't exhausted and closed. Due to GC semantics, -it can't use the same ``lsid`` for ``killCursors`` as was used for a cursor's ``find`` and ``getMore``, -so there's no point in using any ``lsid`` at all. - -When multiple users are authenticated and the session is implicit ------------------------------------------------------------------ - -The driver MUST NOT send a session ID from an implicit session when multiple -users are authenticated. If possible the driver MUST NOT start an implicit -session when multiple users are authenticated. Alternatively, if the driver -cannot determine whether multiple users are authenticated at the point in time -that an implicit session is started, then the driver MUST ignore any implicit -sessions that subsequently end up being used on a connection that has multiple -users authenticated. - -When using unacknowledged writes --------------------------------- - -A session ID MUST NOT be used simultaneously by more than one operation. Since -drivers don't wait for a response for an unacknowledged write a driver would -not know when the session ID could be reused. In theory a driver could use a -new session ID for each unacknowledged write, but that would result in many -orphaned sessions building up at the server. - -Therefore drivers MUST NOT send a session ID with unacknowledged writes under -any circumstances: - -* For unacknowledged writes with an explicit session, drivers SHOULD raise an - error. If a driver allows users to provide an explicit session with an - unacknowledged write (e.g. for backwards compatibility), the driver MUST NOT - send the session ID. - -* For unacknowledged writes without an explicit session, drivers SHOULD NOT use - an implicit session. If a driver creates an implicit session for - unacknowledged writes without an explicit session, the driver MUST NOT send - the session ID. - -Drivers MUST document the behavior of unacknowledged writes for both explicit -and implicit sessions. - -When wrapping commands in a ``$query`` field --------------------------------------------- - -If the driver is wrapping the command in a ``$query`` field in order to pass a readPreference to a mongos (see `ReadPreference and Mongos <./find_getmore_killcursors_commands.rst#readpreference-and-mongos>`_), the driver SHOULD NOT add the ``lsid`` as a top-level field, and MUST add the ``lsid`` as a field of the ``$query`` - -.. code:: typescript - - // Wrapped command: - { - $query: { - find: { foo: 1 } - }, - $readPreference: {} - } - - // Correct application of lsid - { - $query: { - find: { foo: 1 }, - lsid: <...> - }, - $readPreference: {} - } - - // Incorrect application of lsid. - { - $query: { - find: { foo: 1 } - }, - $readPreference: {}, - lsid: <...> - } - - -Server Commands -=============== - -startSession ------------- - -The ``startSession`` server command has the following format: - -.. code:: typescript - - { startSession : 1, $clusterTime : ... } - -The ``$clusterTime`` field should only be sent when gossipping the cluster time. See the -section "Gossipping the cluster time" for information on ``$clusterTime``. - -The ``startSession`` command MUST be sent to the ``admin`` database. - -The server response has the following format: - -.. code:: typescript - - { - ok : 1, - id : , - } - -In case of an error, the server response has the following format: - -.. code:: typescript - - { ok : 0, errmsg : "...", code : NN } - -When connected to a replica set the ``startSession`` command MUST be sent to the -primary if the primary is available. The ``startSession`` command MAY be sent to a -secondary if there is no primary available at the time the ``startSession`` command -needs to be run. - -Drivers SHOULD generate session IDs locally if possible instead of running the -``startSession`` command, since running the command requires a network round trip. - -endSessions ------------ - -The ``endSessions`` server command has the following format: - -.. code:: typescript - - { endSessions : [ { id : }, { id : }, ... ], $clusterTime : ... } - -The ``$clusterTime`` field should only be sent when gossipping the cluster time. See the -section of "Gossipping the cluster time" for information on ``$clusterTime``. - -The ``endSessions`` command MUST be sent to the ``admin`` database. - -The server response has the following format: - -.. code:: typescript - - { ok : 1 } - -In case of an error, the server response has the following format: - -.. code:: typescript - - { ok : 0, errmsg : "...", code : NN } - -Drivers MUST ignore any errors returned by the ``endSessions`` command. - -Drivers that do not implement a server session pool MUST run the ``endSessions`` -command when the ``ClientSession.endSession`` method is called. Drivers that do -implement a server session pool SHOULD run the ``endSessions`` command once when -the ``MongoClient`` instance is shut down. If the number of sessions is very large -the ``endSessions`` command SHOULD be run multiple times to end 10,000 sessions at -a time (in order to avoid creating excessively large commands). - -When connected to a sharded cluster the ``endSessions`` command can be sent to any -mongos. When connected to a replica set the ``endSessions`` command MUST be sent to -the primary if the primary is available, otherwise it MUST be sent to any -available secondary. - -Server Session Pool -=================== - -Conceptually, each ``ClientSession`` can be thought of as having a new -corresponding ``ServerSession``. However, starting a server session might require a -round trip to the server (which can be avoided by generating the session ID -locally) and ending a session requires a separate round trip to the server. -Drivers can operate more efficiently and put less load on the server if they -cache ``ServerSession`` instances for reuse. To this end drivers SHOULD implement a server -session pool containing ``ServerSession`` instances available for reuse. A -``ServerSession`` pool MUST belong to a ``MongoClient`` instance and have the same -lifetime as the ``MongoClient`` instance. - -If a driver has a server session pool, then when a new ``ClientSession`` is started -it MUST attempt to acquire a server session from the server session pool. See -the algorithm below for the steps to follow when attempting to acquire a -``ServerSession`` from the server session pool. - -Note that ``ServerSession`` instances acquired from the server session pool might have as -little as one minute left before becoming stale and being discarded server -side. Drivers MUST document that if an application waits more than one minute -after calling ``startSession`` to perform operations with that session it risks -getting errors due to the server session going stale before it was used. - -A server session is considered stale by the server when it has not been used -for a certain amount of time. The default amount of time is 30 minutes, but -this value is configurable on the server. Servers that support sessions will -report this value in the ``logicalSessionTimeoutMinutes`` field of the reply -to the ``ismaster`` command. The smallest reported timeout is recorded in the -``logicalSessionTimeoutMinutes`` property of the ``TopologyDescription``. See the -Server Discovery And Monitoring specification for details. - -If a driver has a server session pool, then when a ``ClientSession`` is ended it -MUST return the server session to the server session pool. See the algorithm -below for the steps to follow when returning a ``ServerSession`` instance to the server -session pool. - -The server session pool has no maximum size. The pool only shrinks when a -server session is acquired for use or discarded. - -If a driver has a server session pool, then when a ``MongoClient`` instance is -closed the driver MUST proactively inform the server that the pooled server -sessions will no longer be used by sending one or more ``endSessions`` commands to the -server. - -The server session pool is modeled as a double ended queue. The algorithms -below require the ability to add and remove ``ServerSession`` instances from the front of -the queue and to inspect and possibly remove ``ServerSession`` instances from the back of -the queue. The front of the queue holds ``ServerSession`` instances that have been released -recently and should be the first to be reused. The back of the queue holds -``ServerSession`` instances that have not been used recently and that potentially will be -discarded if they are not used again before they expire. - -An implicit session MUST be returned to the pool immediately following the completion of -an operation. When an implicit session is associated with a cursor for use with ``getMore`` -operations, the session MUST be returned to the pool immediately following a ``getMore`` -operation that indicates that the cursor has been exhausted. In particular, it MUST not wait -until all documents have been iterated by the application or until the application disposes -of the cursor. For language runtimes that provide the ability to attach finalizers to objects -that are run prior to garbage collection, the cursor class SHOULD return an implicit session -to the pool in the finalizer if the cursor has not already been exhausted. - -Algorithm to acquire a ServerSession instance from the server session pool --------------------------------------------------------------------------- - -1. If the server session pool is empty create a new ``ServerSession`` and use it - -2. Otherwise remove a ``ServerSession`` from the front of the queue and examine it: - * If it has at least one minute left before becoming stale use this ``ServerSession`` - * If it has less than one minute left before becoming stale discard it (let it be garbage collected) and return to step 1. - -Algorithm to return a ServerSession instance to the server session pool ------------------------------------------------------------------------ - -1. Before returning a server session to the pool a driver MUST first check the - server session pool for server sessions at the back of the queue that are about - to expire (meaning they will expire in less than one minute). A driver MUST - stop checking server sessions once it encounters a server session that is not - about to expire. Any server sessions found that are about to expire are removed - from the end of the queue and discarded (or allowed to be garbage collected) - -2. Then examine the server session that is being returned to the pool and: - * If it won't expire for at least one minute add it to the front of the queue - * If it will expire in less than one minute discard it (let it be garbage collected) - -Gossipping the cluster time -=========================== - -Drivers MUST gossip the cluster time when connected to a deployment that uses -cluster times. - -Gossipping the cluster time is a process in which the driver participates in -distributing the logical cluster time in a deployment. Drivers learn the -current cluster time (from a particular server's perspective) in responses -they receive from servers. Drivers in turn forward the highest cluster -time they have seen so far to any server they subsequently send commands -to. - -A driver detects that it MUST participate in gossipping the cluster time when it sees -a ``$clusterTime`` in a response received from a server. - -Receiving the current cluster time ----------------------------------- - -Drivers MUST examine all responses to server -commands to see if they contain a top level field named ``$clusterTime`` formatted -as follows: - -.. code:: typescript - - { - ... - $clusterTime : { - clusterTime : , - signature : { - hash : , - keyId : - } - }, - ... - } - -Whenever a driver receives a cluster time from a server it MUST compare it to -the current highest seen cluster time for the deployment. If the new cluster time -is higher than the highest seen cluster time it MUST become the new highest -seen cluster time. Two cluster times are compared using only the BsonTimestamp -value of the ``clusterTime`` embedded field (be sure to include both the timestamp -and the increment of the BsonTimestamp in the comparison). The signature field -does not participate in the comparison. - -Sending the highest seen cluster time -------------------------------------- - -Whenever a driver sends a command to a server it MUST include the highest -seen cluster time in a top level field called ``$clusterTime``, in the same format -as it was received in (but see Gossipping with mixed server versions below). - -How to compute the $clusterTime to send to a server ---------------------------------------------------- - -When sending ``$clusterTime`` to the server the driver MUST send the greater of -the ``clusterTime`` values from ``MongoClient`` and ``ClientSession``. Normally -a session's ``clusterTime`` will be less than or equal to the ``clusterTime`` -in ``MongoClient``, but it could be greater than the ``clusterTime`` in -``MongoClient`` if ``advanceClusterTime`` was called with a ``clusterTime`` -that came from somewhere else. - -A driver MUST NOT use the ``clusterTime`` of a ``ClientSession`` anywhere else -except when executing an operation with this session. This rule protects the -driver from the scenario where ``advanceClusterTime`` was called with an -invalid ``clusterTime`` by limiting the resulting server errors to the one -session. The ``clusterTime`` of a ``MongoClient`` MUST NOT be advanced by any -``clusterTime`` other than a ``$clusterTime`` received directly from a server. - -The safe way to compute the ``$clusterTime`` to send to a server is: - -1. When the ``ClientSession`` is first started its ``clusterTime`` is set to -null. - -2. When the driver sends ``$clusterTime`` to the server it should send the -greater of the ``ClientSession`` ``clusterTime`` and the ``MongoClient`` -``clusterTime`` (either one could be null). - -3. When the driver receives a ``$clusterTime`` from the server it should advance -both the ``ClientSession`` and the ``MongoClient`` ``clusterTime``. The ``clusterTime`` -of a ``ClientSession`` can also be advanced by calling ``advanceClusterTime``. - -This sequence ensures that if the ``clusterTime`` of a ``ClientSession`` is invalid only that -one session will be affected. The ``MongoClient`` ``clusterTime`` is only -updated with ``$clusterTime`` values known to be valid because they were -received directly from a server. - -Tracking the highest seen cluster time does not require checking the deployment topology or the server version --------------------------------------------------------------------------------------------------------------- - -Drivers do not need to check the deployment topology or the server version they -are connected to in order to track the highest seen ``$clusterTime``. They simply -need to check for the presence of the ``$clusterTime`` field in responses received -from servers. - -Gossipping with mixed server versions -------------------------------------- - -Drivers MUST check that the server they are sending a command to supports -``$clusterTime`` before adding ``$clusterTime`` to the command. A server supports -``$clusterTime`` when the ``maxWireVersion`` >= 6. - -This supports the (presumably short lived) scenario where not all servers have -been upgraded to 3.6. - -Test Plan -========= - -The test plan SHOULD be run against both replica set and sharded cluster -topologies. It MUST NOT be run against a standalone server. - -1. Pool is LIFO. - * This test applies to drivers with session pools. - * Call ``MongoClient.startSession`` twice to create two sessions, let us call them ``A`` and ``B``. - * Call ``A.endSession``, then ``B.endSession``. - * Call ``MongoClient.startSession``: the resulting session must have the same session ID as ``B``. - * Call ``MongoClient.startSession`` again: the resulting session must have the same session ID as ``A``. - -2. ``$clusterTime`` in commands - * Turn ``heartbeatFrequencyMS`` up to a very large number. - * Register a command-started and a command-succeeded APM listener. If the driver has no APM support, inspect commands/replies in another idiomatic way, such as monkey-patching or a mock server. - * Send a ``ping`` command to the server with the generic ``runCommand`` method. - * Assert that the command passed to the command-started listener includes ``$clusterTime`` if and only if ``maxWireVersion`` >= 6. - * Record the ``$clusterTime``, if any, in the reply passed to the command-succeeded APM listener. - * Send another ``ping`` command. - * Assert that ``$clusterTime`` in the command passed to the command-started listener, if any, equals the ``$clusterTime`` in the previous server reply. (Turning ``heartbeatFrequencyMS`` up prevents an intervening heartbeat from advancing the ``$clusterTime`` between these final two steps.) - - Repeat for: - * An aggregate command from the ``aggregate`` helper method - * A find command from the ``find`` helper method - * An insert command from the ``insert_one`` helper method - -3. Test explicit and implicit session arguments - * Register a command-started APM listener. If the driver has no APM support, inspect commands in another idiomatic way, such as monkey-patching or a mock server. - * Create ``client1`` - * Get ``database`` from ``client1`` - * Get ``collection`` from ``database`` - * Start ``session`` from ``client1`` - * Call ``collection.insertOne(session,...)`` - * Assert that the command passed to the command-started listener contained the session ``lsid`` from ``session``. - * Call ``collection.insertOne(,...)`` (*without* a session argument) - * Assert that the command passed to the command-started listener contained a session ``lsid``. - - Repeat for: - * All methods that take a session parameter. - -4. Test that session argument is for the right client - * Create ``client1`` and ``client2`` - * Get ``database`` from ``client1`` - * Get ``collection`` from ``database`` - * Start ``session`` from ``client2`` - * Call ``collection.insertOne(session,...)`` - * Assert that an error was reported because ``session`` was not started from ``client1`` - - Repeat for: - * All methods that take a session parameter. - -5. Test that no further operations can be performed using a session after ``endSession`` has been called - * Start a ``session`` - * End the ``session`` - * Call ``collection.InsertOne(session, ...)`` - * Assert that the proper error was reported - - Repeat for: - * All methods that take a session parameter. - - If your driver implements a platform dependent idiomatic disposal pattern, test - that also (if the idiomatic disposal pattern calls ``endSession`` it would be - sufficient to only test the disposal pattern since that ends up calling - ``endSession``). - -6. Authenticating as multiple users suppresses implicit sessions - * Skip this test if your driver does not allow simultaneous authentication with multiple users - * Authenticate as two users - * Call ``findOne`` with no explicit session - * Capture the command sent to the server - * Assert that the command sent to the server does not have an ``lsid`` field - -7. Client-side cursor that exhausts the results on the initial query immediately returns the implicit session -to the pool. - - * Insert two documents into a collection - * Execute a find operation on the collection and iterate past the first document - * Assert that the implicit session is returned to the pool. This can be done in several ways: - - * Track in-use count in the server session pool and assert that the count has dropped to zero - * Track the lsid used for the find operation (e.g. with APM) and then do another operation and - assert that the same lsid is used as for the find operation. - -8. Client-side cursor that exhausts the results after a ``getMore`` immediately returns the implicit session -to the pool. - - * Insert five documents into a collection - * Execute a find operation on the collection with batch size of 3 - * Iterate past the first four documents, forcing the final ``getMore`` operation - * Assert that the implicit session is returned to the pool prior to iterating past the last document - -9. At the end of every individual functional test of the driver, there SHOULD be an assertion that -there are no remaining sessions checked out from the pool. This may require changes to existing tests to -ensure that they close any explicit client sessions and any unexhausted cursors. - -10. For every combination of topology and readPreference, ensure that ``find`` and ``getMore`` both send the same session id - - * Insert three documents into a collection - * Execute a ``find`` operation on the collection with a batch size of 2 - * Assert that the server receives a non-zero lsid - * Iterate through enough documents (3) to force a ``getMore`` - * Assert that the server receives a non-zero lsid equal to the lsid that ``find`` sent. - - -Tests that only apply to drivers that have not implemented OP_MSG and are still using OP_QUERY ----------------------------------------------------------------------------------------------- - -1. For a command to a mongos that includes a readPreference, verify that the ``lsid`` on query commands is added inside the ``$query`` field, and NOT as a top-level field - - -Tests that only apply to drivers that allow authentication to be changed on the fly ------------------------------------------------------------------------------------ - -1. Authenticating as a second user after starting a session results in a server error - * Authenticate as the first user - * Start a session by calling ``startSession`` - * Authenticate as a second user - * Call ``findOne`` using the session as an explicit session - * Assert that the driver returned an error because multiple users are authenticated - -2. Driver verifies that session is owned by the current user - * Authenticate as user A - * Start a session by calling ``startSession`` - * Logout user A - * Authenticate as user B - * Call ``findOne`` using the session as an explicit session - * Assert that the driver returned an error because the session is owned by a different user - -Motivation -========== - -Drivers currently have no concept of a session. The driver API needs to be -extended to support sessions. - -Design Rationale -================ - -The goal is to modify the driver API in such a way that existing programs that -don't use sessions continue to compile and run correctly. This goal is met by -defining new methods (or overloads) that take a session parameter. An -application does not need to be modified unless it wants to take advantage of -the new features supported by sessions. - -Backwards Compatibility -======================= - -The API changes to support sessions extend the existing API but do not -introduce any backward breaking changes. Existing programs that don't use -sessions continue to compile and run correctly. - -Reference Implementation (always required) -========================================== - -A reference implementation must be completed before any spec is given status -"Final", but it need not be completed before the spec is “Accepted”. While -there is merit to the approach of reaching consensus on the specification and -rationale before writing code, the principle of "rough consensus and running -code" is still useful when it comes to resolving many discussions of spec -details. A final reference implementation must include test code and -documentation. - -The C and C# drivers will do initial POC implementations. - -Future work (optional) -====================== - -Use this section to discuss any possible work for a future spec. This could -cover issues where no consensus could be reached but that don’t block this -spec, changes that were rejected due to unclear use cases, etc. - -Open questions -============== - -Q&A -=== - -Why do we say drivers MUST NOT attempt to detect unsafe multi-threaded use of ``ClientSession``? ------------------------------------------------------------------------------------------------- - -Because doing so would provide an illusion of safety. It doesn't make these -instances thread safe. And even if when testing an application no such exceptions -are encountered, that doesn't prove anything. The application might still be -using the instances in a thread-unsafe way and just didn't happen to do so during -a test run. The final argument is that checking this would require overhead -that doesn't provide any clear benefit. - -Why is session an explicit parameter? -------------------------------------- - -A previous draft proposed that ClientSession would be a MongoClient-like object added to the object hierarchy:: - - session = client.startSession(...) - database = session.getDatabase(...) // database is associated with session - collection = database.getCollection(...) // collection is associated with session - // operations on collection implicitly use session - collection.insertOne({}) - session.endSession() - -The central feature of this design is that a MongoCollection (or database, or perhaps a GridFS object) is associated with a session, which is then an implied parameter to any operations executed using that MongoCollection. - -This API was rejected, with the justification that a ClientSession does not naturally belong to the state of a MongoCollection. MongoCollection has up to now been a stable long-lived object that could be widely shared, and in most drivers it is thread safe. Once we associate a ClientSession with it, the MongoCollection object becomes short-lived and is no longer thread safe. It is a bad sign that MongoCollection's thread safety and lifetime vary depending on how its parent MongoDatabase is created. - -Instead, we require users to pass session as a parameter to each function:: - - session = client.startSession(...) - database = client.getDatabase(...) - collection = database.getCollection(...) - // users must explicitly pass session to operations - collection.insertOne(session, {}) - session.endSession() - -Change log -========== - -:2018-07-19: Justify why session must be an explicit parameter to each function -:2018-06-07: Document that estimatedDocumentCount does not support explicit sessions -:2018-05-23: Document that parallelCollectionScan helpers do not support implicit sessions -:2017-09-13: If causalConsistency option is ommitted assume true -:2017-09-16: Omit session ID when opening and authenticating a connection -:2017-09-18: Drivers MUST gossip the cluster time when they see a $clusterTime -:2017-09-19: How to safely use initialClusterTime -:2017-09-29: Add an exception to the rule that ``KILLCURSORS`` commands always require a session id -:2017-10-03: startSession and endSessions commands MUST be sent to the admin database -:2017-10-03: Fix format of endSessions command -:2017-10-04: Added advanceClusterTime -:2017-10-06: Added descriptions of explicit and implicit sessions -:2017-10-17: Implicit sessions MUST NOT be used when multiple users authenticated -:2017-10-19: Possible race conditions when checking whether a deployment supports sessions -:2017-11-21: Drivers MUST NOT send a session ID for unacknowledged writes -:2018-01-10: Note that MongoClient must retain highest clusterTime -:2018-01-10: Update test plan for drivers without APM -:2018-01-11: Clarify that sessions require replica sets or sharded clusters -:2018-02-20: Add implicit/explicit session tests -:2018-02-20: Drivers SHOULD error if unacknowledged writes are used with sessions -:2018-05-23: Drivers MUST not use session ID with parallelCollectionScan +.. note:: + This specification has been converted to Markdown and renamed to + `driver-sessions.md `_. diff --git a/source/sessions/snapshot-sessions.md b/source/sessions/snapshot-sessions.md new file mode 100644 index 0000000000..85720f6486 --- /dev/null +++ b/source/sessions/snapshot-sessions.md @@ -0,0 +1,288 @@ +# Snapshot Reads Specification + +- Status: Accepted +- Minimum Server Version: 5.0 + +______________________________________________________________________ + +## Abstract + +Version 5.0 of the server introduces support for read concern level "snapshot" (non-speculative) for read commands +outside of transactions, including on secondaries. This spec builds upon the +[Sessions Specification](./driver-sessions.md) to define how an application requests "snapshot" level read concern and +how a driver interacts with the server to implement snapshot reads. + +## Definitions + +### META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +### Terms + +**ClientSession** + +The driver object representing a client session and the operations that can be performed on it. + +**MongoClient** + +The root object of a driver's API. MAY be named differently in some drivers. + +**MongoCollection** + +The driver object representing a collection and the operations that can be performed on it. MAY be named differently in +some drivers. + +**MongoDatabase** + +The driver object representing a database and the operations that can be performed on it. MAY be named differently in +some drivers. + +**ServerSession** + +The driver object representing a server session. + +**Session** + +A session is an abstract concept that represents a set of sequential operations executed by an application that are +related in some way. This specification defines how sessions are used to implement snapshot reads. + +**Snapshot reads** + +Reads with read concern level `snapshot` that occur outside of transactions on both the primary and secondary nodes, +including in sharded clusters. Snapshots reads are majority committed reads. + +**Snapshot timestamp** + +Snapshot timestamp, representing timestamp of the first supported read operation (i.e. find/aggregate/distinct) in the +session. The server creates a cursor in response to a snapshot find/aggregate command and reports `atClusterTime` within +the `cursor` field in the response. For the distinct command the server adds a top-level `atClusterTime` field to the +response. The `atClusterTime` field represents the timestamp of the read and is guaranteed to be majority committed. + +## Specification + +An application requests snapshot reads by creating a `ClientSession` with options that specify that snapshot reads are +desired and optionally specifying a `snapshotTime`. An application then passes the session as an argument to methods in +the `MongoDatabase` and `MongoCollection` classes. Read operations (find/aggregate/distinct) performed against that +session will be read from the same snapshot. + +## High level summary of the API changes for snapshot reads + +Snapshot reads are built on top of client sessions. + +Applications will start a new client session for snapshot reads and possibly retrieve the snapshot time like this: + +```typescript +options = new SessionOptions(snapshot = true, snapshotTime = timestampValue); +session = client.startSession(options); +snapshotTime = session.snapshotTime; +``` + +All read operations performed using this session will be read from the same snapshot. + +If no value is provided for `snapshot` a value of false is implied. `snapshotTime` is an optional parameter and if not +passed the snapshot time will be set internally after the first find/aggregate/distinct operation inside the session. + +There are no MongoDatabase, MongoClient, or MongoCollection API changes. + +## SessionOptions changes + +`SessionOptions` change summary + +```typescript +class SessionOptions { + Optional snapshot; + Optional snapshotTime; + + // other options defined by other specs +} +``` + +In order to support snapshot reads two properties called `snapshot` and `snapshotTime` are added to `SessionOptions`. +Applications set `snapshot` when starting a client session to indicate whether they want snapshot reads and optionally +set `snapshotTime` to specify the desired snapshot time beforehand. All read operations performed using that client +session will share the same snapshot. + +Each new member is documented below. + +### snapshot + +Applications set `snapshot` when starting a session to indicate whether they want snapshot reads. + +Note that the `snapshot` property is optional. The default value of this property is false. + +Snapshot reads and causal consistency are mutually exclusive. Therefore if `snapshot` is set to true, +`causalConsistency` must be false. Client MUST throw an error if both `snapshot` and `causalConsistency` are set to +true. Snapshot reads are supported on both primaries and secondaries. + +### snapshotTime + +Applications set `snapshotTime` when starting a snapshot session to specify the desired snapshot time. + +Note that the `snapshotTime` property is optional. The default value of this property is null. + +Client MUST throw an error if `snapshotTime` is set and `snapshot` is not set to true. + +## ClientSession changes + +A readonly property called `snapshotTime` will be added to `ClientSession` that allows applications to retrieve the +snapshot time of the session: + +```typescript +class ClientSession { + readonly Optional snapshotTime; + + // other options defined by other specs +} +``` + +On a non-snapshot session, the value of `snapshotTime` is undefined. Drivers MUST either raise an error when the +property is accessed or return an unset value. + +Transactions are not allowed with snapshot sessions. Calling `session.startTransaction(options)` on a snapshot session +MUST raise an error. + +## ReadConcern changes + +`snapshot` added to [ReadConcernLevel enumeration](../read-write-concern/read-write-concern.md#read-concern). + +## Server Commands + +There are no new server commands related to snapshot reads. Instead, snapshot reads are implemented by: + +1. If `snapshotTime` is specified in `SessionOptions`, saving the value in a `snapshotTime` property of the + `ClientSession`. +2. If `snapshotTime` is not specified in `SessionOptions`, saving the `atClusterTime` returned by 5.0+ servers for the + first find/aggregate/distinct operation in a `snapshotTime` property of the `ClientSession` object. Drivers MUST + save `atClusterTime` in the `ClientSession` object. +3. Passing that `snapshotTime` in the `atClusterTime` field of the `readConcern` field for subsequent snapshot read + operations (i.e. find/aggregate/distinct commands). + +## Server Command Responses + +For find/aggregate commands the server returns `atClusterTime` within the `cursor` field of the response. + +```typescript +{ + ok : 1 or 0, + ... // the rest of the command reply + cursor : { + ... // the rest of the cursor reply + atClusterTime : + } +} +``` + +For distinct commands the server returns `atClusterTime` as a top-level field in the response. + +```typescript +{ + ok : 1 or 0, + ... // the rest of the command reply + atClusterTime : +} +``` + +The `atClusterTime` timestamp MUST be stored in the `ClientSession` to later be passed as the `atClusterTime` field of +the `readConcern` with a `snapshot` level in subsequent read operations. + +## Server Errors + +1. The server may reply to read commands with a `SnapshotTooOld(239)` error if the client's `atClusterTime` value is not + available in the server's history. +2. The server will return `InvalidOptions(72)` error if both `atClusterTime` and `afterClusterTime` options are set to + true. +3. The server will return `InvalidOptions(72)` error if the command does not support readConcern.level "snapshot". + +## Snapshot Read Commands + +For snapshot reads the driver MUST first obtain `atClusterTime` from the server response of a find/aggregate/distinct +command, by specifying `readConcern` with `snapshot` level field, and store it as `snapshotTime` in the `ClientSession` +object. + +```typescript +{ + find : , // or other read command + ... // the rest of the command parameters + readConcern : + { + level : "snapshot" + } +} +``` + +For subsequent reads in the same session, the driver MUST send the `snapshotTime` saved in the `ClientSession` as the +value of the `atClusterTime` field of the `readConcern` with a `snapshot` level: + +```typescript +{ + find : , // or other read command + ... // the rest of the command parameters + readConcern : + { + level : "snapshot", + atClusterTime : + } +} +``` + +Lists of commands that support snapshot reads: + +1. find +2. aggregate +3. distinct + +## Sending readConcern to the server on all commands + +Drivers MUST set the readConcern `level` and `atClusterTime` fields (as outlined above) on all commands in a snapshot +session, including commands that do not accept a readConcern (e.g. insert, update). This ensures that the server will +return an error for invalid operations, such as writes, within a session configured for snapshot reads. + +## Requires MongoDB 5.0+ + +Snapshot reads require MongoDB 5.0+. When the connected server's maxWireVersion is less than 13, drivers MUST throw an +exception with the message "Snapshot reads require MongoDB 5.0 or later". + +## Motivation + +To support snapshot reads. Only supported with server version 5.0+ or newer. + +## Design Rationale + +The goal is to modify the driver API as little as possible so that existing programs that don't need snapshot reads +don't have to be changed. This goal is met by defining a `SessionOptions` field that applications use to start a +`ClientSession` that can be used for snapshot reads. Alternative explicit approach of obtaining `atClusterTime` from +`cursor` object and passing it to read concern object was considered initially. A session-based approach was chosen as +it aligns better with the existing API, and requires minimal API changes. Future extensibility for snapshot reads would +be best served by a session-based approach, as no API changes will be required. + +### `snapshotTimeout` must be read-only + +Allowing callers to mutate `snapshotTime` enables misuse patterns where applications overwrite `snapshotTime` on an +existing session and then use that session in transactions, combining an inconsistent timestamp with an existing server +session and triggering hard-to-diagnose write conflicts. + +## Backwards Compatibility + +The API changes to support snapshot reads extend the existing API but do not introduce any backward breaking changes. +Existing programs that don't use snapshot reads continue to compile and run correctly. + +## Reference Implementation + +C# driver will provide the reference implementation. The corresponding ticket is +[CSHARP-3668](https://jira.mongodb.org/browse/CSHARP-3668). + +## Q&A + +## Changelog + +- 2025-12-18: Make snapshot getter optional. +- 2025-09-23: Exposed snapshotTime to applications. +- 2024-05-08: Migrated from reStructuredText to Markdown. +- 2021-06-15: Initial version. +- 2021-06-28: Raise client side error on < 5.0. +- 2021-06-29: Send readConcern with all snapshot session commands. +- 2021-07-16: Grammar revisions. Change SHOULD to MUST for startTransaction error to comply with existing tests. +- 2021-08-09: Updated client-side error spec tests to use correct syntax for `test.expectEvents` +- 2022-10-05: Remove spec front matter diff --git a/source/sessions/snapshot-sessions.rst b/source/sessions/snapshot-sessions.rst new file mode 100644 index 0000000000..244a49ce25 --- /dev/null +++ b/source/sessions/snapshot-sessions.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `snapshot-sessions.md `_. diff --git a/source/sessions/tests/README.md b/source/sessions/tests/README.md new file mode 100644 index 0000000000..b5004b4be5 --- /dev/null +++ b/source/sessions/tests/README.md @@ -0,0 +1,302 @@ +# Driver Session Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +sessions. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +### Snapshot session tests + +The default snapshot history window on the server is 5 minutes. Running the test in debug mode, or in any other slow +configuration may lead to `SnapshotTooOld` errors. Drivers can work around this issue by increasing the server's +`minSnapshotHistoryWindowInSeconds` parameter, for example: + +```python +client.admin.command('setParameter', 1, minSnapshotHistoryWindowInSeconds=600) +``` + +### Testing against servers that do not support sessions + +Since all regular 3.6+ servers support sessions, the prose tests which test for session non-support SHOULD use a +mongocryptd server as the test server (available with server versions 4.2+); however, if future versions of mongocryptd +support sessions or if mongocryptd is not a viable option for the driver implementing these tests, another server MAY be +substituted as long as it does not return a non-null value for `logicalSessionTimeoutMinutes`; in the event that no such +server is readily available, a mock server may be used as a last resort. + +As part of the test setup for these cases, create a `MongoClient` pointed at the test server with the options specified +in the test case and verify that the test server does NOT define a value for `logicalSessionTimeoutMinutes` by sending a +hello command and checking the response. + +## Specific operations and behaviour for unified tests + +An operation on sessions called `getSnapshotTime` must be supported for unified tests. This operation returns the value +of `snapshotTime` on the session, so that it can be used in subsequent operations. + +When parsing `snapshotTime` from `sessionOptions` for unified tests, the parsed string is the name of the key for the +actual value of `snapshotTime` to be found in the +[entity map](https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md#entity-map). + +## Prose tests + +### 1. Setting both `snapshot` and `causalConsistency` to true is not allowed + +Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. + +- `client.startSession(snapshot = true, causalConsistency = true)` +- Assert that an error was raised by driver + +### 2. Pool is LIFO + +This test applies to drivers with session pools. + +- Call `MongoClient.startSession` twice to create two sessions, let us call them `A` and `B`. +- Call `A.endSession`, then `B.endSession`. +- Call `MongoClient.startSession`: the resulting session must have the same session ID as `B`. +- Call `MongoClient.startSession` again: the resulting session must have the same session ID as `A`. + +### 3. `$clusterTime` in commands + +- Register a command-started and a command-succeeded APM listener. If the driver has no APM support, inspect + commands/replies in another idiomatic way, such as monkey-patching or a mock server. +- Send a `ping` command to the server with the generic `runCommand` method. +- Assert that the command passed to the command-started listener includes `$clusterTime` if and only if `maxWireVersion` + \>= 6. +- Record the `$clusterTime`, if any, in the reply passed to the command-succeeded APM listener. +- Send another `ping` command. +- Assert that `$clusterTime` in the command passed to the command-started listener, if any, equals the `$clusterTime` in + the previous server reply. + +Repeat the above for: + +- An aggregate command from the `aggregate` helper method +- A find command from the `find` helper method +- An insert command from the `insert_one` helper method + +### 4. Explicit and implicit session arguments + +- Register a command-started APM listener. If the driver has no APM support, inspect commands in another idiomatic way, + such as monkey-patching or a mock server. +- Create `client1` +- Get `database` from `client1` +- Get `collection` from `database` +- Start `session` from `client1` +- Call `collection.insertOne(session,...)` +- Assert that the command passed to the command-started listener contained the session `lsid` from `session`. +- Call `collection.insertOne(,...)` (*without* a session argument) +- Assert that the command passed to the command-started listener contained a session `lsid`. + +Repeat the above for all methods that take a session parameter. + +### 5. Session argument is for the right client + +- Create `client1` and `client2` +- Get `database` from `client1` +- Get `collection` from `database` +- Start `session` from `client2` +- Call `collection.insertOne(session,...)` +- Assert that an error was reported because `session` was not started from `client1` + +Repeat the above for all methods that take a session parameter. + +### 6. No further operations can be performed using a session after `endSession` has been called + +- Start a `session` +- End the `session` +- Call `collection.InsertOne(session, ...)` +- Assert that the proper error was reported + +Repeat the above for all methods that take a session parameter. + +If your driver implements a platform dependent idiomatic disposal pattern, test that also (if the idiomatic disposal +pattern calls `endSession` it would be sufficient to only test the disposal pattern since that ends up calling +`endSession`). + +### 7. Authenticating as multiple users suppresses implicit sessions + +Skip this test if your driver does not allow simultaneous authentication with multiple users. + +- Authenticate as two users +- Call `findOne` with no explicit session +- Capture the command sent to the server +- Assert that the command sent to the server does not have an `lsid` field + +### 8. Client-side cursor that exhausts the results on the initial query immediately returns the implicit session to the pool + +- Insert two documents into a collection +- Execute a find operation on the collection and iterate past the first document +- Assert that the implicit session is returned to the pool. This can be done in several ways: + - Track in-use count in the server session pool and assert that the count has dropped to zero + - Track the lsid used for the find operation (e.g. with APM) and then do another operation and assert that the same + lsid is used as for the find operation. + +### 9. Client-side cursor that exhausts the results after a `getMore` immediately returns the implicit session to the pool + +- Insert five documents into a collection +- Execute a find operation on the collection with batch size of 3 +- Iterate past the first four documents, forcing the final `getMore` operation +- Assert that the implicit session is returned to the pool prior to iterating past the last document + +### 10. No remaining sessions are checked out after each functional test + +At the end of every individual functional test of the driver, there SHOULD be an assertion that there are no remaining +sessions checked out from the pool. This may require changes to existing tests to ensure that they close any explicit +client sessions and any unexhausted cursors. + +### 11. For every combination of topology and readPreference, ensure that `find` and `getMore` both send the same session id + +- Insert three documents into a collection +- Execute a `find` operation on the collection with a batch size of 2 +- Assert that the server receives a non-zero lsid +- Iterate through enough documents (3) to force a `getMore` +- Assert that the server receives a non-zero lsid equal to the lsid that `find` sent. + +### 12. Session pool can be cleared after forking without calling `endSession` + +Skip this test if your driver does not allow forking. + +- Create ClientSession +- Record its lsid +- Delete it (so the lsid is pushed into the pool) +- Fork +- In the parent, create a ClientSession and assert its lsid is the same. +- In the child, create a ClientSession and assert its lsid is different. + +### 13. Existing sessions are not checked into a cleared pool after forking + +Skip this test if your driver does not allow forking. + +- Create ClientSession +- Record its lsid +- Fork +- In the parent, return the ClientSession to the pool, create a new ClientSession, and assert its lsid is the same. +- In the child, return the ClientSession to the pool, create a new ClientSession, and assert its lsid is different. + +### 14. Implicit sessions only allocate their server session after a successful connection checkout + +- Create a MongoClient with the following options: `maxPoolSize=1` and `retryWrites=true`. If testing against a sharded + deployment, the test runner MUST ensure that the MongoClient connects to only a single mongos host. +- Attach a command started listener that collects each command's lsid +- Initiate the following concurrent operations + - `insertOne({ }),` + - `deleteOne({ }),` + - `updateOne({ }, { $set: { a: 1 } }),` + - `bulkWrite([{ updateOne: { filter: { }, update: { $set: { a: 1 } } } }]),` + - `findOneAndDelete({ }),` + - `findOneAndUpdate({ }, { $set: { a: 1 } }),` + - `findOneAndReplace({ }, { a: 1 }),` + - `find().toArray()` +- Wait for all operations to complete successfully +- Assert the following across at least 5 retries of the above test: + - Drivers MUST assert that exactly one session is used for all operations at least once across the retries of this + test. + - Note that it's possible, although rare, for >1 server session to be used because the session is not released until + after the connection is checked in. + - Drivers MUST assert that the number of allocated sessions is strictly less than the number of concurrent operations + in every retry of this test. In this instance it would be less than (but NOT equal to) 8. + +### 15. `lsid` is added inside `$query` when using OP_QUERY + +This test only applies to drivers that have not implemented OP_MSG and still use OP_QUERY. + +- For a command to a mongos that includes a readPreference, verify that the `lsid` on query commands is added inside the + `$query` field, and NOT as a top-level field. + +### 16. Authenticating as a second user after starting a session results in a server error + +This test only applies to drivers that allow authentication to be changed on the fly. + +- Authenticate as the first user +- Start a session by calling `startSession` +- Authenticate as a second user +- Call `findOne` using the session as an explicit session +- Assert that the driver returned an error because multiple users are authenticated + +### 17. Driver verifies that the session is owned by the current user + +This test only applies to drivers that allow authentication to be changed on the fly. + +- Authenticate as user A +- Start a session by calling `startSession` +- Logout user A +- Authenticate as user B +- Call `findOne` using the session as an explicit session +- Assert that the driver returned an error because the session is owned by a different user + +### 18. Implicit session is ignored if connection does not support sessions + +Refer to [Testing against servers that do not support sessions](#testing-against-servers-that-do-not-support-sessions) +and configure a `MongoClient` with command monitoring enabled. + +- Send a read command to the server (e.g., `findOne`), ignoring any errors from the server response +- Check the corresponding `commandStarted` event: verify that `lsid` is not set +- Send a write command to the server (e.g., `insertOne`), ignoring any errors from the server response +- Check the corresponding `commandStarted` event: verify that lsid is not set + +### 19. Explicit session raises an error if connection does not support sessions + +Refer to [Testing against servers that do not support sessions](#testing-against-servers-that-do-not-support-sessions) +and configure a `MongoClient` with default options. + +- Create a new explicit session by calling `startSession` (this MUST NOT error) +- Attempt to send a read command to the server (e.g., `findOne`) with the explicit session passed in +- Assert that a client-side error is generated indicating that sessions are not supported +- Attempt to send a write command to the server (e.g., `insertOne`) with the explicit session passed in +- Assert that a client-side error is generated indicating that sessions are not supported + +### 20. Drivers do not gossip `$clusterTime` on SDAM commands. + +- Skip this test when connected to a deployment that does not support cluster times +- Create a client, C1, directly connected to a writable server and a small heartbeatFrequencyMS + - `c1 = MongoClient(directConnection=True, heartbeatFrequencyMS=10)` +- Run a ping command using C1 and record the `$clusterTime` in the response, as `clusterTime`. + - `clusterTime = c1.admin.command({"ping": 1})["$clusterTime"]` +- Using a separate client, C2, run an insert to advance the cluster time + - `c2.test.test.insert_one({"advance": "$clusterTime"})` +- Next, wait until the client C1 processes the next pair of SDAM heartbeat started + succeeded events. + - If possible, assert the SDAM heartbeats do not send `$clusterTime` +- Run a ping command using C1 and assert that `$clusterTime` sent is the same as the `clusterTime` recorded earlier. + This assertion proves that C1's `$clusterTime` was not advanced by gossiping through SDAM. + +### 21. Having `snapshotTime` set and `snapshot` set to false is not allowed + +Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. + +- Start a session by calling `startSession` with `snapshot = false` and `snapshotTime = new Timestamp(1)`. +- Assert that a client side error was raised. + +### 22. Retrieving `snapshotTime` on a non-snapshot session raises an error + +Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. + +- Start a session by calling `startSession` on with `snapshot = false`. +- Try to access the session's snapshot time. +- Assert that a client side error was raised. +- Assert that the session's `snapshotTime` is unset. + +Drivers MAY skip this test if they choose not to implement a getter for `snapshotTime`. + +### 23. Ensure `snapshotTime` is Read-Only + +Snapshot sessions tests require server of version 5.0 or higher and replica set or a sharded cluster deployment. + +- Start a session by calling `startSession` on with `snapshot = false`. +- Assert that the session's `snapshotTime` is unset. +- Attempt to mutate the session's `snapshotTime` field through any publicly accessible API. +- Assert that the original session's `snapshotTime` remains unchanged. + +Drivers MAY skip this test if they choose to expose a read-only `snapshotTime` property using an accessor method only. + +## Changelog + +- 2025-12-18: Make snapshot getter prose test optional. +- 2025-09-25: Added tests for snapshotTime. +- 2025-02-24: Test drivers do not gossip $clusterTime on SDAM. +- 2024-05-08: Migrated from reStructuredText to Markdown. +- 2019-05-15: Initial version. +- 2021-06-15: Added snapshot-session tests. Introduced legacy and unified folders. +- 2021-07-30: Use numbering for prose test +- 2022-02-11: Convert legacy tests to unified format +- 2022-06-13: Relocate prose test from spec document and apply new ordering +- 2023-02-24: Fix formatting and add new prose tests 18 and 19 diff --git a/source/sessions/tests/driver-sessions-dirty-session-errors.json b/source/sessions/tests/driver-sessions-dirty-session-errors.json new file mode 100644 index 0000000000..d7a1c6aba7 --- /dev/null +++ b/source/sessions/tests/driver-sessions-dirty-session-errors.json @@ -0,0 +1,976 @@ +{ + "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": { + "$$unsetOrMatches": 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": { + "$$unsetOrMatches": 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": { + "$$unsetOrMatches": 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": { + "$$unsetOrMatches": 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/source/sessions/tests/driver-sessions-dirty-session-errors.yml b/source/sessions/tests/driver-sessions-dirty-session-errors.yml new file mode 100644 index 0000000000..0fb032bde2 --- /dev/null +++ b/source/sessions/tests/driver-sessions-dirty-session-errors.yml @@ -0,0 +1,351 @@ +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 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name session-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + - &find_with_implicit_session + name: find + object: *collection0 + arguments: + filter: { _id: -1 } + expectResult: [] + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &insert_attempt + command: + insert: *collection0Name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + commandName: insert + databaseName: *database0Name + - commandStartedEvent: *insert_attempt + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 2 + commandName: insert + databaseName: *database0Name + - commandStartedEvent: &find_with_implicit_session_event + command: + find: *collection0Name + filter: { _id: -1 } + # There is no explicit session to use with $$sessionLsid, so + # just assert an arbitrary lsid document + lsid: { $$type: object } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &findAndModify_attempt + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database0Name + - commandStartedEvent: *findAndModify_attempt + - commandStartedEvent: *find_with_implicit_session_event + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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 } } } + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &insert_attempt + command: + insert: *collection0Name + documents: + - { _id: 2 } + ordered: true + lsid: { $$type: object } + txnNumber: 1 + commandName: insert + databaseName: *database0Name + - commandStartedEvent: *insert_attempt + - commandStartedEvent: *find_with_implicit_session_event + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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 } + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &findAndModify_attempt + command: + findAndModify: *collection0Name + query: { _id: 1 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$type: object } + txnNumber: 1 + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database0Name + - commandStartedEvent: *findAndModify_attempt + - commandStartedEvent: *find_with_implicit_session_event + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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 } ] + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &aggregate_attempt + command: + aggregate: *collection0Name + pipeline: [ { $project: { _id: 1 } } ] + lsid: { $$type: object } + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: *aggregate_attempt + - commandStartedEvent: *find_with_implicit_session_event + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &countDocuments_attempt + command: + aggregate: *collection0Name + pipeline: [ { $match: {} }, { $group: { _id: 1, n: { $sum: 1 } } } ] + lsid: { $$type: object } + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: *countDocuments_attempt + - commandStartedEvent: *find_with_implicit_session_event + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } diff --git a/source/sessions/tests/driver-sessions-server-support.json b/source/sessions/tests/driver-sessions-server-support.json new file mode 100644 index 0000000000..55312b32eb --- /dev/null +++ b/source/sessions/tests/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/source/sessions/tests/driver-sessions-server-support.yml b/source/sessions/tests/driver-sessions-server-support.yml new file mode 100644 index 0000000000..437b4863f1 --- /dev/null +++ b/source/sessions/tests/driver-sessions-server-support.yml @@ -0,0 +1,123 @@ +description: "driver-sessions-server-support" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "3.6" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name session-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + - &find_with_implicit_session + name: find + object: *collection0 + arguments: + filter: { _id: -1 } + expectResult: [] + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: [ { _id: 2 } ] + ordered: true + lsid: { $$sessionLsid: *session0 } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: -1 } + lsid: { $$sessionLsid: *session0 } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + + - description: "Server supports implicit sessions" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 2 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } } + - *find_with_implicit_session + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - { _id: 2 } + ordered: true + # There is no explicit session to use with $$sessionLsid, so + # just assert an arbitrary lsid document + lsid: { $$type: object } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: -1 } + lsid: { $$type: object } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/sessions/tests/implicit-sessions-default-causal-consistency.json b/source/sessions/tests/implicit-sessions-default-causal-consistency.json new file mode 100644 index 0000000000..517c8ebc63 --- /dev/null +++ b/source/sessions/tests/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/source/sessions/tests/implicit-sessions-default-causal-consistency.yml b/source/sessions/tests/implicit-sessions-default-causal-consistency.yml new file mode 100644 index 0000000000..052c6dac20 --- /dev/null +++ b/source/sessions/tests/implicit-sessions-default-causal-consistency.yml @@ -0,0 +1,119 @@ +description: "implicit sessions default causal consistency" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.2" + topologies: [replicaset, sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName implicit-cc-tests + - collection: + id: &collectionDefault collectionDefault + database: *database0 + collectionName: &collectionNameDefault coll-default + - collection: + id: &collectionSnapshot collectionSnapshot + database: *database0 + collectionName: &collectionNameSnapshot coll-snapshot + collectionOptions: + readConcern: { level: snapshot } + - collection: + id: &collectionlinearizable collectionlinearizable + database: *database0 + collectionName: &collectionNamelinearizable coll-linearizable + collectionOptions: + readConcern: { level: linearizable } + +initialData: + - collectionName: *collectionNameDefault + databaseName: *databaseName + documents: + - { _id: 1, x: default } + - collectionName: *collectionNameSnapshot + databaseName: *databaseName + documents: + - { _id: 1, x: snapshot } + - collectionName: *collectionNamelinearizable + databaseName: *databaseName + documents: + - { _id: 1, x: linearizable } + +tests: + - description: "readConcern is not sent on retried read in implicit session when readConcern level is not specified" + operations: + - &failPointCommand + name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [find] + errorCode: 11600 #InterruptedAtShutdown + - name: find + object: *collectionDefault + arguments: + filter: {} + expectResult: [{ _id: 1, x: default }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventDefault + command: + find: *collectionNameDefault + filter: {} + readConcern: { $$exists: false } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventDefault + + - description: "afterClusterTime is not sent on retried read in implicit session when readConcern level is snapshot" + runOnRequirements: + - minServerVersion: "5.0" + operations: + - *failPointCommand + - name: find + object: *collectionSnapshot + arguments: + filter: {} + expectResult: [{ _id: 1, x: snapshot }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventSnapshot + command: + find: *collectionNameSnapshot + filter: {} + readConcern: + { level: snapshot, afterClusterTime: { $$exists: false } } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventSnapshot + + - description: "afterClusterTime is not sent on retried read in implicit session when readConcern level is linearizable" + operations: + - *failPointCommand + - name: find + object: *collectionlinearizable + arguments: + filter: {} + expectResult: [{ _id: 1, x: linearizable }] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &commandStartedEventLinearizable + command: + find: *collectionNamelinearizable + filter: {} + readConcern: + { level: linearizable, afterClusterTime: { $$exists: false } } + databaseName: *databaseName + - commandStartedEvent: *commandStartedEventLinearizable diff --git a/source/sessions/tests/snapshot-sessions-not-supported-client-error.json b/source/sessions/tests/snapshot-sessions-not-supported-client-error.json new file mode 100644 index 0000000000..208e4cfe63 --- /dev/null +++ b/source/sessions/tests/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/source/sessions/tests/snapshot-sessions-not-supported-client-error.yml b/source/sessions/tests/snapshot-sessions-not-supported-client-error.yml new file mode 100644 index 0000000000..e725c848d3 --- /dev/null +++ b/source/sessions/tests/snapshot-sessions-not-supported-client-error.yml @@ -0,0 +1,75 @@ +description: snapshot-sessions-not-supported-client-error + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "3.6" + maxServerVersion: "4.4.99" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandFailedEvent ] + - database: + id: &database0Name database0 + client: *client0 + databaseName: *database0Name + - collection: + id: &collection0Name collection0 + database: *database0Name + collectionName: *collection0Name + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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/source/sessions/tests/snapshot-sessions-not-supported-server-error.json b/source/sessions/tests/snapshot-sessions-not-supported-server-error.json new file mode 100644 index 0000000000..79213f314f --- /dev/null +++ b/source/sessions/tests/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/source/sessions/tests/snapshot-sessions-not-supported-server-error.yml b/source/sessions/tests/snapshot-sessions-not-supported-server-error.yml new file mode 100644 index 0000000000..4953dbcbe5 --- /dev/null +++ b/source/sessions/tests/snapshot-sessions-not-supported-server-error.yml @@ -0,0 +1,102 @@ +description: snapshot-sessions-not-supported-server-error + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + topologies: [ single ] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandFailedEvent ] + - database: + id: &database0Name database0 + client: *client0 + databaseName: *database0Name + - collection: + id: &collection0Name collection0 + database: *database0Name + collectionName: *collection0Name + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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/source/sessions/tests/snapshot-sessions-unsupported-ops.json b/source/sessions/tests/snapshot-sessions-unsupported-ops.json new file mode 100644 index 0000000000..c41f74d337 --- /dev/null +++ b/source/sessions/tests/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/source/sessions/tests/snapshot-sessions-unsupported-ops.yml b/source/sessions/tests/snapshot-sessions-unsupported-ops.yml new file mode 100644 index 0000000000..91d785e47e --- /dev/null +++ b/source/sessions/tests/snapshot-sessions-unsupported-ops.yml @@ -0,0 +1,258 @@ +description: snapshot-sessions-unsupported-ops + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + topologies: [replicaset, sharded] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent, commandFailedEvent ] + - database: + id: &database0Name database0 + client: *client0 + databaseName: *database0Name + - collection: + id: &collection0Name collection0 + database: *database0Name + collectionName: *collection0Name + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: +- description: Server returns an error on insertOne with snapshot + # Skip on sharded clusters due to SERVER-58176. + 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 + # Skip on sharded clusters due to SERVER-58176. + 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 + # Skip on sharded clusters due to SERVER-58176. + 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 + # Skip on sharded clusters due to SERVER-58176. + 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/source/sessions/tests/snapshot-sessions.json b/source/sessions/tests/snapshot-sessions.json new file mode 100644 index 0000000000..8f806ea759 --- /dev/null +++ b/source/sessions/tests/snapshot-sessions.json @@ -0,0 +1,1797 @@ +{ + "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" + } + } + ] + }, + { + "description": "Find operation with snapshot and snapshot time", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + } + ] + }, + { + "name": "getSnapshotTime", + "object": "session0", + "saveResultAsEntity": "savedSnapshotTime" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "snapshot": true, + "snapshotTime": "savedSnapshotTime" + } + } + } + ] + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + } + ] + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 11 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "collection0", + "readConcern": { + "$$exists": false + } + }, + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Distinct operation with snapshot and snapshot time", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {}, + "fieldName": "x" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "getSnapshotTime", + "object": "session0", + "saveResultAsEntity": "savedSnapshotTime" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "snapshot": true, + "snapshotTime": "savedSnapshotTime" + } + } + } + ] + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {}, + "fieldName": "x" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {}, + "fieldName": "x" + }, + "expectResult": [ + 11 + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "filter": {}, + "fieldName": "x" + }, + "expectResult": [ + 11, + 33 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "$$exists": false + } + }, + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Aggregate operation with snapshot and snapshot time", + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "getSnapshotTime", + "object": "session0", + "saveResultAsEntity": "savedSnapshotTime" + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "snapshot": true, + "snapshotTime": "savedSnapshotTime" + } + } + } + ] + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session2", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session2", + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "$$exists": false + } + }, + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "countDocuments operation with snapshot and snapshot time", + "operations": [ + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectResult": 2 + }, + { + "name": "getSnapshotTime", + "object": "session0", + "saveResultAsEntity": "savedSnapshotTime" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "snapshot": true, + "snapshotTime": "savedSnapshotTime" + } + } + } + ] + } + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {} + }, + "expectResult": 2 + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session2", + "filter": {} + }, + "expectResult": 2 + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectResult": 3 + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$exists": false + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + }, + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "readConcern": { + "$$exists": false + } + }, + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Mixed operation with snapshot and snapshotTime", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "getSnapshotTime", + "object": "session0", + "saveResultAsEntity": "savedSnapshotTime" + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "After" + }, + "expectResult": { + "_id": 1, + "x": 12 + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "snapshot": true, + "snapshotTime": "savedSnapshotTime" + } + } + } + ] + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1, + "x": 12 + } + ] + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": 1 + } + } + ], + "session": "session2" + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": {}, + "session": "session2" + }, + "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": { + "$$matchesEntity": "savedSnapshotTime" + } + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "collection0", + "readConcern": { + "level": "snapshot", + "atClusterTime": { + "$$matchesEntity": "savedSnapshotTime" + } + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/sessions/tests/snapshot-sessions.yml b/source/sessions/tests/snapshot-sessions.yml new file mode 100644 index 0000000000..90d4481991 --- /dev/null +++ b/source/sessions/tests/snapshot-sessions.yml @@ -0,0 +1,885 @@ +description: snapshot-sessions + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "5.0" + topologies: [replicaset, sharded] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent] + ignoreCommandMonitoringEvents: [ findAndModify, insert, update ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name collection0 + collectionOptions: + writeConcern: { w: majority } + - session: + id: session0 + client: client0 + sessionOptions: + snapshot: true + - session: + id: session1 + client: client0 + sessionOptions: + snapshot: true + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + +- description: Find operation with snapshot and snapshot time + operations: + - name: find + object: collection0 + arguments: + session: session0 + filter: {} + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 11 } + - name: getSnapshotTime + object: session0 + saveResultAsEntity: &savedSnapshotTime savedSnapshotTime + - name: insertOne + object: collection0 + arguments: + document: { _id: 3, x: 33 } + - name: createEntities + object: testRunner + arguments: + entities: + - session: + id: session2 + client: client0 + sessionOptions: + snapshot: true + snapshotTime: *savedSnapshotTime + - name: find + object: collection0 + arguments: + session: session2 + filter: {} + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 11 } + ## Calling the operation again to verify that atClusterTime/snapshotTime has not been modified after the first query + - name: find + object: collection0 + arguments: + session: session2 + filter: {} + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 11 } + - name: find + object: collection0 + arguments: + filter: {} + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 11 } + - { _id: 3, x: 33 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + databaseName: database0 + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + find: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + find: collection0 + readConcern: + "$$exists": false + databaseName: database0 + +- description: Distinct operation with snapshot and snapshot time + operations: + - name: distinct + object: collection0 + arguments: + session: session0 + filter: {} + fieldName: x + expectResult: [11] + - name: getSnapshotTime + object: session0 + saveResultAsEntity: &savedSnapshotTime savedSnapshotTime + - name: insertOne + object: collection0 + arguments: + document: { _id: 3, x: 33 } + - name: createEntities + object: testRunner + arguments: + entities: + - session: + id: session2 + client: client0 + sessionOptions: + snapshot: true + snapshotTime: *savedSnapshotTime + - name: distinct + object: collection0 + arguments: + session: session2 + filter: {} + fieldName: x + expectResult: [11] + ## Calling the operation again to verify that atClusterTime/snapshotTime has not been modified after the first query + - name: distinct + object: collection0 + arguments: + session: session2 + filter: {} + fieldName: x + expectResult: [11] + - name: distinct + object: collection0 + arguments: + filter: {} + fieldName: x + expectResult: [11, 33] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + databaseName: database0 + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + "$$exists": false + databaseName: database0 + +- description: Aggregate operation with snapshot and snapshot time + operations: + - name: aggregate + object: collection0 + arguments: + session: session0 + pipeline: + - "$match": { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + - name: getSnapshotTime + object: session0 + saveResultAsEntity: &savedSnapshotTime savedSnapshotTime + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 12 } + - name: createEntities + object: testRunner + arguments: + entities: + - session: + id: session2 + client: client0 + sessionOptions: + snapshot: true + snapshotTime: *savedSnapshotTime + - name: aggregate + object: collection0 + arguments: + session: session2 + pipeline: + - "$match": { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + ## Calling the operation again to verify that atClusterTime/snapshotTime has not been modified after the first query + - name: aggregate + object: collection0 + arguments: + session: session2 + pipeline: + - "$match": { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": { _id: 1 } + expectResult: + - { _id: 1, x: 12 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + "$$exists": false + databaseName: database0 + +- description: countDocuments operation with snapshot and snapshot time + operations: + - name: countDocuments + object: collection0 + arguments: + session: session0 + filter: {} + expectResult: 2 + - name: getSnapshotTime + object: session0 + saveResultAsEntity: &savedSnapshotTime savedSnapshotTime + - name: insertOne + object: collection0 + arguments: + document: { _id: 3, x: 33 } + - name: createEntities + object: testRunner + arguments: + entities: + - session: + id: session2 + client: client0 + sessionOptions: + snapshot: true + snapshotTime: *savedSnapshotTime + - name: countDocuments + object: collection0 + arguments: + session: session2 + filter: {} + expectResult: 2 + ## Calling the operation again to verify that atClusterTime/snapshotTime has not been modified after the first query + - name: countDocuments + object: collection0 + arguments: + session: session2 + filter: {} + expectResult: 2 + - name: countDocuments + object: collection0 + arguments: + filter: {} + expectResult: 3 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: + "$$exists": false + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } + databaseName: database0 + - commandStartedEvent: + command: + aggregate: collection0 + readConcern: + "$$exists": false + databaseName: database0 + +- description: Mixed operation with snapshot and snapshotTime + operations: + - name: find + object: collection0 + arguments: + session: session0 + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 11 } + - name: getSnapshotTime + object: session0 + saveResultAsEntity: &savedSnapshotTime savedSnapshotTime + - name: findOneAndUpdate + object: collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: After + expectResult: { _id: 1, x: 12 } + - name: createEntities + object: testRunner + arguments: + entities: + - session: + id: session2 + client: client0 + sessionOptions: + snapshot: true + snapshotTime: *savedSnapshotTime + - name: find + object: collection0 + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1, x: 12 } + - name: aggregate + object: collection0 + arguments: + pipeline: + - "$match": + _id: 1 + session: session2 + expectResult: + - { _id: 1, x: 11 } + - name: distinct + object: collection0 + arguments: + fieldName: x + filter: {} + session: session2 + 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: { $$matchesEntity: *savedSnapshotTime } + - commandStartedEvent: + command: + distinct: collection0 + readConcern: + level: snapshot + atClusterTime: { $$matchesEntity: *savedSnapshotTime } \ No newline at end of file diff --git a/source/socks5-support/socks5.md b/source/socks5-support/socks5.md new file mode 100644 index 0000000000..468600be84 --- /dev/null +++ b/source/socks5-support/socks5.md @@ -0,0 +1,133 @@ +# SOCKS5 Support + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +SOCKS5 is a standardized protocol for connecting to network services through a separate proxy server. It can be used for +connecting to hosts that would otherwise be unreachable from the local network by connecting to a proxy server, which +receives the intended target host's address from the client and then connects to that address. + +This specification defines driver behaviour when connecting to MongoDB services through a SOCKS5 proxy. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + +#### SOCKS5 + +The SOCKS protocol, version 5, as defined in [RFC1928](https://datatracker.ietf.org/doc/html/rfc1928), restricted to +either no authentication or username/password authentication as defined in +[RFC1929](https://datatracker.ietf.org/doc/html/rfc1929). + +### MongoClient Configuration + +#### proxyHost + +To specify to the driver to connect using a SOCKS5 proxy, a connection string option of `proxyHost=host` MUST be added +to the connection string or passed through an equivalent `MongoClient` option. This option specifies a domain name or +IPv4 or IPv6 address on which a SOCKS5 proxy is listening. This option MUST only be configurable at the level of a +`MongoClient`. + +#### proxyPort + +To specify to the driver to connect using a SOCKS5 proxy listening on a non-default port, a connection string option of +`proxyPort=port` MUST be added to the connection string or passed through an equivalent `MongoClient` option. This +option specifies a TCP port number. The default for this option MUST be `1080`. This option MUST only be configurable at +the level of a `MongoClient`. Drivers MUST error if this option was specified and `proxyHost` was not specified. + +#### proxyUsername + +To specify to the driver to connect using a SOCKS5 proxy requiring username/password authentication, a connection string +option of - Code:`proxyUsername=username` MUST be added to the connection string or passed through an equivalent +`MongoClient` option. This option specifies a string of non-zero length. Drivers MUST ignore this option if it specifies +a zero-length string. Drivers MUST error if this option was specified and `proxyHost` was not specified or +`proxyPassword` was not specified. + +#### proxyPassword + +To specify to the driver to connect using a SOCKS5 proxy requiring username/password authentication, a connection string +option of - Code:`proxyPassword=password` MUST be added to the connection string or passed through an equivalent +`MongoClient` option. This option specifies a string of non-zero length. Drivers MUST ignore this option if it specifies +a zero-length string. Drivers MUST error if this option was specified and `proxyHost` was not specified or +`proxyUsername` was not specified. + +### Connection Pooling + +#### Connection Establishment + +When establishing a new outgoing TCP connection, drivers MUST perform the following steps if `proxyHost` was specified: + +1. Connect to the SOCKS5 proxy host, using `proxyHost` and `proxyPort` as specified. + +2. Perform a SOCKS5 handshake as specified in RFC1928. + + > If `proxyUsername` and `proxyPassword` were passed, drivers MUST indicate in the handshake that both "no + > authentication" and "username/password authentication" are supported. Otherwise, drivers MUST indicate support for + > "no authentication" only. + > + > Drivers MUST NOT attempt to perform DNS A or AAAA record resolution of the destination hostname and instead pass + > the hostname to the proxy as-is. + +3. Continue with connection establishment as if the connection was one to the destination host. + +Drivers MUST use the SOCKS5 proxy for connections to MongoDB services and +[client-side field-level encryption KMS servers](../client-side-encryption/client-side-encryption.md#kms-provider). + +Drivers MUST NOT use the SOCKS5 proxy for connections to - Code:`mongocryptd` processes spawned for automatic +client-side field-level encryption. + +Drivers MUST treat a connection failure when connecting to the SOCKS5 proxy or a SOCKS5 handshake or authentication +failure the same as a network error (e.g. `ECONNREFUSED`) which does not indicate server overload for backpressure +purposes. + +### Events + +SOCKS5 proxies are fully transparent to connection monitoring events. In particular, in `CommandStartedEvent`, +`CommandSucceededEvent`, and - Code:`CommandFailedEvent`, the driver SHOULD NOT reference the SOCKS5 proxy as part of +the `connectionId` field or other fields. + +### Q&A + +#### Why not include DNS requests in the set of proxied network communication? + +While SOCKS5 as a protocol does support UDP forwarding, using this feature has a number of downsides. Notably, only a +subset of SOCKS5 client libraries and SOCKS5 server implementations support UDP forwarding (e.g. the OpenSSH client's +dynamic forwarding feature does not). This would also considerably increase implementation complexity in drivers that do +not use DNS libraries in which the driver is in control of how the UDP packets are sent and received. + +#### Why not support other proxy protocols, such as Socks4/Socks4a, HTTP Connect proxies, etc.? + +SOCKS5 is a powerful, standardized and widely used proxy protocol. It is likely that almost all users which require +tunneling/proxying of some sort will be able to use it, and those who require another protocol or a more advanced setup +like proxy chaining, can work around that by using a local SOCKS5 intermediate proxy. + +#### Why are the connection string parameters generic, with no explicit mention of SOCKS5? + +In the case that future changes will enable drivers using other proxy protocols, keeping the option names generic allows +their re-use. In that case, another option would specify the protocol and SOCKS5 would be the implied default. However, +since there is no reason to believe that such additions will be made in the foreseeable future, no option for specifying +the proxy protocol is introduced here. + +#### Why is support for authentication methods limited to no authentication and username/password authentication? + +This matches the set of authentication methods most commonly implemented by SOCKS5 client libraries and thus reduces +implementation complexity for drivers. This advantage is sufficient to ignore the possible advantages that would come +with enabling other authentication methods. + +### Design Rationales + +### Alternative Designs + +## Changelog + +- 2024-09-04: Migrated from reStructuredText to Markdown. +- 2022-10-05: Remove spec front matter diff --git a/source/socks5-support/socks5.rst b/source/socks5-support/socks5.rst new file mode 100644 index 0000000000..269e2c9a8b --- /dev/null +++ b/source/socks5-support/socks5.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `socks5.md `_. diff --git a/source/socks5-support/tests/README.md b/source/socks5-support/tests/README.md new file mode 100644 index 0000000000..29aa331923 --- /dev/null +++ b/source/socks5-support/tests/README.md @@ -0,0 +1,54 @@ +# SOCKS5 Support Tests + +______________________________________________________________________ + +## Introduction + +This document describes how drivers should test support for SOCKS5 proxies. + +## Testing Requirements + +For a single server version (at least 5.0), drivers MUST add two Evergreen tasks: one with a replica set with TLS +enabled, and one with TLS disabled. The servers of the replica set may listen on any free ports. + +Each task MUST also start up two SOCKS5 proxies: one requiring authentication, and one that does not require +authentication. + +### SOCKS5 Proxy Configuration + +Drivers MUST use the `socks5srv.py` script in `drivers-evergreen-tools` to run the SOCKS5 proxy servers for Evergreen +tasks. This script MUST be run after the backing replica set has already been started, and MUST be started with +`--map "localhost:12345 to "` where `host` is the host:port identifier of an arbitrary member of the replica set. +The SOCKS5 proxy server requiring authentication MUST be started with `--port 1080 --auth username:p4ssw0rd`, the one +not requiring authentication with `--port 1081`. + +## Prose Tests + +Drivers MUST test the following connection strings: + +| Connection String | Expected Result | +| -------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `mongodb:///?proxyHost=localhost&proxyPort=1080&directConnection=true` | (fails) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081&directConnection=true` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1080` | (fails) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1080&proxyUsername=nonexistentuser&proxyPassword=badauth&directConnection=true` | (fails) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081&proxyUsername=nonexistentuser&proxyPassword=badauth&directConnection=true` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081&proxyUsername=nonexistentuser&proxyPassword=badauth` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1080&proxyUsername=username&proxyPassword=p4ssw0rd&directConnection=true` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081&directConnection=true` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1080&proxyUsername=username&proxyPassword=p4ssw0rd` | (succeeds) | +| `mongodb:///?proxyHost=localhost&proxyPort=1081` | (succeeds) | + +where `` stands for all hosts in the tests replica set and `mappedhost` stands for `localhost:12345`. For +the Evergreen task in which TLS is enabled, the required `tls` and - Code:`tlsCAFile` connection string options are +appended to all connection strings listed above. + +Drivers MUST create a `MongoClient` for each of these connection strings, and attempt to run a `hello` command using +each client. The operation must succeed for table entries marked (succeeds) and fail for table entries marked (fails). +The connection strings MUST all be accepted as valid connection strings. + +Drivers MUST run variants of these tests in which the proxy options are substituted for `MongoClient` options. + +Drivers MUST verify for at least one of the connection strings marked (succeeds) that command monitoring events do not +reference the SOCKS5 proxy host where the MongoDB service server/port are referenced. diff --git a/source/transactions-convenient-api/tests/README.md b/source/transactions-convenient-api/tests/README.md new file mode 100644 index 0000000000..62cf4ff77b --- /dev/null +++ b/source/transactions-convenient-api/tests/README.md @@ -0,0 +1,124 @@ +# Convenient API for Transactions Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +the Convenient API for Transactions spec. These tests utilize the +[Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +## Prose Tests + +### Callback Raises a Custom Error + +Write a callback that raises a custom exception or error that does not include either UnknownTransactionCommitResult or +TransientTransactionError error labels. Execute this callback using `withTransaction` and assert that the callback's +error bypasses any retry logic within `withTransaction` and is propagated to the caller of `withTransaction`. + +### Callback Returns a Value + +Write a callback that returns a custom value (e.g. boolean, string, object). Execute this callback using +`withTransaction` and assert that the callback's return value is propagated to the caller of `withTransaction`. + +### Retry Timeout is Enforced + +Drivers should test that `withTransaction` enforces a non-configurable timeout before retrying both commits and entire +transactions. Specifically, three cases should be checked: + +- If the callback raises an error with the `TransientTransactionError` label and the retry timeout has been exceeded, + `withTransaction` should propagate the error as described in the + [propagation mechanism](../transactions-convenient-api.md#timeout-error-propagation) to its caller. +- If committing raises an error with the `UnknownTransactionCommitResult` label, and the retry timeout has been + exceeded, `withTransaction` should propagate the error as described in the + [propagation mechanism](../transactions-convenient-api.md#timeout-error-propagation) to its caller +- If committing raises an error with the `TransientTransactionError` label and the retry timeout has been exceeded, + `withTransaction` should propagate the error as described in the + [propagation mechanism](../transactions-convenient-api.md#timeout-error-propagation) to its caller. This case may + occur if the commit was internally retried against a new primary after a failover and the second primary returned a + `NoSuchTransaction` error response. + +If possible, drivers should implement these tests without requiring the test runner to block for the full duration of +the retry timeout. This might be done by internally modifying the timeout value used by `withTransaction` with some +private API or using a mock timer. + +The drivers should assert that the timeout error propagated has the same labels as the error it wraps. + +### Retry Backoff is Enforced + +Drivers should test that retries within `withTransaction` do not occur immediately. + +1. let `client` be a `MongoClient` +2. let `coll` be a collection +3. Now, run transactions without backoff: + 1. Configure the random number generator used for jitter to always return `0` -- this effectively disables backoff. + + 2. Configure a fail point that forces 13 retries like so: + + ```python + set_fail_point( + { + "configureFailPoint": "failCommand", + "mode": { + "times": 13 + }, # sufficiently high enough such that the time effect of backoff is noticeable + "data": { + "failCommands": ["commitTransaction"], + "errorCode": 251, + }, + } + ) + ``` + + > Note: errorCode 251 is NoSuchTransaction. + + 3. Define the callback for the transaction as follows: + + ```python + def callback(session): + coll.insert_one({}, session=session) + ``` + + 4. Let `no_backoff_time` be the duration of the withTransaction API call: + + ```python + start = time.monotonic() + with client.start_session() as s: + s.with_transaction(callback) + end = time.monotonic() + no_backoff_time = end - start + ``` +4. Now run the command with backoff: + 1. Configure the random number generator used for jitter to always return a number as close as possible to `1`. + 2. Configure a fail point that forces 13 retries like in step 3.2. + 3. Use the same callback defined in 3.3. + 4. Let `with_backoff_time` be the duration of the withTransaction API call: + ```python + start = time.monotonic() + with client.start_session() as s: + s.with_transaction(callback) + end = time.monotonic() + no_backoff_time = end - start + ``` +5. Compare the durations of the two runs. + ```python + assertTrue(absolute_value(with_backoff_time - (no_backoff_time + 1.8 seconds)) < 0.5 seconds) + ``` + The sum of 13 backoffs is roughly 1.8 seconds. There is a half-second window to account for potential variance + between the two runs. + +## Changelog + +- 2026-04-02: [DRIVERS-3436](https://github.com/mongodb/specifications/pull/1920) Refine withTransaction timeout error + wrapping semantics and label propagation in spec and prose tests +- 2026-03-03: Clarify exponential backoff jitter upper bound. +- 2026-02-17: Clarify expected error when timeout is reached + [DRIVERS-3391](https://jira.mongodb.org/browse/DRIVERS-3391). +- 2026-01-07: Fixed Retry Backoff is Enforced test accordingly to the updated spec. +- 2025-11-18: Added Backoff test. +- 2024-09-06: Migrated from reStructuredText to Markdown. +- 2024-02-08: Converted legacy tests to unified format. +- 2021-04-29: Remove text about write concern timeouts from prose test. diff --git a/source/transactions-convenient-api/tests/unified/callback-aborts.json b/source/transactions-convenient-api/tests/unified/callback-aborts.json new file mode 100644 index 0000000000..dc8f7fb5a4 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-aborts.json @@ -0,0 +1,346 @@ +{ + "description": "callback-aborts", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction succeeds if callback aborts", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ] + }, + { + "description": "withTransaction succeeds if callback aborts with no ops", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "abortTransaction", + "object": "session0" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ] + }, + { + "description": "withTransaction still succeeds if callback aborts and runs extra op", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "abortTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "autocommit": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/callback-aborts.yml b/source/transactions-convenient-api/tests/unified/callback-aborts.yml new file mode 100644 index 0000000000..d5a95d7776 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-aborts.yml @@ -0,0 +1,179 @@ +description: callback-aborts + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + # Session state will be ABORTED when callback returns to withTransaction + description: withTransaction succeeds if callback aborts + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: abortTransaction + object: *session0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + - + # Session state will be ABORTED when callback returns to withTransaction + description: withTransaction succeeds if callback aborts with no ops + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: abortTransaction + object: *session0 + expectEvents: + - client: *client0 + events: [] + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + - + # Session state will be NO_TXN when callback returns to withTransaction + description: withTransaction still succeeds if callback aborts and runs extra op + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: abortTransaction + object: *session0 + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + command: + # This test is agnostic about retryWrites, so we do not assert the + # txnNumber. If retryWrites=true, the txnNumber will be incremented + # from the value used in the previous transaction; otherwise, the + # field will not be present at all. + insert: *collectionName + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + # omitted fields + autocommit: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 2 } diff --git a/source/transactions-convenient-api/tests/unified/callback-commits.json b/source/transactions-convenient-api/tests/unified/callback-commits.json new file mode 100644 index 0000000000..edda386418 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-commits.json @@ -0,0 +1,425 @@ +{ + "description": "callback-commits", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction succeeds if callback commits", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "commitTransaction", + "object": "session0" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "withTransaction still succeeds if callback commits and runs extra op", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "autocommit": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/callback-commits.yml b/source/transactions-convenient-api/tests/unified/callback-commits.yml new file mode 100644 index 0000000000..7bd3e7feae --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-commits.yml @@ -0,0 +1,210 @@ +description: callback-commits + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + # Session state will be COMMITTED when callback returns to withTransaction + description: withTransaction succeeds if callback commits + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - name: commitTransaction + object: *session0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - { _id: 2 } + - + # Session state will be NO_TXN when callback returns to withTransaction + description: withTransaction still succeeds if callback commits and runs extra op + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - name: commitTransaction + object: *session0 + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 3 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + # This test is agnostic about retryWrites, so we do not assert the + # txnNumber. If retryWrites=true, the txnNumber will be incremented + # from the value used in the previous transaction; otherwise, the + # field will not be present at all. + insert: *collectionName + documents: + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session0 } + readConcern: + afterClusterTime: { $$exists: true } + # omitted fields + autocommit: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } diff --git a/source/transactions-convenient-api/tests/unified/callback-retry.json b/source/transactions-convenient-api/tests/unified/callback-retry.json new file mode 100644 index 0000000000..277dfa18ed --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-retry.json @@ -0,0 +1,472 @@ +{ + "description": "callback-retry", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "callback succeeds after multiple connection errors", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "ignoreResultAndError": true + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "callback is not retried after non-transient error (DuplicateKeyError)", + "operations": [ + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ], + "errorContains": "E11000" + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/callback-retry.yml b/source/transactions-convenient-api/tests/unified/callback-retry.yml new file mode 100644 index 0000000000..2a89de0806 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/callback-retry.yml @@ -0,0 +1,249 @@ +description: callback-retry + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + # Define a second set of entities for useMultipleMongoses:true + - client: + id: &client1 client1 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - session: + id: &session1 session1 + client: *client1 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: callback succeeds after multiple connection errors + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309) + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + closeConnection: true + - name: withTransaction + object: *session0 + arguments: + callback: + - + # We do not assert the result here, as insertOne will fail for + # the first two executions of the callback before ultimately + # succeeding and returning a result. Asserting the state of the + # output collection after the test is sufficient. + name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + ignoreResultAndError: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # second transaction will be causally consistent with the first + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "2" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "2" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # third transaction will be causally consistent with the second + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "3" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "3" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + description: callback is not retried after non-transient error (DuplicateKeyError) + operations: + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection1 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: insertOne + object: *collection1 + arguments: + session: *session1 + document: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + expectError: + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + # DuplicateKey error code included in the bulk write error message + # returned by the server + errorContains: E11000 + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] diff --git a/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.json b/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.json new file mode 100644 index 0000000000..c6a4e44d62 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.json @@ -0,0 +1,231 @@ +{ + "description": "commit-retry-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commit is retried after commitTransaction UnknownTransactionCommitResult (NotWritablePrimary)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 10107, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.yml b/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.yml new file mode 100644 index 0000000000..f6f61375b8 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-retry-errorLabels.yml @@ -0,0 +1,118 @@ +description: commit-retry-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: commit is retried after commitTransaction UnknownTransactionCommitResult (NotWritablePrimary) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 10107 # NotWritablePrimary + errorLabels: ["RetryableWriteError"] # SPEC-1565 + closeConnection: false + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } diff --git a/source/transactions-convenient-api/tests/unified/commit-retry.json b/source/transactions-convenient-api/tests/unified/commit-retry.json new file mode 100644 index 0000000000..928f0167e4 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-retry.json @@ -0,0 +1,552 @@ +{ + "description": "commit-retry", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction succeeds after multiple connection errors", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction retry only overwrites write concern w option", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "writeConcern": { + "w": 2, + "journal": true, + "wtimeoutMS": 5000 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 2, + "j": true, + "wtimeout": 5000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "j": true, + "wtimeout": 5000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "j": true, + "wtimeout": 5000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commit is not retried after MaxTimeMSExpired error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 50 + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "maxCommitTimeMS": 60000 + }, + "expectError": { + "errorCodeName": "MaxTimeMSExpired", + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "maxTimeMS": 60000, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit-retry.yml b/source/transactions-convenient-api/tests/unified/commit-retry.yml new file mode 100644 index 0000000000..22d2125485 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-retry.yml @@ -0,0 +1,279 @@ +description: commit-retry + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: commitTransaction succeeds after multiple connection errors + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309) + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + closeConnection: true + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + description: commitTransaction retry only overwrites write concern w option + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309) + runOnRequirements: + - serverless: forbid + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + closeConnection: true + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + writeConcern: { w: 2, journal: true, wtimeoutMS: 5000 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 2, j: true, wtimeout: 5000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, j: true, wtimeout: 5000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, j: true, wtimeout: 5000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + description: commit is not retried after MaxTimeMSExpired error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ commitTransaction ] + errorCode: 50 # MaxTimeMSExpired + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + maxCommitTimeMS: 60000 + expectError: + errorCodeName: MaxTimeMSExpired + errorLabelsContain: ["UnknownTransactionCommitResult"] + errorLabelsOmit: ["TransientTransactionError"] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + maxTimeMS: 60000 + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + # In reality, the outcome of the commit is unknown but we fabricate + # the error with failCommand.errorCode which does not apply the commit + # operation. + documents: [] diff --git a/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.json b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.json new file mode 100644 index 0000000000..0f5a782452 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.json @@ -0,0 +1,294 @@ +{ + "description": "commit-transienttransactionerror-4.2", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.1.6", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "transaction is retried after commitTransaction TransientTransactionError (PreparedTransactionInProgress)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 267, + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.yml b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.yml new file mode 100644 index 0000000000..36cf90b91e --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror-4.2.yml @@ -0,0 +1,159 @@ +description: commit-transienttransactionerror-4.2 + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: '4.1.6' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +# These tests use error codes where the TransientTransactionError label will be +# applied to the error response for commitTransaction. This will cause the +# entire transaction to be retried instead of commitTransaction. +# +# See: https://github.com/mongodb/mongo/blob/r4.1.6/src/mongo/db/handle_request_response.cpp +tests: + - + description: transaction is retried after commitTransaction TransientTransactionError (PreparedTransactionInProgress) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 267 # PreparedTransactionInProgress + closeConnection: false + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # second transaction will be causally consistent with the first + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "2" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "2" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # third transaction will be causally consistent with the second + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "3" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "3" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } diff --git a/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.json b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.json new file mode 100644 index 0000000000..dd5158d813 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.json @@ -0,0 +1,996 @@ +{ + "description": "commit-transienttransactionerror", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "transaction is retried after commitTransaction TransientTransactionError (LockTimeout)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 24, + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "transaction is retried after commitTransaction TransientTransactionError (WriteConflict)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 112, + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "transaction is retried after commitTransaction TransientTransactionError (SnapshotUnavailable)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 246, + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "transaction is retried after commitTransaction TransientTransactionError (NoSuchTransaction)", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 251, + "closeConnection": false + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.yml b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.yml new file mode 100644 index 0000000000..befd8f1031 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-transienttransactionerror.yml @@ -0,0 +1,212 @@ +description: commit-transienttransactionerror + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +# These tests use error codes where the TransientTransactionError label will be +# applied to the error response for commitTransaction. This will cause the +# entire transaction to be retried instead of commitTransaction. +# +# See: https://github.com/mongodb/mongo/blob/r4.1.6/src/mongo/db/handle_request_response.cpp +tests: + - + description: transaction is retried after commitTransaction TransientTransactionError (LockTimeout) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 24 # LockTimeout + closeConnection: false + - &withTransaction + name: withTransaction + object: *session0 + arguments: + callback: + - + name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: &expectEvents + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # second transaction will be causally consistent with the first + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "2" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "2" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # third transaction will be causally consistent with the second + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented when retrying the transaction + txnNumber: { $numberLong: "3" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "3" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: &outcome + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + description: transaction is retried after commitTransaction TransientTransactionError (WriteConflict) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 112 # WriteConflict + closeConnection: false + - *withTransaction + expectEvents: *expectEvents + outcome: *outcome + - + description: transaction is retried after commitTransaction TransientTransactionError (SnapshotUnavailable) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 246 # SnapshotUnavailable + closeConnection: false + - *withTransaction + expectEvents: *expectEvents + outcome: *outcome + - + description: transaction is retried after commitTransaction TransientTransactionError (NoSuchTransaction) + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + errorCode: 251 # NoSuchTransaction + closeConnection: false + - *withTransaction + expectEvents: *expectEvents + outcome: *outcome diff --git a/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.json b/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.json new file mode 100644 index 0000000000..568f7ede42 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.json @@ -0,0 +1,812 @@ +{ + "description": "commit-writeconcernerror", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction is retried after WriteConcernTimeout timeout error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 64, + "errmsg": "waiting for replication timed out", + "errInfo": { + "wtimeout": true + } + } + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction is retried after WriteConcernTimeout non-timeout error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 64, + "errmsg": "multiple errors reported" + } + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction is not retried after UnknownReplWriteConcern error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 79, + "codeName": "UnknownReplWriteConcern", + "errmsg": "No write concern mode named 'foo' found in replica set configuration" + } + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + }, + "expectError": { + "errorCodeName": "UnknownReplWriteConcern", + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction is not retried after UnsatisfiableWriteConcern error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 100, + "codeName": "UnsatisfiableWriteConcern", + "errmsg": "Not enough data-bearing nodes" + } + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + }, + "expectError": { + "errorCodeName": "UnsatisfiableWriteConcern", + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction is not retried after MaxTimeMSExpired error", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 50, + "codeName": "MaxTimeMSExpired", + "errmsg": "operation exceeded time limit" + } + } + } + } + }, + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + }, + "expectError": { + "errorCodeName": "MaxTimeMSExpired", + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.yml b/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.yml new file mode 100644 index 0000000000..408f57fde1 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit-writeconcernerror.yml @@ -0,0 +1,248 @@ +description: commit-writeconcernerror + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: commitTransaction is retried after WriteConcernTimeout timeout error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + # Do not specify closeConnection: false, since that would conflict + # with writeConcernError (see: SERVER-39292) + writeConcernError: + code: 64 + errmsg: "waiting for replication timed out" + errInfo: { wtimeout: true } + - &operation + name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: &expectEvents_with_retries + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # commitTransaction applies w:majority on retries (SPEC-1185) + writeConcern: { w: majority, wtimeout: 10000 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + # The write operation is still applied despite the write concern error + outcome: &outcome + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + # This test configures the fail point to return an error with the + # WriteConcernTimeout code but without errInfo that would identify it as a + # wtimeout error. This tests that drivers do not assume that all + # WriteConcernTimeout errors are due to a replication timeout. + description: commitTransaction is retried after WriteConcernTimeout non-timeout error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ commitTransaction ] + # Do not specify closeConnection: false, since that would conflict + # with writeConcernError (see: SERVER-39292) + writeConcernError: + code: 64 + errmsg: "multiple errors reported" + - *operation + expectEvents: *expectEvents_with_retries + outcome: *outcome + - + description: commitTransaction is not retried after UnknownReplWriteConcern error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ commitTransaction ] + writeConcernError: + code: 79 + codeName: UnknownReplWriteConcern + errmsg: "No write concern mode named 'foo' found in replica set configuration" + - <<: *operation + expectError: + errorCodeName: UnknownReplWriteConcern + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + expectEvents: &expectEvents_without_retries + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + # failCommand with writeConcernError still applies the write operation(s) + outcome: *outcome + - + description: commitTransaction is not retried after UnsatisfiableWriteConcern error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ commitTransaction ] + writeConcernError: + code: 100 + codeName: UnsatisfiableWriteConcern + errmsg: "Not enough data-bearing nodes" + - <<: *operation + expectError: + errorCodeName: UnsatisfiableWriteConcern + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + expectEvents: *expectEvents_without_retries + # failCommand with writeConcernError still applies the write operation(s) + outcome: *outcome + - + description: commitTransaction is not retried after MaxTimeMSExpired error + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ commitTransaction ] + writeConcernError: + code: 50 + codeName: MaxTimeMSExpired + errmsg: "operation exceeded time limit" + - <<: *operation + expectError: + errorCodeName: MaxTimeMSExpired + errorLabelsContain: ["UnknownTransactionCommitResult"] + errorLabelsOmit: ["TransientTransactionError"] + expectEvents: *expectEvents_without_retries + # failCommand with writeConcernError still applies the write operation(s) + outcome: *outcome diff --git a/source/transactions-convenient-api/tests/unified/commit.json b/source/transactions-convenient-api/tests/unified/commit.json new file mode 100644 index 0000000000..5684d5ee89 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit.json @@ -0,0 +1,398 @@ +{ + "description": "commit", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction commits after callback returns", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "withTransaction commits after callback returns (second transaction)", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/commit.yml b/source/transactions-convenient-api/tests/unified/commit.yml new file mode 100644 index 0000000000..774ddb1b06 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/commit.yml @@ -0,0 +1,199 @@ +description: commit + +schemaVersion: '1.3' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: withTransaction commits after callback returns + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - { _id: 2 } + - + # In this scenario, the callback commits the transaction originally started + # by withTransaction and starts a second transaction before returning. Since + # withTransaction only examines the session's state, it should commit that + # second transaction after the callback returns. + description: withTransaction commits after callback returns (second transaction) + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - name: commitTransaction + object: *session0 + - name: startTransaction + object: *session0 + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + # second transaction will be causally consistent with the first + readConcern: { afterClusterTime: { $$exists: true } } + # txnNumber is incremented for the second transaction + txnNumber: { $numberLong: "2" } + startTransaction: true + autocommit: false + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "2" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/transactions-convenient-api/tests/unified/transaction-options.json b/source/transactions-convenient-api/tests/unified/transaction-options.json new file mode 100644 index 0000000000..b1a74c5fd1 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/transaction-options.json @@ -0,0 +1,819 @@ +{ + "description": "transaction-options", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction and no transaction options set", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction inherits transaction options from client", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": true, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction inherits transaction options from defaultTransactionOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "session": { + "id": "session1", + "client": "client0", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction explicit transaction options", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction explicit transaction options override defaultTransactionOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "session": { + "id": "session1", + "client": "client0", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "snapshot" + }, + "writeConcern": { + "w": "majority" + } + } + } + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction explicit transaction options override client options", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": true, + "uriOptions": { + "readConcernLevel": "local", + "w": "majority" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "withTransaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "withTransaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "withTransaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions-convenient-api/tests/unified/transaction-options.yml b/source/transactions-convenient-api/tests/unified/transaction-options.yml new file mode 100644 index 0000000000..fc6674c9e2 --- /dev/null +++ b/source/transactions-convenient-api/tests/unified/transaction-options.yml @@ -0,0 +1,379 @@ +description: transaction-options + +schemaVersion: '1.9' + +runOnRequirements: + - minServerVersion: '4.0' + topologies: [ replicaset ] + - minServerVersion: 4.1.8 + topologies: [ sharded, load-balanced ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName withTransaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - + description: withTransaction and no transaction options set + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: &outcome + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + - + description: withTransaction inherits transaction options from client + operations: + - object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: true + uriOptions: + readConcernLevel: local + w: 1 + observeEvents: [ commandStartedEvent ] + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - session: + id: &session1 session1 + client: *client1 + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection1 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + readConcern: { level: local } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: withTransaction inherits transaction options from defaultTransactionOptions + operations: + - object: testRunner + name: createEntities + arguments: + entities: + - session: + id: &session1 session1 + client: *client0 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: majority } + writeConcern: { w: 1 } + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: withTransaction explicit transaction options + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + readConcern: { level: majority } + writeConcern: { w: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: withTransaction explicit transaction options override defaultTransactionOptions + operations: + - object: testRunner + name: createEntities + arguments: + entities: + - session: + id: &session1 session1 + client: *client0 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: snapshot } + writeConcern: { w: majority } + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + readConcern: { level: majority } + writeConcern: { w: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: withTransaction explicit transaction options override client options + operations: + - object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: true + uriOptions: + readConcernLevel: local + w: majority + observeEvents: [ commandStartedEvent ] + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - session: + id: &session1 session1 + client: *client1 + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection1 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + readConcern: { level: majority } + writeConcern: { w: 1 } + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: "1" } + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome diff --git a/source/transactions-convenient-api/transactions-convenient-api.md b/source/transactions-convenient-api/transactions-convenient-api.md new file mode 100644 index 0000000000..c28ffcb9d6 --- /dev/null +++ b/source/transactions-convenient-api/transactions-convenient-api.md @@ -0,0 +1,471 @@ +# Convenient API for Transactions + +- Status: Accepted +- Minimum Server Version: 4.0 + +______________________________________________________________________ + +## Abstract + +Reliably committing a transaction in the face of errors can be a complicated endeavor using the MongoDB 4.0 drivers API. +This specification introduces a `withTransaction` method on the ClientSession object that allows application logic to be +executed within a transaction. This method is capable of retrying either the commit operation or entire transaction as +needed (and when the error permits) to better ensure that the transaction can complete successfully. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Terms + + + +**Callback** + +A user-defined function that will be passed to the helper method defined in this specification. Depending on the +implementing language, this may be a closure, function pointer, or other callable type. + +**ClientSession** + +Driver object representing a client session, as defined in the [Driver Session](../sessions/driver-sessions.md) +specification. The name of this object MAY vary across drivers. + +**MongoClient** + +The root object of a driver's API. The name of this object MAY vary across drivers. + + + +**TransactionOptions** + +Options for `ClientSession.startTransaction`, as defined in the [Transactions](../transactions/transactions.md) +specification. The structure of these options MAY vary across drivers (e.g. dictionary, typed class). + +### Naming Deviations + +This specification defines the name for a new ClientSession method, `withTransaction`. Drivers SHOULD use the defined +name but MAY deviate to comply with their existing conventions. For example, a driver may use `with_transaction` instead +of `withTransaction`. + +### Callback Semantics + +The purpose of the callback is to allow the application to specify some sequence of operations to be executed within the +body of a transaction. In an ideal scenario, `withTransaction` will start a transaction, execute the callback, and +commit the transaction. In the event of error, the commit or entire transaction may need to be retried and thusly the +callback could be invoked multiple times. + +Drivers MUST ensure that the application can access the ClientSession within the callback, since the ClientSession will +be needed to associate operations with the transaction. Drivers may elect an idiomatic approach to satisfy this +requirement (e.g. require the callback to receive the ClientSession as its first argument, expect the callback to access +the ClientSession from its lexical scope). Drivers MAY allow the callback to support additional parameters as needed +(e.g. user data parameter, error output parameter). Drivers MAY allow the callback to return a value to be propagated as +the return value of `withTransaction`. + +### ClientSession Changes + +This specification introduces a `withTransaction` method on the ClientSession class: + +```typescript +interface ClientSession { + withTransaction(function callback, + Optional options, + ... /* other arguments as needed */): any + + // other existing members of ClientSession +} +``` + +#### ClientSession.withTransaction + +This method is responsible for starting a transaction, invoking a callback, and committing a transaction. The callback +is expected to execute one or more operations with the transaction; however, that is not enforced. The callback is +allowed to execute other operations not associated with the transaction. + +Since `withTransaction` includes logic to retry transactions and commits, drivers MUST apply timeouts per +[Client Side Operations Timeout: Convenient Transactions API](../client-side-operations-timeout/client-side-operations-timeout.md#convenient-transactions-api). +If `timeoutMS` is unset for a `withTransaction` call, drivers MUST enforce a 120-second timeout to limit retry behavior +and safeguard applications from long-running (or infinite) retry loops. Drivers SHOULD use a monotonic clock to +determine elapsed time. + +If an UnknownTransactionCommitResult error is encountered for a commit, the driver MUST retry the commit if and only if +the error is not MaxTimeMSExpired and the retry timeout has not been exceeded. Otherwise, the driver MUST NOT retry the +commit and allow `withTransaction` to propagate the error to its caller. + +If a TransientTransactionError is encountered at any point, the entire transaction may be retried. If the retry timeout +has not been exceeded, the driver MUST retry a transaction that fails with an error bearing the +"TransientTransactionError" label. Since retrying the entire transaction will entail invoking the callback again, +drivers MUST document that the callback may be invoked multiple times (i.e. one additional time per retry attempt) and +MUST document the risk of side effects from using a non-idempotent callback. If the retry timeout has been exceeded, +drivers MUST NOT retry the transaction and allow `withTransaction` to propagate the error to its caller. When retrying, +drivers MUST implement an exponential backoff with jitter following the algorithm described below. + +If an error bearing neither the UnknownTransactionCommitResult nor the TransientTransactionError label is encountered at +any point, the driver MUST NOT retry and MUST allow `withTransaction` to propagate the error to its caller. + +This method MUST receive a [callback](#callback) as its first parameter. An optional +[TransactionOptions](#TransactionOptions) MUST be provided as its second parameter (with deviations permitted as +outlined in the [CRUD](../crud/crud.md#deviations) specification). Drivers MAY support other parameters or options as +needed (e.g. user data to pass as a parameter to the callback). + +##### Sequence of Actions + +This method should perform the following sequence of actions: + +1. Define the following: + + 1. Record the current monotonic time, which will be used to enforce the 120-second / CSOT timeout before later retry + attempts. + 2. Set `transactionAttempt` to `0`. + 3. Set `TIMEOUT_MS` to be `timeoutMS` if given, otherwise 120-seconds. + +2. If `transactionAttempt` > 0: + + 1. Calculate `backoffMS` to be `jitter * min(BACKOFF_INITIAL * 1.5 ** (transactionAttempt - 1), BACKOFF_MAX)`. If + elapsed time + `backoffMS` > `TIMEOUT_MS`, then propagate the previously encountered error to the caller of + `withTransaction` as per [timeout error propagation](#timeout-error-propagation) and return immediately. + Otherwise, sleep for `backoffMS`. + + 1. jitter is a random float between \[0, 1), optionally including 1, depending on what is most natural for the + given driver language. + + 2. `transactionAttempt` is the variable defined in step 1. + + 3. `BACKOFF_INITIAL` is 5ms + + 4. `BACKOFF_MAX` is 500ms + +3. Invoke [startTransaction](../transactions/transactions.md#starttransaction) on the session and increment + `transactionAttempt`. If TransactionOptions were specified in the call to `withTransaction`, those MUST be used + for `startTransaction`. Note that `ClientSession.defaultTransactionOptions` will be used in the absence of any + explicit TransactionOptions. + +4. If `startTransaction` reported an error, propagate that error to the caller of `withTransaction` as is and return + immediately. + +5. Invoke the callback. Drivers MUST ensure that the ClientSession can be accessed within the callback (e.g. pass + ClientSession as the first parameter, rely on lexical scoping). Drivers MAY pass additional parameters as needed + (e.g. user data solicited by withTransaction). + +6. Control returns to `withTransaction`. Determine the current + [state](../transactions/transactions.md#clientsession-changes) of the ClientSession and whether the callback + reported an error (e.g. thrown exception, error output parameter). + +7. If the callback reported an error: + + 1. If the ClientSession is in the "starting transaction" or "transaction in progress" state, invoke + [abortTransaction](../transactions/transactions.md#aborttransaction) on the session. + + 2. If the callback's error includes a "TransientTransactionError" label, jump back to step two. + + 3. If the callback's error includes a "UnknownTransactionCommitResult" label, the callback must have manually + committed a transaction, propagate the callback's error to the caller of `withTransaction` as is and return + immediately. + + 4. Otherwise, propagate the callback's error to the caller of `withTransaction` as is and return immediately. + +8. If the ClientSession is in the "no transaction", "transaction aborted", or "transaction committed" state, assume the + callback intentionally aborted or committed the transaction and return immediately. + +9. Invoke [commitTransaction](../transactions/transactions.md#committransaction) on the session. + +10. If `commitTransaction` reported an error: + + 1. If the `commitTransaction` error includes a `UnknownTransactionCommitResult` label and the error is not + `MaxTimeMSExpired` + + 1. If the elapsed time of `withTransaction` exceeded `TIMEOUT_MS`, propagate the `commitTransaction` error to the + caller of `withTransaction` as per [timeout error propagation](#timeout-error-propagation) and return + immediately. + 2. Otherwise, jump back to step nine. We will trust `commitTransaction` to apply a majority write concern on + retry attempts (see: + [Majority write concern is used when retrying commitTransaction](#majority-write-concern-is-used-when-retrying-committransaction)). + + 2. If the `commitTransaction` error includes a `TransientTransactionError` label, jump back to step two. + + 3. Otherwise, propagate the `commitTransaction` error to the caller of `withTransaction` as is and return + immediately. + +11. The transaction was committed successfully. Return immediately. + +###### Timeout Error propagation + +When the previously encountered error needs to be propagated because there is no more time for another attempt, and it +is not already a [timeout error](../client-side-operations-timeout/client-side-operations-timeout.md#errors), then: + +- A timeout error MUST be propagated instead. It MUST expose the previously encountered error as specified in the + ["Errors" section of the CSOT specification](../client-side-operations-timeout/client-side-operations-timeout.md#errors). + - If exposing the previously encountered error from a timeout error is impossible in a driver, then the driver is + exempt from the requirement and MUST propagate the previously encountered error as is. +- The timeout error MUST copy all error labels from the previously encountered error. + +##### Pseudo-code + +This method can be expressed by the following pseudo-code: + +```typescript +var BACKOFF_INITIAL = 5 // 5ms initial backoff +var BACKOFF_MAX = 500 // 500ms max backoff +withTransaction(callback, options) { + // Note: drivers SHOULD use a monotonic clock to determine elapsed time + var startTime = Date.now(); // milliseconds since Unix epoch + // See the CSOT specification for information on calculating timeoutMS for a convenient transaction API call. + var timeout = getCSOTTimeoutIfSet() ?? 120_000; + var transactionAttempt = 0; + var lastError = null; + + retryTransaction: while (true) { + if (transactionAttempt > 0) { + var backoff = Math.random() * min(BACKOFF_INITIAL * 1.5 ** (transactionAttempt - 1), + BACKOFF_MAX); + + if (Date.now() + backoff - startTime >= timeout) { + throw makeTimeoutError(lastError); + } + sleep(backoff); + } + + this.startTransaction(options); // may throw on error + transactionAttempt += 1; + + try { + callback(this); + } catch (error) { + lastError = error; + if (this.transactionState == STARTING || + this.transactionState == IN_PROGRESS) { + this.abortTransaction(); + } + + if (error.hasErrorLabel("TransientTransactionError")) { + if (Date.now() - startTime < timeout) { + continue retryTransaction; + } else { + throw makeTimeoutError(error) + } + } + + throw error; + } + + if (this.transactionState == NO_TXN || + this.transactionState == COMMITTED || + this.transactionState == ABORTED) { + return; // Assume callback intentionally ended the transaction + } + + retryCommit: while (true) { + try { + /* We will rely on ClientSession.commitTransaction() to + * apply a majority write concern if commitTransaction is + * being retried (see: DRIVERS-601) */ + this.commitTransaction(); + } catch (error) { + /* Note: a maxTimeMS error will have the MaxTimeMSExpired + * code (50) and can be reported as a top-level error or + * inside writeConcernError, ie: + * {ok:0, code: 50, codeName: "MaxTimeMSExpired"} + * {ok:1, writeConcernError: {code: 50, codeName: "MaxTimeMSExpired"}} + */ + lastError = error; + if (!isMaxTimeMSExpiredError(error) && + error.hasErrorLabel("UnknownTransactionCommitResult")) { + if (Date.now() - startTime >= timeout) { + throw makeTimeoutError(error); + } + continue retryCommit; + } + + if (error.hasErrorLabel("TransientTransactionError")) { + continue retryTransaction; + } + + throw error; + } + break; // Commit was successful + } + break; // Transaction was successful + } +} + +function makeTimeoutError(error) { + return getCSOTTimeoutIfSet() != null ? createCSOTMongoTimeoutException(error) : createLegacyMongoTimeoutException(error); +} +``` + +### ClientSession must provide access to a MongoClient + +The callback invoked by `withTransaction` is only guaranteed to receive a ClientSession parameter. Drivers MUST ensure +that it is possible to obtain a MongoClient within the callback in order to execute operations within the transaction. +Per the [Driver Session](../sessions/driver-sessions.md) specification, ClientSessions should already provide access to +a client object. + +### Handling errors inside the callback + +Drivers MUST document that the callback MUST NOT silently handle command errors without allowing such errors to +propagate. Command errors may abort the transaction on the server, and an attempt to commit the transaction will be +rejected with `NoSuchTransaction` error. + +For example, `DuplicateKeyError` is an error that aborts a transaction on the server. If the callback catches +`DuplicateKeyError` and does not report it, the driver will attempt to commit the transaction. The server will reject +the commit attempt with `NoSuchTransaction` error. This error has the "TransientTransactionError" label and the driver +will retry the commit. This will result in an infinite loop. + +Drivers MUST recommend that the callback report command errors if they need to be handled inside the callback. Drivers +SHOULD also recommend using Core Transaction API if a user wants to handle errors in a custom way. + +## Test Plan + +See the [README](tests/README.md) for tests. + +## Motivation for Change + +Reliably committing a transaction in the face of errors can be a complicated endeavor using the MongoDB 4.0 drivers API. +Providing helper method in the driver to execute a transaction (and retry when possible) will enable our users to make +better use of transactions in their applications. + +## Design Rationale + +This specification introduces a helper method on the ClientSession object that applications may optionally employ to +execute a user-defined function within a transaction. An application does not need to be modified unless it wants to +take advantage of this helper method. + +### Majority write concern is used when retrying commitTransaction + +Drivers should apply a majority write concern when retrying commitTransaction to guard against a transaction being +applied twice. This requirement was originally enforced in the implementation of `withTransaction`, but will now be +handled by the transaction spec itself in order to benefit applications irrespective of whether they use +`withTransaction` (see the corresponding section in the +[Transactions spec Design Rationale](../transactions/transactions.md#majority-write-concern-is-used-when-retrying-committransaction)). + +### The callback function has a flexible signature + +An original design considered requiring the callback to accept a ClientSession as its first parameter. That could be +superfluous for languages where the callback might already have access to ClientSession through its lexical scope. +Instead, the spec simply requires that drivers ensure the callback will be able to access the ClientSession. + +Similarly, the specification does not concern itself with the return type of the callback function. If drivers allow the +callback to return a value, they may also choose to propagate that value as the return value of withTransaction. + +An earlier design also considered using the callback's return value to indicate whether control should break out of +`withTransaction` (and its retry loop) and return to the application. The design allows this to be accomplished in one +of two ways: + +- The callback aborts the transaction directly and returns to `withTransaction`, which will then return to its caller. +- The callback propagates an error without the `TransientTransactionError` label, in which case `withTransaction` will + abort the transaction and return to its caller. + +### Applications are responsible for passing ClientSession for operations within a transaction + +It remains the responsibility of the application to pass a ClientSession to all operations that should be included in a +transaction. With regard to `withTransaction`, applications are free to execute any operations within the callback, +irrespective of whether those operations are associated with the transaction. + +### It is assumed that the callback will not start a new transaction on the ClientSession + +Under normal circumstances, the callback should not commit the transaction nor should it start a new transaction. The +`withTransaction` method will inspect the ClientSession's transaction state after the callback returns and take the most +sensible course of action; however, it will not detect whether the callback has started a new transaction. + +### The callback function may be executed multiple times + +The callback may be executed any number of times. Drivers are free to encourage their users to design idempotent +callbacks. + +### The commit is retried after a write concern timeout (i.e. wtimeout) error + +Per the Transactions specification, drivers internally retry `commitTransaction` once if it fails due to a retryable +error (as defined in the [Retryable Writes](../retryable-writes/retryable-writes.md#terms) specification). Beyond that, +applications may manually retry `commitTransaction` if it fails with any error bearing the +[UnknownTransactionCommitResult](../transactions/transactions.md#unknowntransactioncommitresult) error label. This label +is applied for the the following errors: + +- Server selection failure +- Retryable error (as defined in the [Retryable Writes](../retryable-writes/retryable-writes.md#terms) specification) +- Write concern failure or timeout (excluding UnsatisfiableWriteConcern and UnknownReplWriteConcern) +- MaxTimeMSExpired errors, ie `{ok:0, code: 50, codeName: "MaxTimeMSExpired"}` and + `{ok:1, writeConcernError: {code: 50, codeName: "MaxTimeMSExpired"}}`. + +A previous design for `withTransaction` retried for all of these errors *except* for write concern timeouts, so as not +to exceed the user's original intention for `wtimeout`. The current design of this specification no longer excludes +write concern timeouts, and simply retries `commitTransaction` within its timeout period for all errors bearing the +"UnknownTransactionCommitResult" label. + +### The commit is not retried after a MaxTimeMSExpired error + +This specification intentionally chooses not to retry commit operations after a MaxTimeMSExpired error as doing so would +exceed the user's original intention for `maxTimeMS`. + +### The transaction and commit may be retried any number of times within a timeout period + +The callback may be executed any number of times. Drivers are free to encourage their users to design idempotent +callbacks. + +A previous design had no limits for retrying commits or entire transactions. The callback is always able to indicate +that `withTransaction` should return to its caller (without future retry attempts) by aborting the transaction directly; +however, that puts the onus on avoiding very long (or infinite) retry loops on the application. We expect the most +common cause of retry loops will be due to TransientTransactionErrors caused by write conflicts, as those can occur +regularly in a healthy application, as opposed to UnknownTransactionCommitResult, which would typically be caused by an +election. + +In order to avoid blocking the application with infinite retry loops, `withTransaction` will cease retrying invocations +of the callback or commitTransaction if it has exceeded a fixed timeout period of 120 seconds. This limit is a +non-configurable default and is intentionally twice the value of MongoDB 4.0's default for the +[transactionLifetimeLimitSeconds](https://www.mongodb.com/docs/manual/reference/parameters/#param.transactionLifetimeLimitSeconds) +parameter (60 seconds). Applications that desire longer retry periods may call `withTransaction` additional times as +needed. Applications that desire shorter retry periods should not use this method. + +### Backoff Benefits + +Previously, the driver would retry transactions immediately, which is fine for low levels of contention. But, as the +server load increases, immediate retries can result in retry storms, unnecessarily further overloading the server. + +Exponential backoff is well-researched and accepted backoff strategy that is simple to implement. A low initial backoff +(5-milliseconds) and growth value (1.5x) were chosen specifically to mitigate latency in low levels of contention. +Empirical evidence suggests that 500-millisecond max backoff ensured that a transaction did not wait so long as to +exceed the 120-second timeout and reduced load spikes. + +## Backwards Compatibility + +The specification introduces a new method on the ClientSession class and does not introduce any backward breaking +changes. Existing programs that do not make use of this new method will continue to compile and run correctly. + +## Reference Implementation + +The C, Java, and Ruby drivers will provide reference implementations. The corresponding tickets for those +implementations may be found via [DRIVERS-556](https://jira.mongodb.org/browse/DRIVERS-556). + +## Security Implication + +Applications that use transaction guarantees to enforce security rules will benefit from a less error-prone API. Adding +a helper method to execute a user-defined function within a transaction has few security implications, as it only +provides an implementation of a technique already described in the MongoDB 4.0 documentation +([DRIVERS-488](https://jira.mongodb.org/browse/DRIVERS-488)). + +## Changelog + +- 2026-04-02: [DRIVERS-3436](https://github.com/mongodb/specifications/pull/1920) Refine withTransaction timeout error + wrapping semantics and label propagation in spec and prose tests. + +- 2026-03-03: Clarify exponential backoff jitter upper bound. + +- 2026-02-20: Fix initial backoff and growth value parameters in "Design Rationale" section. + +- 2026-02-17: Clarify expected error when timeout is reached + [DRIVERS-3391](https://jira.mongodb.org/browse/DRIVERS-3391). + +- 2025-11-20: withTransaction applies exponential backoff when retrying. + +- 2024-09-06: Migrated from reStructuredText to Markdown. + +- 2023-11-22: Document error handling inside the callback. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-19: withTransaction applies timeouts per the client-side operations timeout specification. + +- 2019-04-24: withTransaction does not retry when commit fails with MaxTimeMSExpired. + +- 2018-02-13: withTransaction should retry commits after a wtimeout diff --git a/source/transactions-convenient-api/transactions-convenient-api.rst b/source/transactions-convenient-api/transactions-convenient-api.rst new file mode 100644 index 0000000000..34f83712ed --- /dev/null +++ b/source/transactions-convenient-api/transactions-convenient-api.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `transactions-convenient-api.md `_. diff --git a/source/transactions/tests/README.md b/source/transactions/tests/README.md new file mode 100644 index 0000000000..8714009ca2 --- /dev/null +++ b/source/transactions/tests/README.md @@ -0,0 +1,106 @@ +# Transactions Tests + +______________________________________________________________________ + +## Introduction + +The YAML and JSON files in this directory are platform-independent tests meant to exercise a driver's implementation of +transactions. These tests utilize the [Unified Test Format](../../unified-test-format/unified-test-format.md). + +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. + +## Mongos Pinning Prose Tests + +The following tests ensure that a ClientSession is properly unpinned after a sharded transaction. Initialize these tests +with a MongoClient connected to multiple mongoses. + +These tests use a cursor's address field to track which server an operation was run on. If this is not possible in your +driver, use command monitoring instead. + +1. Test that starting a new transaction on a pinned ClientSession unpins the session and normal server selection is + performed for the next operation. + + ```python + @require_server_version(4, 1, 6) + @require_mongos_count_at_least(2) + def test_unpin_for_next_transaction(self): + # Increase localThresholdMS and wait until both nodes are discovered + # to avoid false positives. + client = MongoClient(mongos_hosts, localThresholdMS=1000) + wait_until(lambda: len(client.nodes) > 1) + # Create the collection. + client.test.test.insert_one({}) + with client.start_session() as s: + # Session is pinned to Mongos. + with s.start_transaction(): + client.test.test.insert_one({}, session=s) + + addresses = set() + for _ in range(50): + with s.start_transaction(): + cursor = client.test.test.find({}, session=s) + assert next(cursor) + addresses.add(cursor.address) + + assert len(addresses) > 1 + ``` + +2. Test non-transaction operations using a pinned ClientSession unpins the session and normal server selection is + performed. + + ```python + @require_server_version(4, 1, 6) + @require_mongos_count_at_least(2) + def test_unpin_for_non_transaction_operation(self): + # Increase localThresholdMS and wait until both nodes are discovered + # to avoid false positives. + client = MongoClient(mongos_hosts, localThresholdMS=1000) + wait_until(lambda: len(client.nodes) > 1) + # Create the collection. + client.test.test.insert_one({}) + with client.start_session() as s: + # Session is pinned to Mongos. + with s.start_transaction(): + client.test.test.insert_one({}, session=s) + + addresses = set() + for _ in range(50): + cursor = client.test.test.find({}, session=s) + assert next(cursor) + addresses.add(cursor.address) + + assert len(addresses) > 1 + ``` + +## PoolClearedError Prose Test + +Test that `PoolClearedError` has `TransientTransactionError` label. Since there is no simple way to trigger +`PoolClearedError`, this test should be implemented in a way that suites each driver the best. + +## Options Inside Transaction Prose Tests. + +These prose tests ensure drivers handle options inside a transaction where the unified tests do not suffice. Ensure +these tests do not run against a standalone server. + +### 1.0 Write concern not inherited from collection object inside transaction. + +- Create a MongoClient running against a configured sharded/replica set/load balanced cluster. + - transactions require a 4.0+ server when non-sharded and 4.2+ when sharded +- Start a new session on the client. +- Start a transaction on the session. +- Instantiate a collection object in the driver with a default write concern of `{ w: 0 }`. + - Create the collection or ensure it exists before the insert +- Insert the document `{ n: 1 }` on the instantiated collection. +- Commit the transaction. +- End the session. +- Ensure the document was inserted and no error was thrown from the transaction. + +## Changelog + +- 2025-03-31: Move PoolClearedError tests to its own section. +- 2025-03-27: Clarify server versions and collection creation for prose test +- 2024-10-31: Add test for PoolClearedError. +- 2024-02-15: Migrated from reStructuredText to Markdown. +- 2024-02-07: Converted legacy transaction tests to unified format and moved the legacy test format docs to a separate + file. diff --git a/source/transactions/tests/README.rst b/source/transactions/tests/README.rst deleted file mode 100644 index 838fb588c9..0000000000 --- a/source/transactions/tests/README.rst +++ /dev/null @@ -1,270 +0,0 @@ -================== -Transactions Tests -================== - -.. contents:: - ----- - -Introduction -============ - -The YAML and JSON files in this directory are platform-independent tests that -drivers can use to prove their conformance to the Transactions Spec. They are -designed with the intention of sharing some test-runner code with the CRUD Spec -tests and the Command Monitoring Spec tests. - -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. - -Server Fail Point -================= - -Some tests depend on a server fail point, expressed in the ``failPoint`` field. -For example the ``failCommand`` fail point allows the client to force the -server to return an error. Keep in mind that the fail point only triggers for -commands listed in the "failCommands" field. See `SERVER-35004`_ and -`SERVER-35083`_ for more information. - -.. _SERVER-35004: https://jira.mongodb.org/browse/SERVER-35004 -.. _SERVER-35083: https://jira.mongodb.org/browse/SERVER-35083 - -The ``failCommand`` fail point may be configured like so:: - - db.adminCommand({ - configureFailPoint: "failCommand", - mode: , - data: { - failCommands: ["commandName", "commandName2"], - closeConnection: , - errorCode: , - writeConcernError: - } - }); - -``mode`` is a generic fail point option and may be assigned a string or document -value. The string values ``"alwaysOn"`` and ``"off"`` may be used to enable or -disable the fail point, respectively. A document may be used to specify either -``times`` or ``skip``, which are mutually exclusive: - -- ``{ times: }`` may be used to limit the number of times the fail - point may trigger before transitioning to ``"off"``. -- ``{ skip: }`` may be used to defer the first trigger of a fail - point, after which it will transition to ``"alwaysOn"``. - -The ``data`` option is a document that may be used to specify options that -control the fail point's behavior. ``failCommand`` supports the following -``data`` options, which may be combined if desired: - -- ``failCommands``: Required, the list of command names to fail. -- ``closeConnection``: Boolean option, which defaults to ``false``. If - ``true``, the connection on which the command is executed will be closed - and the client will see a network error. -- ``errorCode``: Integer option, which is unset by default. If set, the - specified command error code will be returned as a command error. -- ``writeConcernError``: A document, which is unset by default. If set, the - server will return this document in the "writeConcernError" field. This - failure response only applies to commands that support write concern and - happens *after* the command finishes (regardless of success or failure). - -Test Format -=========== - -Each YAML file has the following keys: - -- ``database_name`` and ``collection_name``: The database and collection to use - for testing. - -- ``data``: The data that should exist in the collection under test before each - test run. - -- ``tests``: An array of tests that are to be run independently of each other. - Each test will have some or all of the following fields: - - - ``description``: The name of the test. - - - ``clientOptions``: Optional, parameters to pass to MongoClient(). - - - ``failPoint``: Optional, a server failpoint to enable expressed as the - configureFailPoint command to run on the admin database. - - - ``sessionOptions``: Optional, parameters to pass to - MongoClient.startSession(). - - - ``operations``: Array of documents, each describing an operation to be - executed. Each document has the following fields: - - - ``name``: The name of the operation on ``object``. - - - ``object``: The name of the object to perform the operation on. Can be - "database", "collection", "session0", or "session1". - - - ``collectionOptions``: Optional, parameters to pass to the Collection() - used for this operation. - - - ``command_name``: Present only when ``name`` is "runCommand". The name - of the command to run. Required for languages that are unable preserve - the order keys in the "command" argument when parsing JSON/YAML. - - - ``arguments``: Optional, the names and values of arguments. - - - ``result``: The return value from the operation, if any. This field may - be a single document or an array of documents in the case of a - multi-document read. If the operation is expected to return an error, the - ``result`` is a single document that has one or more of the following - fields: - - - ``errorContains``: A substring of the expected error message. - - - ``errorCodeName``: The expected "codeName" field in the server - error response. - - - ``errorLabelsContain``: A list of error label strings that the - error is expected to have. - - - ``errorLabelsOmit``: A list of error label strings that the - error is expected not to have. - - - ``expectations``: Optional list of command-started events. - - - ``outcome``: Document describing the return value and/or expected state of - the collection after the operation is executed. Contains the following - fields: - - - ``collection``: - - - ``data``: The data that should exist in the collection after the - operations have run. - -Use as integration tests -======================== - -Run a MongoDB replica set with a primary, a secondary, and an arbiter, -**server version 4.0.0-rc4 or later**. (Including a secondary ensures that -server selection in a transaction works properly. Including an arbiter helps -ensure that no new bugs have been introduced related to arbiters.) - -Load each YAML (or JSON) file using a Canonical Extended JSON parser. - -Then for each element in ``tests``: - -#. Create a MongoClient and call - ``client.admin.runCommand({killAllSessions: []})`` to clean up any open - transactions from previous test failures. The command will fail with message - "operation was interrupted", because it kills its own implicit session. Catch - the exception and continue. -#. Create a collection object from the MongoClient, using the ``database_name`` - and ``collection_name`` fields of the YAML file. -#. Drop the test collection, using writeConcern "majority". -#. Execute the "create" command to recreate the collection, using writeConcern - "majority". (Creating the collection inside a transaction is prohibited, so - create it explicitly.) -#. If the YAML file contains a ``data`` array, insert the documents in ``data`` - into the test collection, using writeConcern "majority". -#. If ``failPoint`` is specified, its value is a configureFailPoint command. - Run the command on the admin database to enable the fail point. -#. Create a **new** MongoClient ``client``, with Command Monitoring listeners - enabled. (Using a new MongoClient for each test ensures a fresh session pool - that hasn't executed any transactions previously, so the tests can assert - actual txnNumbers, starting from 1.) Pass this test's ``clientOptions`` if - present. -#. Call ``client.startSession`` twice to create ClientSession objects - ``session0`` and ``session1``, using the test's "sessionOptions" if they - are present. Save their lsids so they are available after calling - ``endSession``, see `Logical Session Id`. -#. For each element in ``operations``: - - - Enter a "try" block or your programming language's closest equivalent. - - Create a Database object from the MongoClient, using the ``database_name`` - field at the top level of the test file. - - Create a Collection object from the Database, using the - ``collection_name`` field at the top level of the test file. - If ``collectionOptions`` is present create the Collection object with the - provided options. Otherwise create the object with the default options. - - Execute the named method on the provided ``object``, passing the - arguments listed. Pass ``session0`` or ``session1`` to the method, - depending on which session's name is in the arguments list. - If ``arguments`` contains no "session", pass no explicit session to the - method. - - If the driver throws an exception / returns an error while executing this - series of operations, store the error message and server error code. - - If the result document has an "errorContains" field, verify that the - method threw an exception or returned an error, and that the value of the - "errorContains" field matches the error string. "errorContains" is a - substring (case-insensitive) of the actual error message. - - If the result document has an "errorCodeName" field, verify that the - method threw a command failed exception or returned an error, and that - the value of the "errorCodeName" field matches the "codeName" in the - server error response. - - If the result document has an "errorLabelsContain" field, verify that the - method threw an exception or returned an error. Verify that all of the - error labels in "errorLabelsContain" are present in the error or exception - using the ``hasErrorLabel`` method. - - If the result document has an "errorLabelsOmit" field, verify that the - method threw an exception or returned an error. Verify that none of the - error labels in "errorLabelsOmit" are present in the error or exception - using the ``hasErrorLabel`` method. - - If the operation returns a raw command response, eg from ``runCommand``, - then compare only the fields present in the expected result document. - Otherwise, compare the method's return value to ``result`` using the same - logic as the CRUD Spec Tests runner. - -#. Call ``session0.endSession()`` and ``session1.endSession``. -#. If the test includes a list of command-started events in ``expectations``, - compare them to the actual command-started events using the - same logic as the Command Monitoring Spec Tests runner, plus the rules in - the Command-Started Events instructions below. -#. If ``failPoint`` is specified, disable the fail point to avoid spurious - failures in subsequent tests. The fail point may be disabled like so:: - - db.adminCommand({ - configureFailPoint: , - mode: "off" - }); - -#. For each element in ``outcome``: - - - If ``name`` is "collection", verify that the test collection contains - exactly the documents in the ``data`` array. Ensure this find uses - Primary read preference even when the MongoClient is configured with - another read preference. - -Command-Started Events -`````````````````````` - -The event listener used for these tests MUST ignore the security commands -listed in the Command Monitoring Spec. - -Logical Session Id -~~~~~~~~~~~~~~~~~~ - -Each command-started event in ``expectations`` includes an ``lsid`` with the -value "session0" or "session1". Tests MUST assert that the command's actual -``lsid`` matches the id of the correct ClientSession named ``session0`` or -``session1``. - -Null Values -~~~~~~~~~~~ - -Some command-started events in ``expectations`` include ``null`` values for -fields such as ``txnNumber``, ``autocommit``, and ``writeConcern``. -Tests MUST assert that the actual command **omits** any field that has a -``null`` value in the expected command. - -Cursor Id -^^^^^^^^^ - -A ``getMore`` value of ``"42"`` in a command-started event is a fake cursorId -that MUST be ignored. (In the Command Monitoring Spec tests, fake cursorIds are -correlated with real ones, but that is not necessary for Transactions Spec -tests.) - -afterClusterTime -^^^^^^^^^^^^^^^^ - -A ``readConcern.afterClusterTime`` value of ``42`` in a command-started event -is a fake cluster time. Drivers MUST assert that the actual command includes an -afterClusterTime. diff --git a/source/transactions/tests/abort.json b/source/transactions/tests/abort.json deleted file mode 100644 index 084429b7a9..0000000000 --- a/source/transactions/tests/abort.json +++ /dev/null @@ -1,607 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "implicit abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "two aborts", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "cannot call abortTransaction twice" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort without start", - "operations": [ - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort directly after no-op commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call abortTransaction after calling commitTransaction" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort directly after commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call abortTransaction after calling commitTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "abort ignores TransactionAborted", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "DuplicateKey", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "NoSuchTransaction", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort does not apply writeConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 10 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/source/transactions/tests/abort.yml b/source/transactions/tests/abort.yml deleted file mode 100644 index f1bbca7630..0000000000 --- a/source/transactions/tests/abort.yml +++ /dev/null @@ -1,403 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: abort - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - "$numberLong": "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: implicit abort - - operations: - # Start a transaction but don't commit - the driver calls abortTransaction - # from ClientSession.endSession(). - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: two aborts - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - name: abortTransaction - object: session0 - result: - errorContains: cannot call abortTransaction twice - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abort without start - - operations: - - name: abortTransaction - object: session0 - result: - errorContains: no transaction started - - expectations: [] - - outcome: - collection: - data: [] - - - description: abort directly after no-op commit - - operations: - - name: startTransaction - object: session0 - - name: commitTransaction - object: session0 - - name: abortTransaction # Error calling abort after no-op commit. - object: session0 - result: - errorContains: Cannot call abortTransaction after calling commitTransaction - - expectations: [] - - outcome: - collection: - data: [] - - - description: abort directly after commit - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - name: abortTransaction # Error calling abort after commit. - object: session0 - result: - errorContains: Cannot call abortTransaction after calling commitTransaction - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: abort ignores TransactionAborted - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - # Abort the server transaction with a duplicate key error. - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - errorCodeName: DuplicateKey - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - # Make sure the server aborted the transaction. - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - errorCodeName: NoSuchTransaction - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - # abortTransaction must ignore the TransactionAborted and succeed. - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - - outcome: - collection: - data: [] - - - description: abort does not apply writeConcern - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: 10 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - # No write concern error. - - outcome: - collection: - data: [] diff --git a/source/transactions/tests/bulk.json b/source/transactions/tests/bulk.json deleted file mode 100644 index 62b2ccb92e..0000000000 --- a/source/transactions/tests/bulk.json +++ /dev/null @@ -1,525 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "bulk", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": 2 - } - }, - "upsert": true - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 5 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 6 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 7 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "y": 2 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 3 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gte": 2 - } - }, - "update": { - "$set": { - "z": 1 - } - } - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 6 - } - } - } - } - ] - }, - "result": { - "deletedCount": 4, - "insertedCount": 6, - "insertedIds": { - "0": 1, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7 - }, - "matchedCount": 7, - "modifiedCount": 7, - "upsertedCount": 1, - "upsertedIds": { - "2": 2 - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 1 - } - }, - "multi": false, - "upsert": false - }, - { - "q": { - "_id": 2 - }, - "u": { - "$set": { - "x": 2 - } - }, - "multi": false, - "upsert": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - }, - { - "_id": 6 - }, - { - "_id": 7 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "y": 1 - }, - "multi": false, - "upsert": false - }, - { - "q": { - "_id": 2 - }, - "u": { - "y": 2 - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 3 - }, - "limit": 1 - }, - { - "q": { - "_id": 4 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 2 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gte": 6 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "y": 1 - }, - { - "_id": 2, - "y": 2, - "z": 1 - }, - { - "_id": 5, - "z": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/bulk.yml b/source/transactions/tests/bulk.yml deleted file mode 100644 index 2ec4c9fbca..0000000000 --- a/source/transactions/tests/bulk.yml +++ /dev/null @@ -1,268 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: bulk - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: deleteOne - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - deletedCount: 1 - - name: bulkWrite - object: collection - arguments: - session: session0 - requests: - - name: insertOne - arguments: - document: {_id: 1} - - name: updateOne - arguments: - filter: {_id: 1} - update: {$set: {x: 1}} - - name: updateOne - arguments: - filter: {_id: 2} - update: {$set: {x: 2}} - upsert: true # Produces upsertedIds: {2: 2} in the result. - - name: insertOne - arguments: - document: {_id: 3} - - name: insertOne - arguments: - document: {_id: 4} - - name: insertOne - arguments: - document: {_id: 5} - - name: insertOne - arguments: - document: {_id: 6} - - name: insertOne - arguments: - document: {_id: 7} - # Keep replaces segregated from updates, so that drivers that aren't able to coalesce - # adjacent updates and replaces into a single update command will still pass this test - - name: replaceOne - arguments: - filter: {_id: 1} - replacement: {y: 1} - - name: replaceOne - arguments: - filter: {_id: 2} - replacement: {y: 2} - - name: deleteOne - arguments: - filter: {_id: 3} - - name: deleteOne - arguments: - filter: {_id: 4} - - name: updateMany - arguments: - filter: {_id: {$gte: 2}} - update: {$set: {z: 1}} - # Keep deleteMany segregated from deleteOne, so that drivers that aren't able to coalesce - # adjacent mixed deletes into a single delete command will still pass this test - - name: deleteMany - arguments: - filter: {_id: {$gte: 6}} - result: - deletedCount: 4 - insertedCount: 6 - insertedIds: {0: 1, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7} - matchedCount: 7 - modifiedCount: 7 - upsertedCount: 1 - upsertedIds: {2: 2} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: 1} - limit: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - # Commands in the bulkWrite. - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 1} - u: {$set: {x: 1}} - multi: false - upsert: false - - q: {_id: 2} - u: {$set: {x: 2}} - multi: false - upsert: true - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 3 - - _id: 4 - - _id: 5 - - _id: 6 - - _id: 7 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 1} - u: {y: 1} - multi: false - upsert: false - - q: {_id: 2} - u: {y: 2} - multi: false - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: 3} - limit: 1 - - q: {_id: 4} - limit: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: {$gte: 2}} - u: {$set: {z: 1}} - multi: true - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: {$gte: 6}} - limit: 0 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - {_id: 1, y: 1} - - {_id: 2, y: 2, z: 1} - - {_id: 5, z: 1} diff --git a/source/transactions/tests/causal-consistency.json b/source/transactions/tests/causal-consistency.json deleted file mode 100644 index 567350f361..0000000000 --- a/source/transactions/tests/causal-consistency.json +++ /dev/null @@ -1,294 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1, - "count": 0 - } - ], - "tests": [ - { - "description": "causal consistency", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - }, - "upsert": false - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - }, - "upsert": false - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": null, - "txnNumber": null, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "count": 2 - } - ] - } - } - }, - { - "description": "causal consistency disabled", - "sessionOptions": { - "session0": { - "causalConsistency": false - } - }, - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - }, - "upsert": false - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "count": 1 - }, - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/causal-consistency.yml b/source/transactions/tests/causal-consistency.yml deleted file mode 100644 index bfb8572562..0000000000 --- a/source/transactions/tests/causal-consistency.yml +++ /dev/null @@ -1,173 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - count: 0 - -tests: - - description: causal consistency - - operations: - # Update a document without a transaction. - - &updateOne - name: updateOne - object: collection - arguments: - session: session0 - filter: {_id: 1} - update: - $inc: {count: 1} - upsert: false - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - # Updating the same document inside a transaction. - # Casual consistency ensures that the transaction snapshot is causally - # after the first updateOne. - - name: startTransaction - object: session0 - - *updateOne - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 1} - u: {$inc: {count: 1}} - multi: false - upsert: false - ordered: true - lsid: session0 - readConcern: - txnNumber: - startTransaction: - autocommit: - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 1} - u: {$inc: {count: 1}} - multi: false - upsert: false - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - count: 2 - - - description: causal consistency disabled - - sessionOptions: - session0: - causalConsistency: false - - operations: - # Insert a document without a transaction. - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - - name: startTransaction - object: session0 - - name: updateOne - object: collection - arguments: - session: session0 - filter: {_id: 1} - update: - $inc: {count: 1} - upsert: false - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - readConcern: - lsid: session0 - txnNumber: - autocommit: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 1} - u: {$inc: {count: 1}} - multi: false - upsert: false - ordered: true - # No afterClusterTime - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - count: 1 - - _id: 2 diff --git a/source/transactions/tests/commit.json b/source/transactions/tests/commit.json deleted file mode 100644 index 173fae6c4d..0000000000 --- a/source/transactions/tests/commit.json +++ /dev/null @@ -1,899 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "rerun commit after empty transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "multiple commits in a row", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "write concern error on commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 10 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit without start", - "operations": [ - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commit after no-op abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call commitTransaction after calling abortTransaction" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commit after abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call commitTransaction after calling abortTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "multiple commits after empty transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "reset session state commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "reset session state abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/commit.yml b/source/transactions/tests/commit.yml deleted file mode 100644 index 32f7309945..0000000000 --- a/source/transactions/tests/commit.yml +++ /dev/null @@ -1,593 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] -tests: - - description: commit - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - # Again, to verify that txnNumber is incremented. - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - _id: 2 - - - description: rerun commit after empty transaction - - operations: - - name: startTransaction - object: session0 - - name: commitTransaction - object: session0 - # Rerun the commit (which does not increment the txnNumber). - - name: commitTransaction - object: session0 - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: multiple commits in a row - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - name: commitTransaction - object: session0 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: write concern error on commit - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: 10 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - # { - # 'ok': 1.0, - # 'writeConcernError': { - # 'code': 100, - # 'codeName': 'UnsatisfiableWriteConcern', - # 'errmsg': 'Not enough data-bearing nodes' - # } - # } - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - - outcome: - collection: - data: - - _id: 1 - - - description: commit without start - - operations: - - name: commitTransaction - object: session0 - result: - errorContains: no transaction started - - expectations: [] - - outcome: - collection: - data: [] - - - description: commit after no-op abort - - operations: - - name: startTransaction - object: session0 - - name: abortTransaction - object: session0 - - name: commitTransaction - object: session0 - result: - errorContains: Cannot call commitTransaction after calling abortTransaction - - expectations: [] - - outcome: - collection: - data: [] - - - description: commit after abort - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - name: commitTransaction - object: session0 - result: - errorContains: Cannot call commitTransaction after calling abortTransaction - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - - description: multiple commits after empty transaction - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - # Increments txnNumber. - - name: startTransaction - object: session0 - # These commits aren't sent to server, transaction is empty. - - name: commitTransaction - object: session0 - - name: commitTransaction - object: session0 - # Verify that previous, empty transaction incremented txnNumber. - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - # txnNumber 2 was skipped. - txnNumber: - $numberLong: "3" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "3" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: reset session state commit - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - # Running any operation after an ended transaction resets the session - # state to "no transaction". - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - # Calling commit again should error instead of re-running the commit. - - name: commitTransaction - object: session0 - result: - errorContains: no transaction started - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - readConcern: - lsid: session0 - txnNumber: - startTransaction: - autocommit: - command_name: insert - database_name: *database_name - - outcome: - collection: - data: - - _id: 1 - - _id: 2 - - - description: reset session state abort - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - # Running any operation after an ended transaction resets the session - # state to "no transaction". - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - # Calling abort should error with "no transaction started" instead of - # "cannot call abortTransaction twice". - - name: abortTransaction - object: session0 - result: - errorContains: no transaction started - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - readConcern: - lsid: session0 - txnNumber: - startTransaction: - autocommit: - command_name: insert - database_name: *database_name - - outcome: - collection: - data: - - _id: 2 diff --git a/source/transactions/tests/delete.json b/source/transactions/tests/delete.json deleted file mode 100644 index 80ff1be90b..0000000000 --- a/source/transactions/tests/delete.json +++ /dev/null @@ -1,313 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "tests": [ - { - "description": "delete", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$lte": 3 - } - } - }, - "result": { - "deletedCount": 2 - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$lte": 3 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 4 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 5 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for delete", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "deleteOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "deleteMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$lte": 3 - } - } - }, - "result": { - "deletedCount": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$lte": 3 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/delete.yml b/source/transactions/tests/delete.yml deleted file mode 100644 index 86b5a3a9aa..0000000000 --- a/source/transactions/tests/delete.yml +++ /dev/null @@ -1,184 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - - _id: 5 - -tests: - - description: delete - - operations: - - name: startTransaction - object: session0 - - name: deleteOne - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - deletedCount: 1 - - name: deleteMany - object: collection - arguments: - session: session0 - filter: - _id: {$lte: 3} - result: - deletedCount: 2 - - name: deleteOne - object: collection - arguments: - session: session0 - filter: - _id: 4 - result: - deletedCount: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: 1} - limit: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: {$lte: 3}} - limit: 0 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: 4} - limit: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 5 - - - description: collection writeConcern ignored for delete - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: deleteOne - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: - _id: 1 - result: - deletedCount: 1 - - name: deleteMany - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: - _id: {$lte: 3} - result: - deletedCount: 2 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: 1} - limit: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - delete: *collection_name - deletes: - - q: {_id: {$lte: 3}} - limit: 0 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: delete - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin diff --git a/source/transactions/tests/error-labels.json b/source/transactions/tests/error-labels.json deleted file mode 100644 index 27ac6c4057..0000000000 --- a/source/transactions/tests/error-labels.json +++ /dev/null @@ -1,1540 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "DuplicateKey errors do not contain transient label", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 1 - }, - { - "_id": 1 - } - ] - }, - "result": { - "errorCodeName": "DuplicateKey", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - }, - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NotMaster errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "WriteConflict errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 112 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NoSuchTransaction errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 251 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NoSuchTransaction errors on commit contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 251 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add transient label to connection errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 4 - }, - "data": { - "failCommands": [ - "insert", - "find", - "aggregate", - "distinct" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": {}, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add unknown commit label to connection errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add unknown commit label to retryable commit errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add unknown commit label to writeConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add unknown commit label to writeConcernError WriteConcernFailed", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "errmsg": "multiple errors reported" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add unknown commit label to writeConcernError WriteConcernFailed with wtimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": true - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "omit unknown commit label to writeConcernError UnsatisfiableWriteConcern", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 100, - "errmsg": "Not enough data-bearing nodes" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "omit unknown commit label to writeConcernError UnknownReplWriteConcern", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 79, - "errmsg": "No write concern mode named 'blah' found in replica set configuration" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/error-labels.yml b/source/transactions/tests/error-labels.yml deleted file mode 100644 index dbe93f0b7b..0000000000 --- a/source/transactions/tests/error-labels.yml +++ /dev/null @@ -1,949 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: DuplicateKey errors do not contain transient label - - operations: - - name: startTransaction - object: session0 - - name: insertMany - object: collection - arguments: - session: session0 - documents: - - _id: 1 - - _id: 1 - result: - errorCodeName: DuplicateKey - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: NotMaster errors contain transient label - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 10107 # NotMaster - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - # Note, the server will return the errorLabel in this case. - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: WriteConflict errors contain transient label - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 112 # WriteConflict - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - # Note, the server will return the errorLabel in this case. - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: NoSuchTransaction errors contain transient label - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - errorCode: 251 # NoSuchTransaction - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - # Note, the server will return the errorLabel in this case. - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: NoSuchTransaction errors on commit contain transient label - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 251 # NoSuchTransaction - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - # Note, the server will return the errorLabel in this case. - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: add transient label to connection errors - - failPoint: - configureFailPoint: failCommand - mode: { times: 4 } - data: - failCommands: ["insert", "find", "aggregate", "distinct"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: &transient_label_only - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: find - object: collection - arguments: - session: session0 - result: *transient_label_only - - name: aggregate - object: collection - arguments: - pipeline: - - $project: - _id: 1 - session: session0 - result: *transient_label_only - - name: distinct - object: collection - arguments: - fieldName: _id - session: session0 - result: *transient_label_only - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - find: *collection_name - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: find - database_name: *database_name - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $project: - _id: 1 - cursor: {} - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - distinct: *collection_name - key: _id - lsid: session0 - readConcern: - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: distinct - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: add unknown commit label to connection errors - - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["commitTransaction"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: add unknown commit label to retryable commit errors - - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["commitTransaction"] - errorCode: 11602 # InterruptedDueToReplStateChange - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: add unknown commit label to writeConcernError ShutdownInProgress - - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: add unknown commit label to writeConcernError WriteConcernFailed - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 64 # WriteConcernFailed without wtimeout - errmsg: multiple errors reported - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: add unknown commit label to writeConcernError WriteConcernFailed with wtimeout - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 64 - codeName: WriteConcernFailed - errmsg: waiting for replication timed out - errInfo: {wtimeout: True} - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: omit unknown commit label to writeConcernError UnsatisfiableWriteConcern - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 100 # UnsatisfiableWriteConcern/CannotSatisfyWriteConcern - errmsg: Not enough data-bearing nodes - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: omit unknown commit label to writeConcernError UnknownReplWriteConcern - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 79 # UnknownReplWriteConcern - errmsg: No write concern mode named 'blah' found in replica set configuration - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 diff --git a/source/transactions/tests/errors.json b/source/transactions/tests/errors.json deleted file mode 100644 index 159cc3ce5d..0000000000 --- a/source/transactions/tests/errors.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "start insert start", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ] - }, - { - "description": "start twice", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - } - ] - }, - { - "description": "commit and start twice", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - } - ] - }, - { - "description": "write conflict commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "WriteConflict", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session1", - "result": { - "errorCodeName": "NoSuchTransaction", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - } - ] - }, - { - "description": "write conflict abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "WriteConflict", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session1" - } - ] - } - ] -} diff --git a/source/transactions/tests/errors.yml b/source/transactions/tests/errors.yml deleted file mode 100644 index 57d0dc9716..0000000000 --- a/source/transactions/tests/errors.yml +++ /dev/null @@ -1,125 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] -tests: - - description: start insert start - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: startTransaction - object: session0 - result: - # Client-side error. - errorContains: transaction already in progress - # Just to clean up. - - name: commitTransaction - object: session0 - - - description: start twice - - operations: - - name: startTransaction - object: session0 - - name: startTransaction - object: session0 - result: - # Client-side error. - errorContains: transaction already in progress - - - description: commit and start twice - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - name: startTransaction - object: session0 - - name: startTransaction - object: session0 - result: - # Client-side error. - errorContains: transaction already in progress - - - description: write conflict commit - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: startTransaction - object: session1 - - name: insertOne - object: collection - arguments: - session: session1 - document: - _id: 1 - result: - errorCodeName: WriteConflict - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: commitTransaction - object: session0 - - name: commitTransaction - object: session1 - result: - errorCodeName: NoSuchTransaction - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - - description: write conflict abort - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: startTransaction - object: session1 - - name: insertOne - object: collection - arguments: - session: session1 - document: - _id: 1 - result: - errorCodeName: WriteConflict - errorLabelsContain: ["TransientTransactionError"] - errorLabelsOmit: ["UnknownTransactionCommitResult"] - - name: commitTransaction - object: session0 - # Driver ignores "NoSuchTransaction" error. - - name: abortTransaction - object: session1 diff --git a/source/transactions/tests/findOneAndDelete.json b/source/transactions/tests/findOneAndDelete.json deleted file mode 100644 index f02facf2a5..0000000000 --- a/source/transactions/tests/findOneAndDelete.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndDelete", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - } - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - } - }, - "result": null - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndDelete", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - } - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/findOneAndDelete.yml b/source/transactions/tests/findOneAndDelete.yml deleted file mode 100644 index 7ade9cbf46..0000000000 --- a/source/transactions/tests/findOneAndDelete.yml +++ /dev/null @@ -1,126 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - - _id: 2 - - _id: 3 - -tests: - - description: findOneAndDelete - - operations: - - name: startTransaction - object: session0 - - name: findOneAndDelete - object: collection - arguments: - session: session0 - filter: {_id: 3} - result: {_id: 3} - - name: findOneAndDelete - object: collection - arguments: - session: session0 - filter: {_id: 4} - result: - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - remove: True - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 4} - remove: True - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - {_id: 1} - - {_id: 2} - - - description: collection writeConcern ignored for findOneAndDelete - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: findOneAndDelete - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: {_id: 3} - result: {_id: 3} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - remove: True - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin diff --git a/source/transactions/tests/findOneAndReplace.json b/source/transactions/tests/findOneAndReplace.json deleted file mode 100644 index 9c77ebf4c3..0000000000 --- a/source/transactions/tests/findOneAndReplace.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndReplace", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "replacement": { - "x": 1 - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "replacement": { - "x": 1 - }, - "upsert": true, - "returnDocument": "After" - }, - "result": { - "_id": 4, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "x": 1 - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "update": { - "x": 1 - }, - "new": true, - "upsert": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "x": 1 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndReplace", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "replacement": { - "x": 1 - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "x": 1 - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/findOneAndReplace.yml b/source/transactions/tests/findOneAndReplace.yml deleted file mode 100644 index 58db2a758e..0000000000 --- a/source/transactions/tests/findOneAndReplace.yml +++ /dev/null @@ -1,140 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - - _id: 2 - - _id: 3 - -tests: - - description: findOneAndReplace - - operations: - - name: startTransaction - object: session0 - - name: findOneAndReplace - object: collection - arguments: - session: session0 - filter: {_id: 3} - replacement: {x: 1} - returnDocument: Before - result: {_id: 3} - - name: findOneAndReplace - object: collection - arguments: - session: session0 - filter: {_id: 4} - replacement: {x: 1} - upsert: true - returnDocument: After - result: {_id: 4, x: 1} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: {x: 1} - new: false - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 4} - update: {x: 1} - new: true - upsert: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - {_id: 1} - - {_id: 2} - - {_id: 3, x: 1} - - {_id: 4, x: 1} - - - description: collection writeConcern ignored for findOneAndReplace - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: findOneAndReplace - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: {_id: 3} - replacement: {x: 1} - returnDocument: Before - result: {_id: 3} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: {x: 1} - new: false - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - diff --git a/source/transactions/tests/findOneAndUpdate.json b/source/transactions/tests/findOneAndUpdate.json deleted file mode 100644 index c56e631e93..0000000000 --- a/source/transactions/tests/findOneAndUpdate.json +++ /dev/null @@ -1,399 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndUpdate", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true, - "returnDocument": "After" - }, - "result": { - "_id": 4, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3, - "x": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": true, - "upsert": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "x": 2 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndUpdate", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/findOneAndUpdate.yml b/source/transactions/tests/findOneAndUpdate.yml deleted file mode 100644 index 2616bb1667..0000000000 --- a/source/transactions/tests/findOneAndUpdate.yml +++ /dev/null @@ -1,228 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - - _id: 2 - - _id: 3 - -tests: - - description: findOneAndUpdate - - operations: - - name: startTransaction - object: session0 - - name: findOneAndUpdate - object: collection - arguments: - session: session0 - filter: {_id: 3} - update: - $inc: {x: 1} - returnDocument: Before - result: {_id: 3} - - name: findOneAndUpdate - object: collection - arguments: - session: session0 - filter: {_id: 4} - update: - $inc: {x: 1} - upsert: true - returnDocument: After - result: {_id: 4, x: 1} - - name: commitTransaction - object: session0 - - name: startTransaction - object: session0 - # Test a second time to ensure txnNumber is incremented. - - name: findOneAndUpdate - object: collection - arguments: - session: session0 - filter: {_id: 3} - update: - $inc: {x: 1} - returnDocument: Before - result: {_id: 3, x: 1} - - name: commitTransaction - object: session0 - # Test a third time to ensure abort works. - - name: startTransaction - object: session0 - - name: findOneAndUpdate - object: collection - arguments: - session: session0 - filter: {_id: 3} - update: - $inc: {x: 1} - returnDocument: Before - result: {_id: 3, x: 2} - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: {$inc: {x: 1}} - new: false - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 4} - update: {$inc: {x: 1}} - new: true - upsert: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: {$inc: {x: 1}} - new: false - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - afterClusterTime: 42 - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: {$inc: {x: 1}} - new: false - lsid: session0 - txnNumber: - $numberLong: "3" - startTransaction: true - autocommit: false - readConcern: - afterClusterTime: 42 - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "3" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: - - {_id: 1} - - {_id: 2} - - {_id: 3, x: 2} - - {_id: 4, x: 1} - - - description: collection writeConcern ignored for findOneAndUpdate - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: findOneAndUpdate - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: {_id: 3} - update: - $inc: {x: 1} - returnDocument: Before - result: {_id: 3} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - findAndModify: *collection_name - query: {_id: 3} - update: - $inc: {x: 1} - new: false - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: findAndModify - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - diff --git a/source/transactions/tests/insert.json b/source/transactions/tests/insert.json deleted file mode 100644 index 4dc9b85fc9..0000000000 --- a/source/transactions/tests/insert.json +++ /dev/null @@ -1,441 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "insert", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "insertedId": 4 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 5 - } - }, - "result": { - "insertedId": 5 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 5 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - }, - { - "description": "collection writeConcern without transaction", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for insert", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/insert.yml b/source/transactions/tests/insert.yml deleted file mode 100644 index 55f0a18ade..0000000000 --- a/source/transactions/tests/insert.yml +++ /dev/null @@ -1,264 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: insert - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: insertMany - object: collection - arguments: - documents: - - _id: 2 - - _id: 3 - session: session0 - result: - insertedIds: {0: 2, 1: 3} - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 4 - result: - insertedId: 4 - - name: commitTransaction - object: session0 - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 5 - result: - insertedId: 5 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - - _id: 3 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 4 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 5 - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - - _id: 5 - - # This test proves that the driver parses the collectionOptions writeConcern. - - description: collection writeConcern without transaction - operations: - - name: insertOne - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - startTransaction: - autocommit: - writeConcern: - w: majority - command_name: insert - database_name: *database_name - - outcome: - collection: - data: - - _id: 1 - - - description: collection writeConcern ignored for insert - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: insertMany - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - documents: - - _id: 2 - - _id: 3 - session: session0 - result: - insertedIds: {0: 2, 1: 3} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - - _id: 3 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - _id: 2 - - _id: 3 diff --git a/source/transactions/tests/isolation.json b/source/transactions/tests/isolation.json deleted file mode 100644 index f5b5ea0c28..0000000000 --- a/source/transactions/tests/isolation.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "one transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "two transactions", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "commitTransaction", - "object": "session1" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/isolation.yml b/source/transactions/tests/isolation.yml deleted file mode 100644 index 79e031f21a..0000000000 --- a/source/transactions/tests/isolation.yml +++ /dev/null @@ -1,125 +0,0 @@ -# Test snapshot isolation. -# This test doesn't check contents of command-started events. -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: one transaction - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: find - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - - {_id: 1} - - name: find - object: collection - arguments: - session: session1 - filter: - _id: 1 - result: [] - - name: find - object: collection - arguments: - filter: - _id: 1 - result: [] - - name: commitTransaction - object: session0 - - name: find - object: collection - arguments: - session: session1 - filter: - _id: 1 - result: - - {_id: 1} - - name: find - object: collection - arguments: - filter: - _id: 1 - result: - - {_id: 1} - - outcome: - collection: - data: - - _id: 1 - - - description: two transactions - - operations: - - name: startTransaction - object: session0 - - name: startTransaction - object: session1 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: find - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - - {_id: 1} - - name: find - object: collection - arguments: - session: session1 - filter: - _id: 1 - result: [] - - name: find - object: collection - arguments: - filter: - _id: 1 - result: [] - - name: commitTransaction - object: session0 - # Snapshot isolation in session1, not read-committed. - - name: find - object: collection - arguments: - session: session1 - filter: - _id: 1 - result: [] - - name: find - object: collection - arguments: - filter: - _id: 1 - result: - - {_id: 1} - - name: commitTransaction - object: session1 - - outcome: - collection: - data: - - {_id: 1} diff --git a/source/transactions/tests/legacy-test-format.md b/source/transactions/tests/legacy-test-format.md new file mode 100644 index 0000000000..73dc482797 --- /dev/null +++ b/source/transactions/tests/legacy-test-format.md @@ -0,0 +1,483 @@ +# Legacy Transactions Tests Format + +______________________________________________________________________ + +## Introduction + +This test format is no longer used by the transactions spec but may still be referenced by other specs (e.g. CSFLE) and +is preserved for historical record. + +## Server Fail Point + +### failCommand + +Some tests depend on a server fail point, expressed in the `failPoint` field. For example the `failCommand` fail point +allows the client to force the server to return an error. Keep in mind that the fail point only triggers for commands +listed in the "failCommands" field. See [SERVER-35004](https://jira.mongodb.org/browse/SERVER-35004) and +[SERVER-35083](https://jira.mongodb.org/browse/SERVER-35083) for more information. + +The `failCommand` fail point may be configured like so: + +```javascript + db.adminCommand({ + configureFailPoint: "failCommand", + mode: , + data: { + failCommands: ["commandName", "commandName2"], + closeConnection: , + errorCode: , + writeConcernError: , + appName: , + blockConnection: , + blockTimeMS: , + } + }); +``` + +`mode` is a generic fail point option and may be assigned a string or document value. The string values `"alwaysOn"` and +`"off"` may be used to enable or disable the fail point, respectively. A document may be used to specify either `times` +or `skip`, which are mutually exclusive: + +- `{ times: }` may be used to limit the number of times the fail point may trigger before transitioning to + `"off"`. +- `{ skip: }` may be used to defer the first trigger of a fail point, after which it will transition to + `"alwaysOn"`. + +The `data` option is a document that may be used to specify options that control the fail point's behavior. +`failCommand` supports the following `data` options, which may be combined if desired: + +- `failCommands`: Required, the list of command names to fail. +- `closeConnection`: Boolean option, which defaults to `false`. If `true`, the command will not be executed, the + connection will be closed, and the client will see a network error. +- `errorCode`: Integer option, which is unset by default. If set, the command will not be executed and the specified + command error code will be returned as a command error. +- `appName`: A string to filter which MongoClient should be affected by the failpoint. + [New in mongod 4.4.0-rc2](https://jira.mongodb.org/browse/SERVER-47195). +- `blockConnection`: Whether the server should block the affected commands. Default false. +- `blockTimeMS`: The number of milliseconds the affect commands should be blocked for. Required when blockConnection is + true. [New in mongod 4.3.4](https://jira.mongodb.org/browse/SERVER-41070). + +## Test Format + +Each YAML file has the following keys: + +- `runOn` (optional): An array of server version and/or topology requirements for which the tests can be run. If the + test environment satisfies one or more of these requirements, the tests may be executed; otherwise, this file should + be skipped. If this field is omitted, the tests can be assumed to have no particular requirements and should be + executed. Each element will have some or all of the following fields: + - `minServerVersion` (optional): The minimum server version (inclusive) required to successfully run the tests. If + this field is omitted, it should be assumed that there is no lower bound on the required server version. + + - `maxServerVersion` (optional): The maximum server version (inclusive) against which the tests can be run + successfully. If this field is omitted, it should be assumed that there is no upper bound on the required server + version. + + - `topology` (optional): An array of server topologies against which the tests can be run successfully. Valid + topologies are "single", "replicaset", "sharded", and "load-balanced". If this field is omitted, the default is + all topologies (i.e. `["single", "replicaset", "sharded", "load-balanced"]`). + + - `serverless`: (optional): Whether or not the test should be run on Atlas Serverless instances. Valid values are + "require", "forbid", and "allow". If "require", the test MUST only be run on Atlas Serverless instances. If + "forbid", the test MUST NOT be run on Atlas Serverless instances. If omitted or "allow", this option has no + effect. + + The test runner MUST be informed whether or not Atlas Serverless is being used in order to determine if this + requirement is met (e.g. through an environment variable or configuration option). + + Note: the Atlas Serverless proxy imitates mongos, so the test runner is not capable of determining if Atlas + Serverless is in use by issuing commands such as `buildInfo` or `hello`. Furthermore, connections to Atlas + Serverless use a load balancer, so the topology will appear as "load-balanced". +- `database_name` and `collection_name`: The database and collection to use for testing. +- `data`: The data that should exist in the collection under test before each test run. +- `tests`: An array of tests that are to be run independently of each other. Each test will have some or all of the + following fields: + - `description`: The name of the test. + + - `skipReason`: Optional, string describing why this test should be skipped. + + - `useMultipleMongoses` (optional): If `true`, and the topology type is `Sharded`, the MongoClient for this test + should be initialized with multiple mongos seed addresses. If `false` or omitted, only a single mongos address + should be specified. + + If `true`, the topology type is `LoadBalanced`, and Atlas Serverless is not being used, the MongoClient for this + test should be initialized with the URI of the load balancer fronting multiple servers. If `false` or omitted, the + MongoClient for this test should be initialized with the URI of the load balancer fronting a single server. + + `useMultipleMongoses` only affects `Sharded` and `LoadBalanced` topologies (excluding Atlas Serverless). + + - `clientOptions`: Optional, parameters to pass to MongoClient(). + + - `failPoint`: Optional, a server failpoint to enable expressed as the configureFailPoint command to run on the admin + database. This option and `useMultipleMongoses: true` are mutually exclusive. + + - `sessionOptions`: Optional, map of session names (e.g. "session0") to parameters to pass to + MongoClient.startSession() when creating that session. + + - `operations`: Array of documents, each describing an operation to be executed. Each document has the following + fields: + + - `name`: The name of the operation on `object`. + - `object`: The name of the object to perform the operation on. Can be "database", "collection", "session0", + "session1", or "testRunner". See the "targetedFailPoint" operation in + [Special Test Operations](#special-test-operations). + - `collectionOptions`: Optional, parameters to pass to the Collection() used for this operation. + - `databaseOptions`: Optional, parameters to pass to the Database() used for this operation. + - `command_name`: Present only when `name` is "runCommand". The name of the command to run. Required for languages + that are unable preserve the order keys in the "command" argument when parsing JSON/YAML. + - `arguments`: Optional, the names and values of arguments. + - `error`: Optional. If true, the test should expect an error or exception. This could be a server-generated or a + driver-generated error. + - `result`: The return value from the operation, if any. This field may be a single document or an array of + documents in the case of a multi-document read. If the operation is expected to return an error, the `result` is + a single document that has one or more of the following fields: + - `errorContains`: A substring of the expected error message. + - `errorCodeName`: The expected "codeName" field in the server error response. + - `errorLabelsContain`: A list of error label strings that the error is expected to have. + - `errorLabelsOmit`: A list of error label strings that the error is expected not to have. + + - `expectations`: Optional list of command-started events. + + - `outcome`: Document describing the return value and/or expected state of the collection after the operation is + executed. Contains the following fields: + + - `collection`: + - `data`: The data that should exist in the collection after the operations have run, sorted by "\_id". + +## Use as Integration Tests + +Run a MongoDB replica set with a primary, a secondary, and an arbiter, **server version 4.0.0 or later**. (Including a +secondary ensures that server selection in a transaction works properly. Including an arbiter helps ensure that no new +bugs have been introduced related to arbiters.) + +A driver that implements support for sharded transactions MUST also run these tests against a MongoDB sharded cluster +with multiple mongoses and **server version 4.2 or later**. Some tests require initializing the MongoClient with +multiple mongos seeds to ensures that mongos transaction pinning and the recoveryToken works properly. + +Load each YAML (or JSON) file using a Canonical Extended JSON parser. + +Then for each element in `tests`: + +1. If the `skipReason` field is present, skip this test completely. + +2. Create a MongoClient and call `client.admin.runCommand({killAllSessions: []})` to clean up any open transactions + from previous test failures. Ignore a command failure with error code 11601 ("Interrupted") to work around + [SERVER-38335](https://jira.mongodb.org/browse/SERVER-38335). + + - Running `killAllSessions` cleans up any open transactions from a previously failed test to prevent the current + test from blocking. It is sufficient to run this command once before starting the test suite and once after each + failed test. + - When testing against a sharded cluster run this command on ALL mongoses. + +3. Create a collection object from the MongoClient, using the `database_name` and `collection_name` fields of the YAML + file. + +4. Drop the test collection, using writeConcern "majority". + +5. Execute the "create" command to recreate the collection, using writeConcern "majority". (Creating the collection + inside a transaction is prohibited, so create it explicitly.) + +6. If the YAML file contains a `data` array, insert the documents in `data` into the test collection, using + writeConcern "majority". + +7. When testing against a sharded cluster run a `distinct` command on the newly created collection on all mongoses. For + an explanation see, + [Why do tests that run distinct sometimes fail with StaleDbVersion?](#why-do-tests-that-run-distinct-sometimes-fail-with-staledbversion) + +8. If `failPoint` is specified, its value is a configureFailPoint command. Run the command on the admin database to + enable the fail point. + +9. Create a **new** MongoClient `client`, with Command Monitoring listeners enabled. (Using a new MongoClient for each + test ensures a fresh session pool that hasn't executed any transactions previously, so the tests can assert actual + txnNumbers, starting from 1.) Pass this test's `clientOptions` if present. + + - When testing against a sharded cluster and `useMultipleMongoses` is `true` the client MUST be created with + multiple (valid) mongos seed addresses. + +10. Call `client.startSession` twice to create ClientSession objects `session0` and `session1`, using the test's + "sessionOptions" if they are present. Save their lsids so they are available after calling `endSession`, see + [Logical Session Id](#logical-session-id). + +11. For each element in `operations`: + + - If the operation `name` is a special test operation type, execute it and go to the next operation, otherwise + proceed to the next step. + + - Enter a "try" block or your programming language's closest equivalent. + + - Create a Database object from the MongoClient, using the `database_name` field at the top level of the test file. + + - Create a Collection object from the Database, using the `collection_name` field at the top level of the test file. + If `collectionOptions` or `databaseOptions` is present, create the Collection or Database object with the + provided options, respectively. Otherwise create the object with the default options. + + - Execute the named method on the provided `object`, passing the arguments listed. Pass `session0` or `session1` to + the method, depending on which session's name is in the arguments list. If `arguments` contains no "session", + pass no explicit session to the method. + + - If the driver throws an exception / returns an error while executing this series of operations, store the error + message and server error code. + + - If the operation's `error` field is `true`, verify that the method threw an exception or returned an error. + + - If the result document has an "errorContains" field, verify that the method threw an exception or returned an + error, and that the value of the "errorContains" field matches the error string. "errorContains" is a substring + (case-insensitive) of the actual error message. + + If the result document has an "errorCodeName" field, verify that the method threw a command failed exception or + returned an error, and that the value of the "errorCodeName" field matches the "codeName" in the server error + response. + + If the result document has an "errorLabelsContain" field, verify that the method threw an exception or returned an + error. Verify that all of the error labels in "errorLabelsContain" are present in the error or exception using + the `hasErrorLabel` method. + + If the result document has an "errorLabelsOmit" field, verify that the method threw an exception or returned an + error. Verify that none of the error labels in "errorLabelsOmit" are present in the error or exception using the + `hasErrorLabel` method. + + - If the operation returns a raw command response, eg from `runCommand`, then compare only the fields present in the + expected result document. Otherwise, compare the method's return value to `result` using the same logic as the + CRUD Spec Tests runner. + +12. Call `session0.endSession()` and `session1.endSession`. + +13. If the test includes a list of command-started events in `expectations`, compare them to the actual command-started + events using the same logic as the + [legacy Command Monitoring Spec Tests runner](../../command-logging-and-monitoring/tests/README.md), plus the + rules in the Command-Started Events instructions below. + +14. If `failPoint` is specified, disable the fail point to avoid spurious failures in subsequent tests. The fail point + may be disabled like so: + + ```javascript + db.adminCommand({ + configureFailPoint: "", + mode: "off" + }); + ``` + +15. For each element in `outcome`: + + - If `name` is "collection", verify that the test collection contains exactly the documents in the `data` array. + Ensure this find reads the latest data by using **primary read preference** with **local read concern** even + when the MongoClient is configured with another read preference or read concern. Note the server does not + guarantee that documents returned by a find command will be in inserted order. This find MUST sort by `{_id:1}`. + +### Special Test Operations + +Certain operations that appear in the "operations" array do not correspond to API methods but instead represent special +test operations. Such operations are defined on the "testRunner" object and documented here: + +#### targetedFailPoint + +The "targetedFailPoint" operation instructs the test runner to configure a fail point on a specific mongos. The mongos +to run the `configureFailPoint` is determined by the "session" argument (either "session0" or "session1"). The session +must already be pinned to a mongos server. The "failPoint" argument is the `configureFailPoint` command to run. + +If a test uses `targetedFailPoint`, disable the fail point after running all `operations` to avoid spurious failures in +subsequent tests. The fail point may be disabled like so: + +```javascript + db.adminCommand({ + configureFailPoint: "", + mode: "off" + }); +``` + +Here is an example which instructs the test runner to enable the failCommand fail point on the mongos server which +"session0" is pinned to: + +```yaml +# Enable the fail point only on the Mongos that session0 is pinned to. +- name: targetedFailPoint + object: testRunner + arguments: + session: session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + closeConnection: true +``` + +Tests that use the "targetedFailPoint" operation do not include `configureFailPoint` commands in their command +expectations. Drivers MUST ensure that `configureFailPoint` commands do not appear in the list of logged commands, +either by manually filtering it from the list of observed commands or by using a different MongoClient to execute +`configureFailPoint`. + +#### assertSessionTransactionState + +The "assertSessionTransactionState" operation instructs the test runner to assert that the transaction state of the +given session is equal to the specified value. The possible values are as follows: `none`, `starting`, `in_progress`, +`committed`, `aborted`: + +```yaml +- name: assertSessionTransactionState + object: testRunner + arguments: + session: session0 + state: in_progress +``` + +#### assertSessionPinned + +The "assertSessionPinned" operation instructs the test runner to assert that the given session is pinned to a mongos: + +```yaml +- name: assertSessionPinned + object: testRunner + arguments: + session: session0 +``` + +#### assertSessionUnpinned + +The "assertSessionUnpinned" operation instructs the test runner to assert that the given session is not pinned to a +mongos: + +```yaml +- name: assertSessionPinned + object: testRunner + arguments: + session: session0 +``` + +#### assertCollectionExists + +The "assertCollectionExists" operation instructs the test runner to assert that the given collection exists in the +database: + +```yaml +- name: assertCollectionExists + object: testRunner + arguments: + database: db + collection: test +``` + +Use a `listCollections` command to check whether the collection exists. Note that it is currently not possible to run +`listCollections` from within a transaction. + +#### assertCollectionNotExists + +The "assertCollectionNotExists" operation instructs the test runner to assert that the given collection does not exist +in the database: + +```yaml +- name: assertCollectionNotExists + object: testRunner + arguments: + database: db + collection: test +``` + +Use a `listCollections` command to check whether the collection exists. Note that it is currently not possible to run +`listCollections` from within a transaction. + +#### assertIndexExists + +The "assertIndexExists" operation instructs the test runner to assert that the index with the given name exists on the +collection: + +```yaml +- name: assertIndexExists + object: testRunner + arguments: + database: db + collection: test + index: t_1 +``` + +Use a `listIndexes` command to check whether the index exists. Note that it is currently not possible to run +`listIndexes` from within a transaction. + +#### assertIndexNotExists + +The "assertIndexNotExists" operation instructs the test runner to assert that the index with the given name does not +exist on the collection: + +```yaml +- name: assertIndexNotExists + object: testRunner + arguments: + database: db + collection: test + index: t_1 +``` + +Use a `listIndexes` command to check whether the index exists. Note that it is currently not possible to run +`listIndexes` from within a transaction. + +### Command-Started Events + +The event listener used for these tests MUST ignore the security commands listed in the Command Monitoring Spec. + +#### Logical Session Id + +Each command-started event in `expectations` includes an `lsid` with the value "session0" or "session1". Tests MUST +assert that the command's actual `lsid` matches the id of the correct ClientSession named `session0` or `session1`. + +#### Null Values + +Some command-started events in `expectations` include `null` values for top level `command` fields such as `txnNumber`, +`autocommit`, and `writeConcern`. Tests MUST assert that the actual command **omits** any field that has a `null` value +in the expected command. + +#### Cursor Id + +A `getMore` value of `"42"` in a command-started event is a fake cursorId that MUST be ignored. (In the Command +Monitoring Spec tests, fake cursorIds are correlated with real ones, but that is not necessary for Transactions Spec +tests.) + +#### afterClusterTime + +A `readConcern.afterClusterTime` value of `42` in a command-started event is a fake cluster time. Drivers MUST assert +that the actual command includes an afterClusterTime. + +#### recoveryToken + +A `recoveryToken` value of `42` in a command-started event is a placeholder for an arbitrary recovery token. Drivers +MUST assert that the actual command includes a "recoveryToken" field and SHOULD assert that field is a BSON document. + +## Q & A + +### Why do tests that run distinct sometimes fail with StaleDbVersion? + +When a shard receives its first command that contains a dbVersion, the shard returns a StaleDbVersion error and the +Mongos retries the operation. In a sharded transaction, Mongos does not retry these operations and instead returns the +error to the client. For example: + +```text +Command distinct failed: Transaction aa09e296-472a-494f-8334-48d57ab530b6:1 was aborted on statement 0 due to: an error from cluster data placement change :: caused by :: got stale databaseVersion response from shard sh01 at host localhost:27217 :: caused by :: don't know dbVersion. +``` + +To workaround this limitation, a driver test runner MUST run a non-transactional `distinct` command on each Mongos +before running any test that uses `distinct`. To ease the implementation drivers can simply run `distinct` before +*every* test. + +Note that drivers can remove this workaround once [SERVER-39704](https://jira.mongodb.org/browse/SERVER-39704) is +resolved so that mongos retries this operation transparently. The `distinct` command is the only command allowed in a +sharded transaction that uses the `dbVersion` concept so it is the only command affected. + +## Changelog + +- 2024-02-15: Migrated from reStructuredText to Markdown. + +- 2024-02-07: Moved legacy test format docs to this file from README.md. + +- 2023-09-28: Add `load-balanced` to test topology requirements. + +- 2022-04-22: Clarifications to `serverless` and `useMultipleMongoses`. + +- 2019-05-15: Add operation level `error` field to assert any error. + +- 2019-03-25: Add workaround for StaleDbVersion on distinct. + +- 2019-03-01: Add top-level `runOn` field to denote server version and/or topology requirements requirements for the + test file. Removes the `topology` top-level field, which is now expressed within `runOn` elements. + +- 2019-02-28: `useMultipleMongoses: true` and non-targeted fail points are mutually exclusive. + +- 2019-02-13: Modify test format for 4.2 sharded transactions, including "useMultipleMongoses", `object: testRunner`, + the `targetedFailPoint` operation, and recoveryToken assertions. diff --git a/source/transactions/tests/read-concern.json b/source/transactions/tests/read-concern.json deleted file mode 100644 index c85df9c933..0000000000 --- a/source/transactions/tests/read-concern.json +++ /dev/null @@ -1,837 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "tests": [ - { - "description": "only first countDocuments includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": null, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": { - "level": "snapshot" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": null, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first find includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": { - "level": "snapshot" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first aggregate includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": { - "level": "snapshot" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first distinct includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": { - "level": "snapshot" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first runCommand includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": { - "level": "snapshot" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/read-concern.yml b/source/transactions/tests/read-concern.yml deleted file mode 100644 index 1dcebf7581..0000000000 --- a/source/transactions/tests/read-concern.yml +++ /dev/null @@ -1,345 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: &data - - {_id: 1} - - {_id: 2} - - {_id: 3} - - {_id: 4} - -tests: - - description: only first countDocuments includes readConcern - - operations: - - &startTransaction - name: startTransaction - object: session0 - arguments: - options: - readConcern: - level: snapshot - - &countDocuments - name: countDocuments - object: collection - collectionOptions: - readConcern: - level: majority - arguments: - session: session0 - filter: {_id: {$gte: 2}} - result: 3 - - *countDocuments - - &commitTransaction - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $match: {_id: {$gte: 2}} - - $group: {_id: null, n: {$sum: 1}} - cursor: {} - lsid: session0 - readConcern: - level: snapshot - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $match: {_id: {$gte: 2}} - - $group: {_id: null, n: {$sum: 1}} - cursor: {} - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: aggregate - database_name: *database_name - - &commitTransactionEvent - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - readConcern: - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: &outcome - collection: - data: - *data - - - description: only first find includes readConcern - - operations: - - *startTransaction - - &find - name: find - object: collection - collectionOptions: - readConcern: - level: majority - arguments: - session: session0 - batchSize: 3 - result: *data - - *find - - *commitTransaction - - expectations: - - command_started_event: - command: - find: *collection_name - batchSize: 3 - lsid: session0 - readConcern: - level: snapshot - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - command_name: find - database_name: *database_name - - command_started_event: - command: - getMore: - # 42 is a fake placeholder value for the cursorId. - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - find: *collection_name - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: find - database_name: *database_name - - command_started_event: - command: - getMore: - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - *commitTransactionEvent - - outcome: &outcome - collection: - data: - *data - - - description: only first aggregate includes readConcern - - operations: - - *startTransaction - - &aggregate - name: aggregate - object: collection - collectionOptions: - readConcern: - level: majority - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: *data - - *aggregate - - *commitTransaction - - expectations: - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $project: - _id: 1 - cursor: - batchSize: 3 - lsid: session0 - readConcern: - level: snapshot - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - getMore: - # 42 is a fake placeholder value for the cursorId. - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $project: - _id: 1 - cursor: - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - getMore: - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - *commitTransactionEvent - - outcome: *outcome - - - description: only first distinct includes readConcern - - operations: - - *startTransaction - - &distinct - name: distinct - object: collection - collectionOptions: - readConcern: - level: majority - arguments: - session: session0 - fieldName: _id - result: [1, 2, 3, 4] - - *distinct - - *commitTransaction - - expectations: - - command_started_event: - command: - distinct: *collection_name - key: _id - lsid: session0 - readConcern: - level: snapshot - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: distinct - database_name: *database_name - - command_started_event: - command: - distinct: *collection_name - key: _id - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: distinct - database_name: *database_name - - *commitTransactionEvent - - outcome: *outcome - - - description: only first runCommand includes readConcern - - operations: - - *startTransaction - - &runCommand - name: runCommand - object: database - command_name: find - arguments: - session: session0 - command: - find: *collection_name - - *runCommand - - *commitTransaction - - expectations: - - command_started_event: - command: - find: *collection_name - lsid: session0 - readConcern: - level: snapshot - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: find - database_name: *database_name - - command_started_event: - command: - find: *collection_name - lsid: session0 - readConcern: # No readConcern - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: find - database_name: *database_name - - *commitTransactionEvent - - outcome: *outcome diff --git a/source/transactions/tests/read-pref.json b/source/transactions/tests/read-pref.json deleted file mode 100644 index 03a7eca177..0000000000 --- a/source/transactions/tests/read-pref.json +++ /dev/null @@ -1,706 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "default readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": [ - { - "count": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "primary readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": [ - { - "count": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "secondary readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "primaryPreferred readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "PrimaryPreferred" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "nearest readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Nearest" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "secondary write only", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/read-pref.yml b/source/transactions/tests/read-pref.yml deleted file mode 100644 index de1b41f635..0000000000 --- a/source/transactions/tests/read-pref.yml +++ /dev/null @@ -1,340 +0,0 @@ -# This test doesn't check contents of command-started events. -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: default readPreference - - operations: - - name: startTransaction - object: session0 - - name: insertMany - object: collection - arguments: - documents: &insertedDocs - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - session: session0 - result: - insertedIds: {0: 1, 1: 2, 2: 3, 3: 4} - - name: aggregate - object: collection - collectionOptions: - # The driver overrides the collection's read pref with the - # transaction's so count runs with Primary and succeeds. - readPreference: - mode: Secondary - arguments: - session: session0 - pipeline: - - $match: - _id: 1 - - $count: count - result: - - count: 1 - - name: find - object: collection - collectionOptions: - readPreference: - mode: Secondary - arguments: - session: session0 - batchSize: 3 - result: *insertedDocs - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Secondary - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: *insertedDocs - - name: commitTransaction - object: session0 - - outcome: - collection: - data: *insertedDocs - - - description: primary readPreference - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Primary - - name: insertMany - object: collection - arguments: - documents: &insertedDocs - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - session: session0 - result: - insertedIds: {0: 1, 1: 2, 2: 3, 3: 4} - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Secondary - arguments: - session: session0 - pipeline: - - $match: - _id: 1 - - $count: count - result: - - count: 1 - - name: find - object: collection - collectionOptions: - readPreference: - mode: Secondary - arguments: - session: session0 - batchSize: 3 - result: *insertedDocs - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Secondary - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: *insertedDocs - - name: commitTransaction - object: session0 - - outcome: - collection: - data: *insertedDocs - - - description: secondary readPreference - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Secondary - - name: insertMany - object: collection - arguments: - documents: &insertedDocs - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - session: session0 - result: - insertedIds: {0: 1, 1: 2, 2: 3, 3: 4} - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - pipeline: - - $match: - _id: 1 - - $count: count - result: - errorContains: read preference in a transaction must be primary - - name: find - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - batchSize: 3 - result: - errorContains: read preference in a transaction must be primary - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: - errorContains: read preference in a transaction must be primary - - name: abortTransaction - object: session0 - - outcome: - collection: - data: [] - - - description: primaryPreferred readPreference - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: PrimaryPreferred - - name: insertMany - object: collection - arguments: - documents: &insertedDocs - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - session: session0 - result: - insertedIds: {0: 1, 1: 2, 2: 3, 3: 4} - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - pipeline: - - $match: - _id: 1 - - $count: count - result: - errorContains: read preference in a transaction must be primary - - name: find - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - batchSize: 3 - result: - errorContains: read preference in a transaction must be primary - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: - errorContains: read preference in a transaction must be primary - - name: abortTransaction - object: session0 - - outcome: - collection: - data: [] - - - description: nearest readPreference - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Nearest - - name: insertMany - object: collection - arguments: - documents: &insertedDocs - - _id: 1 - - _id: 2 - - _id: 3 - - _id: 4 - session: session0 - result: - insertedIds: {0: 1, 1: 2, 2: 3, 3: 4} - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - pipeline: - - $match: - _id: 1 - - $count: count - result: - errorContains: read preference in a transaction must be primary - - name: find - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - session: session0 - batchSize: 3 - result: - errorContains: read preference in a transaction must be primary - - name: aggregate - object: collection - collectionOptions: - readPreference: - mode: Primary - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: - errorContains: read preference in a transaction must be primary - - name: abortTransaction - object: session0 - - outcome: - collection: - data: [] - - - description: secondary write only - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Secondary - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - outcome: - collection: - data: - - _id: 1 diff --git a/source/transactions/tests/reads.json b/source/transactions/tests/reads.json deleted file mode 100644 index 0b7cf836b1..0000000000 --- a/source/transactions/tests/reads.json +++ /dev/null @@ -1,615 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "tests": [ - { - "description": "collection readConcern without transaction", - "operations": [ - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "readConcern": { - "level": "majority" - }, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "count", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "count", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "Cannot run 'count' in a multi-document transaction", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "test", - "query": { - "_id": 1 - }, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "count", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "find", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "aggregate", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "distinct", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/reads.yml b/source/transactions/tests/reads.yml deleted file mode 100644 index 4ebc4dfa25..0000000000 --- a/source/transactions/tests/reads.yml +++ /dev/null @@ -1,298 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: &data - - {_id: 1} - - {_id: 2} - - {_id: 3} - - {_id: 4} - -tests: - - description: collection readConcern without transaction - - operations: - - name: find - object: collection - collectionOptions: - readConcern: - level: majority - arguments: - session: session0 - result: *data - - expectations: - - command_started_event: - command: - find: *collection_name - readConcern: - level: majority - lsid: session0 - txnNumber: - startTransaction: - autocommit: - command_name: find - database_name: *database_name - - outcome: &outcome - collection: - data: - *data - - - description: count - - operations: - - &startTransaction - name: startTransaction - object: session0 - - name: count - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - errorContains: "Cannot run 'count' in a multi-document transaction" - errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - count: *collection_name - query: - _id: 1 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: count - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: find - - operations: - - *startTransaction - - &find - name: find - object: collection - arguments: - session: session0 - batchSize: 3 - result: *data - - *find - - &commitTransaction - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - find: *collection_name - batchSize: 3 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - command_name: find - database_name: *database_name - - command_started_event: - command: - getMore: - # 42 is a fake placeholder value for the cursorId. - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - find: *collection_name - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: find - database_name: *database_name - - command_started_event: - command: - getMore: - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: *outcome - - - description: aggregate - - operations: - - *startTransaction - - &aggregate - name: aggregate - object: collection - arguments: - pipeline: - - $project: - _id: 1 - batchSize: 3 - session: session0 - result: *data - - *aggregate - - *commitTransaction - - expectations: - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $project: - _id: 1 - cursor: - batchSize: 3 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - getMore: - # 42 is a fake placeholder value for the cursorId. - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - aggregate: *collection_name - pipeline: - - $project: - _id: 1 - cursor: - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: aggregate - database_name: *database_name - - command_started_event: - command: - getMore: - $numberLong: '42' - collection: *collection_name - batchSize: 3 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - command_name: getMore - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: *outcome - - - description: distinct - - operations: - - *startTransaction - - name: distinct - object: collection - arguments: - session: session0 - fieldName: _id - result: [1, 2, 3, 4] - - *commitTransaction - - expectations: - - command_started_event: - command: - distinct: *collection_name - key: _id - lsid: session0 - readConcern: - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: distinct - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - readConcern: - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: *outcome diff --git a/source/transactions/tests/retryable-abort.json b/source/transactions/tests/retryable-abort.json deleted file mode 100644 index d2ab05dc93..0000000000 --- a/source/transactions/tests/retryable-abort.json +++ /dev/null @@ -1,1958 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "abortTransaction only performs a single retry", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction does not retry after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction does not retry after WriteConcernError Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after connection error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotMaster", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 10107, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotMasterOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 13436, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotMasterNoSlaveOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 13435, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11602, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11600, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 189, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 91, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 7, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 6, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 9001, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 89, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/source/transactions/tests/retryable-abort.yml b/source/transactions/tests/retryable-abort.yml deleted file mode 100644 index 3dd5320909..0000000000 --- a/source/transactions/tests/retryable-abort.yml +++ /dev/null @@ -1,1292 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: abortTransaction only performs a single retry - - clientOptions: - retryWrites: false - - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["abortTransaction"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - # Call to abort returns no error even when the retry attempt fails. - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction does not retry after Interrupted - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 11601 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction does not retry after WriteConcernError Interrupted - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - writeConcernError: - code: 11601 - errmsg: operation was interrupted - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after connection error - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after NotMaster - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 10107 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after NotMasterOrSecondary - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 13436 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after NotMasterNoSlaveOk - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 13435 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after InterruptedDueToReplStateChange - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 11602 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after InterruptedAtShutdown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 11600 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after PrimarySteppedDown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 189 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after ShutdownInProgress - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 91 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after HostNotFound - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 7 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after HostUnreachable - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 6 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after SocketException - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 9001 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after NetworkTimeout - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - errorCode: 89 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after WriteConcernError InterruptedAtShutdown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - writeConcernError: - code: 11600 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - writeConcernError: - code: 11602 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after WriteConcernError PrimarySteppedDown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - writeConcernError: - code: 189 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abortTransaction succeeds after WriteConcernError ShutdownInProgress - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["abortTransaction"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] diff --git a/source/transactions/tests/retryable-commit.json b/source/transactions/tests/retryable-commit.json deleted file mode 100644 index a4a6850cbf..0000000000 --- a/source/transactions/tests/retryable-commit.json +++ /dev/null @@ -1,2069 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction fails after two errors", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction fails after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorCodeName": "Interrupted", - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commitTransaction fails after WriteConcernError Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after connection error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotMaster", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 10107, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotMasterOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 13436, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotMasterNoSlaveOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 13435, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11602, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11600, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 189, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 91, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 7, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 6, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 9001, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 89, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/retryable-commit.yml b/source/transactions/tests/retryable-commit.yml deleted file mode 100644 index d0354ddb61..0000000000 --- a/source/transactions/tests/retryable-commit.yml +++ /dev/null @@ -1,1332 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: commitTransaction fails after two errors - - clientOptions: - retryWrites: false - - failPoint: - configureFailPoint: failCommand - mode: { times: 2 } - data: - failCommands: ["commitTransaction"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - # First call to commit fails after a single retry attempt. - - name: commitTransaction - object: session0 - result: - errorLabelsContain: ["UnknownTransactionCommitResult"] - errorLabelsOmit: ["TransientTransactionError"] - # Second call to commit succeeds because the failpoint was disabled. - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction fails after Interrupted - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 11601 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorCodeName: Interrupted - errorLabelsOmit: ["TransientTransactionError"] - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: commitTransaction fails after WriteConcernError Interrupted - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 11601 - errmsg: operation was interrupted - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - result: - errorLabelsOmit: ["TransientTransactionError"] - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after connection error - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after NotMaster - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 10107 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after NotMasterOrSecondary - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 13436 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after NotMasterNoSlaveOk - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 13435 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after InterruptedDueToReplStateChange - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 11602 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after InterruptedAtShutdown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 11600 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after PrimarySteppedDown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 189 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after ShutdownInProgress - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 91 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after HostNotFound - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 7 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after HostUnreachable - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 6 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after SocketException - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 9001 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after NetworkTimeout - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - errorCode: 89 - closeConnection: false - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after WriteConcernError InterruptedAtShutdown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 11600 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 11602 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after WriteConcernError PrimarySteppedDown - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 189 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commitTransaction succeeds after WriteConcernError ShutdownInProgress - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["commitTransaction"] - writeConcernError: - code: 91 - errmsg: Replication is being shut down - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 diff --git a/source/transactions/tests/retryable-writes.json b/source/transactions/tests/retryable-writes.json deleted file mode 100644 index e568de5c0e..0000000000 --- a/source/transactions/tests/retryable-writes.json +++ /dev/null @@ -1,329 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "increment txnNumber", - "clientOptions": { - "retryWrites": true - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 4, - "1": 5 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "4" - }, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - }, - { - "description": "writes are not retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/source/transactions/tests/retryable-writes.yml b/source/transactions/tests/retryable-writes.yml deleted file mode 100644 index e60d04c3b1..0000000000 --- a/source/transactions/tests/retryable-writes.yml +++ /dev/null @@ -1,208 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: increment txnNumber - - clientOptions: - retryWrites: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - # Retryable write should include the next txnNumber - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - # Next transaction should include the next txnNumber - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 3 - result: - insertedId: 3 - - name: abortTransaction - object: session0 - # Retryable write should include the next txnNumber - - name: insertMany - object: collection - arguments: - documents: - - _id: 4 - - _id: 5 - session: session0 - result: - insertedIds: {0: 4, 1: 5} - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 3 - ordered: true - readConcern: - afterClusterTime: 42 - lsid: session0 - txnNumber: - $numberLong: "3" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "3" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 4 - - _id: 5 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "4" - startTransaction: - autocommit: - writeConcern: - command_name: insert - database_name: *database_name - - outcome: - collection: - data: - - _id: 1 - - _id: 2 - - _id: 4 - - _id: 5 - - - description: writes are not retried - - clientOptions: - retryWrites: true - - failPoint: - configureFailPoint: failCommand - mode: { times: 1 } - data: - failCommands: ["insert"] - closeConnection: true - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - errorLabelsContain: ["TransientTransactionError"] - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] diff --git a/source/transactions/tests/run-command.json b/source/transactions/tests/run-command.json deleted file mode 100644 index e05e5cb39e..0000000000 --- a/source/transactions/tests/run-command.json +++ /dev/null @@ -1,292 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "run command with default read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command with secondary read preference in client option and primary read preference in transaction options", - "clientOptions": { - "readPreference": "secondary" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command with explicit primary read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - }, - "readPreference": { - "mode": "Primary" - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command fails with explicit secondary read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - }, - "readPreference": { - "mode": "Secondary" - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - } - ] - }, - { - "description": "run command fails with secondary read preference from transaction options", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/run-command.yml b/source/transactions/tests/run-command.yml deleted file mode 100644 index 16a633a73e..0000000000 --- a/source/transactions/tests/run-command.yml +++ /dev/null @@ -1,189 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: run command with default read preference - - operations: - - name: startTransaction - object: session0 - - name: runCommand - object: database - command_name: insert - arguments: - session: session0 - command: - insert: *collection_name - documents: - - _id : 1 - result: - n: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id : 1 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - - description: run command with secondary read preference in client option and primary read preference in transaction options - - clientOptions: - readPreference: secondary - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Primary - - name: runCommand - object: database - command_name: insert - arguments: - session: session0 - command: - insert: *collection_name - documents: - - _id : 1 - result: - n: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id : 1 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - - description: run command with explicit primary read preference - - operations: - - name: startTransaction - object: session0 - - name: runCommand - object: database - command_name: insert - arguments: - session: session0 - command: - insert: *collection_name - documents: - - _id : 1 - readPreference: - mode: Primary - result: - n: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id : 1 - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - - description: run command fails with explicit secondary read preference - - operations: - - name: startTransaction - object: session0 - - name: runCommand - object: database - command_name: find - arguments: - session: session0 - command: - find: *collection_name - readPreference: - mode: Secondary - result: - errorContains: read preference in a transaction must be primary - - - description: run command fails with secondary read preference from transaction options - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Secondary - - name: runCommand - object: database - command_name: find - arguments: - session: session0 - command: - find: *collection_name - result: - errorContains: read preference in a transaction must be primary - diff --git a/source/transactions/tests/transaction-options.json b/source/transactions/tests/transaction-options.json deleted file mode 100644 index 0c134e5f65..0000000000 --- a/source/transactions/tests/transaction-options.json +++ /dev/null @@ -1,1535 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "no transaction options set", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction options inherited from client", - "clientOptions": { - "w": 1, - "readConcernLevel": "local" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction options inherited from defaultTransactionOptions", - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": 1 - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "startTransaction options override defaults", - "clientOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "defaultTransactionOptions override client options", - "clientOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readConcern local in defaultTransactionOptions", - "clientOptions": { - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "local" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readConcern local in startTransaction options", - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "client writeConcern ignored for bulk", - "clientOptions": { - "w": "majority" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 1 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "session": "session0" - }, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 1 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readPreference inherited from client", - "clientOptions": { - "readPreference": "secondary" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readPreference inherited from defaultTransactionOptions", - "clientOptions": { - "readPreference": "primary" - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "startTransaction overrides readPreference", - "clientOptions": { - "readPreference": "primary" - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/source/transactions/tests/transaction-options.yml b/source/transactions/tests/transaction-options.yml deleted file mode 100644 index 83d07ad1cf..0000000000 --- a/source/transactions/tests/transaction-options.yml +++ /dev/null @@ -1,878 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: no transaction options set - - operations: &commitAbortOperations - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - # Now test abort. - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: &outcome - collection: - data: - - _id: 1 - - - description: transaction options inherited from client - - clientOptions: - w: 1 - readConcernLevel: local - - operations: *commitAbortOperations - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: local - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: local - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: transaction options inherited from defaultTransactionOptions - - sessionOptions: - session0: - defaultTransactionOptions: - readConcern: - level: snapshot - writeConcern: - w: 1 - - operations: *commitAbortOperations - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: startTransaction options override defaults - - clientOptions: - readConcernLevel: local - w: 1 - - sessionOptions: - session0: - defaultTransactionOptions: - readConcern: - level: majority - writeConcern: - w: 1 - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readConcern: - level: snapshot - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - name: startTransaction - object: session0 - arguments: - options: - readConcern: - level: snapshot - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: defaultTransactionOptions override client options - - clientOptions: - readConcernLevel: local - w: 1 - - sessionOptions: - session0: - defaultTransactionOptions: - readConcern: - level: snapshot - writeConcern: - w: majority - - operations: *commitAbortOperations - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: readConcern local in defaultTransactionOptions - - clientOptions: - w: 1 - - sessionOptions: - session0: - defaultTransactionOptions: - readConcern: - level: local - - operations: *commitAbortOperations - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: local - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: local - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - w: 1 - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: readConcern local in startTransaction options - - sessionOptions: - session0: - defaultTransactionOptions: - readConcern: - level: majority # Overridden. - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readConcern: - level: snapshot - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - # Now test abort. - - name: startTransaction - object: session0 - arguments: - options: - readConcern: - level: snapshot - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 2 - result: - insertedId: 2 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 2 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: true - autocommit: false - readConcern: - level: snapshot - afterClusterTime: 42 - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "2" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: *outcome - - - description: client writeConcern ignored for bulk - - clientOptions: - w: majority - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: 1 - - name: bulkWrite - object: collection - arguments: - requests: - - name: insertOne - arguments: - document: {_id: 1} - session: session0 - result: - deletedCount: 0 - insertedCount: 1 - insertedIds: {0: 1} - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 0 - upsertedIds: {} - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - # No writeConcern. - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: 1 - command_name: commitTransaction - database_name: admin - - outcome: *outcome - - - description: readPreference inherited from client - - clientOptions: - readPreference: secondary - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: find - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - errorContains: read preference in a transaction must be primary - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: readPreference inherited from defaultTransactionOptions - - clientOptions: - readPreference: primary - - sessionOptions: - session0: - defaultTransactionOptions: - readPreference: - mode: Secondary - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: find - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - errorContains: read preference in a transaction must be primary - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: startTransaction overrides readPreference - - clientOptions: - readPreference: primary - - sessionOptions: - session0: - defaultTransactionOptions: - readPreference: - mode: Primary - - operations: - - name: startTransaction - object: session0 - arguments: - options: - readPreference: - mode: Secondary - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: find - object: collection - arguments: - session: session0 - filter: - _id: 1 - result: - errorContains: read preference in a transaction must be primary - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - readConcern: - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - readConcern: - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 diff --git a/source/transactions/tests/unified/abort.json b/source/transactions/tests/unified/abort.json new file mode 100644 index 0000000000..c151a7d0c6 --- /dev/null +++ b/source/transactions/tests/unified/abort.json @@ -0,0 +1,828 @@ +{ + "description": "abort", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "abort", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "implicit abort", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "endSession" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "two aborts", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "session0", + "name": "abortTransaction", + "expectError": { + "errorContains": "cannot call abortTransaction twice" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abort without start", + "operations": [ + { + "object": "session0", + "name": "abortTransaction", + "expectError": { + "errorContains": "no transaction started" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abort directly after no-op commit", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "abortTransaction", + "expectError": { + "errorContains": "Cannot call abortTransaction after calling commitTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abort directly after commit", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "abortTransaction", + "expectError": { + "errorContains": "Cannot call abortTransaction after calling commitTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "abort ignores TransactionAborted", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ], + "errorContains": "E11000" + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorCodeName": "NoSuchTransaction", + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abort does not apply writeConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 10 + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/abort.yml b/source/transactions/tests/unified/abort.yml new file mode 100644 index 0000000000..194846c92f --- /dev/null +++ b/source/transactions/tests/unified/abort.yml @@ -0,0 +1,483 @@ +description: abort + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: abort + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'implicit abort' + operations: + # Start a transaction but don't commit - the driver calls abortTransaction from ClientSession.endSession() + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # The original legacy test relied on the test runner to call endSession after all operations. Since the unified + # test runner has no such behavior, we manually call endSession as the last operation. + - + object: *session0 + name: endSession + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'two aborts' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + - + object: *session0 + name: abortTransaction + expectError: + errorContains: 'cannot call abortTransaction twice' + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abort without start' + operations: + - + object: *session0 + name: abortTransaction + expectError: + errorContains: 'no transaction started' + expectEvents: + - + client: *client0 + events: [] + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abort directly after no-op commit' + operations: + - + object: *session0 + name: startTransaction + - + object: *session0 + name: commitTransaction + # Error calling abort after no-op commit + - + object: *session0 + name: abortTransaction + expectError: + errorContains: 'Cannot call abortTransaction after calling commitTransaction' + expectEvents: + - + client: *client0 + events: [] + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abort directly after commit' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: abortTransaction + expectError: + errorContains: 'Cannot call abortTransaction after calling commitTransaction' + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'abort ignores TransactionAborted' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # Abort the server transaction with a duplicate key error + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + errorLabelsOmit: + - TransientTransactionError + - UnknownTransactionCommitResult + # DuplicateKey error code included in the bulk write error message returned by the server + errorContains: E11000 + # Make sure the server aborted the transaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + errorCodeName: NoSuchTransaction + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + # abortTransaction must ignore the TransactionAborted and succeed + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abort does not apply writeConcern' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: 10 + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + # No write concern error + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/backpressure-retryable-abort.json b/source/transactions/tests/unified/backpressure-retryable-abort.json new file mode 100644 index 0000000000..3a2a3b4368 --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-abort.json @@ -0,0 +1,342 @@ +{ + "description": "backpressure-retryable-abort", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "abortTransaction retries if backpressure labels are added", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction is retried maxAttempts=2 times if backpressure labels are added", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/backpressure-retryable-abort.yml b/source/transactions/tests/unified/backpressure-retryable-abort.yml new file mode 100644 index 0000000000..bd8180dd1f --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-abort.yml @@ -0,0 +1,207 @@ +description: backpressure-retryable-abort +schemaVersion: "1.3" +runOnRequirements: + - minServerVersion: "4.4" + topologies: + - replicaset + - sharded + - load-balanced +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] +tests: + - description: abortTransaction retries if backpressure labels are added + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: abortTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: [] + - description: abortTransaction is retried maxAttempts=2 times if backpressure labels are added + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: abortTransaction + databaseName: admin + - commandStartedEvent: + commandName: abortTransaction + - commandStartedEvent: + commandName: abortTransaction + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/backpressure-retryable-commit.json b/source/transactions/tests/unified/backpressure-retryable-commit.json new file mode 100644 index 0000000000..844ed25ab4 --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-commit.json @@ -0,0 +1,359 @@ +{ + "description": "backpressure-retryable-commit", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "sharded", + "replicaset", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction retries if backpressure labels are added", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction is retried maxAttempts=2 times if backpressure labels are added", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/backpressure-retryable-commit.yml b/source/transactions/tests/unified/backpressure-retryable-commit.yml new file mode 100644 index 0000000000..8ee1b96c7f --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-commit.yml @@ -0,0 +1,214 @@ +description: backpressure-retryable-commit +schemaVersion: "1.4" +runOnRequirements: + - minServerVersion: "4.4" + topologies: + - sharded + - replicaset + - load-balanced +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] +tests: + - description: commitTransaction retries if backpressure labels are added + runOnRequirements: + - serverless: forbid + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - _id: 1 + - description: commitTransaction is retried maxAttempts=2 times if backpressure labels are added + runOnRequirements: + - serverless: forbid + operations: + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: *session0 + name: commitTransaction + expectError: + isError: true + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + - commandStartedEvent: + commandName: commitTransaction + - commandStartedEvent: + commandName: commitTransaction + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/backpressure-retryable-reads.json b/source/transactions/tests/unified/backpressure-retryable-reads.json new file mode 100644 index 0000000000..a859ec4bda --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-reads.json @@ -0,0 +1,313 @@ +{ + "description": "backpressure-retryable-reads", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "reads are retried if backpressure labels are added", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "session": "session0" + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "reads are retried maxAttempts=2 times if backpressure labels are added", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "find" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectError": { + "isError": true + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/backpressure-retryable-reads.yml b/source/transactions/tests/unified/backpressure-retryable-reads.yml new file mode 100644 index 0000000000..7edc41fa47 --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-reads.yml @@ -0,0 +1,186 @@ +description: backpressure-retryable-reads +schemaVersion: "1.3" +runOnRequirements: + - minServerVersion: "4.4" + topologies: + - replicaset + - sharded + - load-balanced +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] +tests: + - description: reads are retried if backpressure labels are added + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - find + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *collection0 + name: find + arguments: + filter: {} + session: *session0 + - object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + find: test + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + autocommit: false + writeConcern: + $$exists: false + commandName: find + databaseName: *database_name + - commandStartedEvent: + command: + find: test + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + autocommit: false + writeConcern: + $$exists: false + commandName: find + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + - description: reads are retried maxAttempts=2 times if backpressure labels are added + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - find + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *collection0 + name: find + arguments: + filter: {} + session: *session0 + expectError: + isError: true + - object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandStartedEvent: + commandName: find + - commandStartedEvent: + commandName: abortTransaction diff --git a/source/transactions/tests/unified/backpressure-retryable-writes.json b/source/transactions/tests/unified/backpressure-retryable-writes.json new file mode 100644 index 0000000000..6cbf450e5f --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-writes.json @@ -0,0 +1,439 @@ +{ + "description": "backpressure-retryable-writes", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "writes are retried if backpressure labels are added", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "writes are retried maxAttempts=2 times if backpressure labels are added", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectError": { + "isError": true + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "insert" + } + }, + { + "commandStartedEvent": { + "commandName": "abortTransaction" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "retry succeeds if backpressure labels are added to the first operation in a transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableError", + "SystemOverloadedError" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "startTransaction": true + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "startTransaction": true + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "startTransaction": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/backpressure-retryable-writes.yml b/source/transactions/tests/unified/backpressure-retryable-writes.yml new file mode 100644 index 0000000000..68c86cff2b --- /dev/null +++ b/source/transactions/tests/unified/backpressure-retryable-writes.yml @@ -0,0 +1,254 @@ +description: backpressure-retryable-writes +schemaVersion: "1.3" +runOnRequirements: + - minServerVersion: "4.4" + topologies: + - replicaset + - sharded + - load-balanced +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] +tests: + - description: writes are retried if backpressure labels are added + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 2 + - object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: test + documents: + - _id: 1 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: true + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: test + documents: + - _id: 2 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: test + documents: + - _id: 2 + ordered: true + readConcern: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + autocommit: false + writeConcern: + $$exists: false + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: + $$exists: false + lsid: + $$sessionLsid: *session0 + txnNumber: + $numberLong: "1" + startTransaction: + $$exists: false + autocommit: false + writeConcern: + $$exists: false + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - description: writes are retried maxAttempts=2 times if backpressure labels are added + operations: + - object: *session0 + name: startTransaction + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 1 + expectResult: + $$unsetOrMatches: + insertedId: + $$unsetOrMatches: 1 + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - insert + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 2 + expectError: + isError: true + - object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: insert + - commandStartedEvent: + commandName: abortTransaction + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: [] + - description: retry succeeds if backpressure labels are added to the first operation in a transaction + operations: + - object: *session0 + name: startTransaction + - object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + errorLabels: + - RetryableError + - SystemOverloadedError + errorCode: 112 + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: + _id: 2 + - object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + startTransaction: true + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + startTransaction: true + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + startTransaction: + $$exists: false + commandName: abortTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/bulk.json b/source/transactions/tests/unified/bulk.json new file mode 100644 index 0000000000..ece162518f --- /dev/null +++ b/source/transactions/tests/unified/bulk.json @@ -0,0 +1,652 @@ +{ + "description": "bulk", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "bulk", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": 2 + } + }, + "upsert": true + } + }, + { + "insertOne": { + "document": { + "_id": 3 + } + } + }, + { + "insertOne": { + "document": { + "_id": 4 + } + } + }, + { + "insertOne": { + "document": { + "_id": 5 + } + } + }, + { + "insertOne": { + "document": { + "_id": 6 + } + } + }, + { + "insertOne": { + "document": { + "_id": 7 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 2 + }, + "replacement": { + "y": 2 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 4 + } + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$gte": 2 + } + }, + "update": { + "$set": { + "z": 1 + } + } + } + }, + { + "deleteMany": { + "filter": { + "_id": { + "$gte": 6 + } + } + } + } + ] + }, + "expectResult": { + "deletedCount": 4, + "insertedCount": 6, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7 + } + }, + "matchedCount": 7, + "modifiedCount": 7, + "upsertedCount": 1, + "upsertedIds": { + "2": 2 + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": 2 + } + }, + "upsert": true, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + }, + { + "_id": 6 + }, + { + "_id": 7 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "y": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "y": 2 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + }, + { + "q": { + "_id": 4 + }, + "limit": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gte": 2 + } + }, + "u": { + "$set": { + "z": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$gte": 6 + } + }, + "limit": 0 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "y": 1 + }, + { + "_id": 2, + "y": 2, + "z": 1 + }, + { + "_id": 5, + "z": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/bulk.yml b/source/transactions/tests/unified/bulk.yml new file mode 100644 index 0000000000..5f686498b4 --- /dev/null +++ b/source/transactions/tests/unified/bulk.yml @@ -0,0 +1,329 @@ +description: bulk + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: bulk + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: deleteOne + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + deletedCount: 1 + - + object: *collection0 + name: bulkWrite + arguments: + session: *session0 + requests: + - + insertOne: + document: { _id: 1 } + - + updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } } + - + updateOne: + filter: { _id: 2 } + update: { $set: { x: 2 } } + # Produces upsertedIds: {2: 2} in the result + upsert: true + - + insertOne: + document: { _id: 3 } + - + insertOne: + document: { _id: 4 } + - + insertOne: + document: { _id: 5 } + - + insertOne: + document: { _id: 6 } + - + insertOne: + document: { _id: 7 } + # Keep replaces segregated from updates, so that drivers that aren't able to coalesce + # adjacent updates and replaces into a single update command will still pass this test + - + replaceOne: + filter: { _id: 1 } + replacement: { y: 1 } + - + replaceOne: + filter: { _id: 2 } + replacement: { y: 2 } + - + deleteOne: + filter: { _id: 3 } + - + deleteOne: + filter: { _id: 4 } + - + updateMany: + filter: { _id: { $gte: 2 } } + update: { $set: { z: 1 } } + # Keep deleteMany segregated from deleteOne, so that drivers that aren't able to coalesce + # adjacent mixed deletes into a single delete command will still pass this test + - + deleteMany: + filter: { _id: { $gte: 6 } } + expectResult: + deletedCount: 4 + insertedCount: 6 + insertedIds: + $$unsetOrMatches: + '0': 1 + '3': 3 + '4': 4 + '5': 5 + '6': 6 + '7': 7 + matchedCount: 7 + modifiedCount: 7 + upsertedCount: 1 + upsertedIds: { '2': 2 } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 1 } + limit: 1 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + # Commands in the bulkWrite + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $set: { x: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - + q: { _id: 2 } + u: { $set: { x: 2 } } + upsert: true + multi: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + - { _id: 6 } + - { _id: 7 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { y: 1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - + q: { _id: 2 } + u: { y: 2 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 3 } + limit: 1 + - + q: { _id: 4 } + limit: 1 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: { $gte: 2 } } + u: { $set: { z: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: { $gte: 6 } } + limit: 0 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, y: 1 } + - { _id: 2, y: 2, z: 1 } + - { _id: 5, z: 1 } diff --git a/source/transactions/tests/unified/causal-consistency.json b/source/transactions/tests/unified/causal-consistency.json new file mode 100644 index 0000000000..52a6cb8180 --- /dev/null +++ b/source/transactions/tests/unified/causal-consistency.json @@ -0,0 +1,426 @@ +{ + "description": "causal-consistency", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session_no_cc", + "client": "client0", + "sessionOptions": { + "causalConsistency": false + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "count": 0 + } + ] + } + ], + "tests": [ + { + "description": "causal consistency", + "operations": [ + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "count": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "count": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "count": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "count": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "count": 2 + } + ] + } + ] + }, + { + "description": "causal consistency disabled", + "operations": [ + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session_no_cc", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session_no_cc", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session_no_cc", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "count": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "session_no_cc", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session_no_cc" + }, + "txnNumber": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$inc": { + "count": 1 + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session_no_cc" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session_no_cc" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "count": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/causal-consistency.yml b/source/transactions/tests/unified/causal-consistency.yml new file mode 100644 index 0000000000..c3e75523de --- /dev/null +++ b/source/transactions/tests/unified/causal-consistency.yml @@ -0,0 +1,222 @@ +description: causal-consistency + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + - + session: + id: &session_no_cc session_no_cc + client: *client0 + sessionOptions: + causalConsistency: false + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, count: 0 } + +tests: + - + description: 'causal consistency' + operations: + # Update a document without a transaction + - &updateOne + object: *collection0 + name: updateOne + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { count: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + # Updating the same document inside a transaction. + # Casual consistency ensures that the transaction snapshot is causally after the first updateOne. + - + object: *session0 + name: startTransaction + - *updateOne + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $inc: { count: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $$exists: false } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $inc: { count: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, count: 2 } + - + description: 'causal consistency disabled' + operations: + # Insert a document without a transaction + - + object: *collection0 + name: insertOne + arguments: + session: *session_no_cc + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session_no_cc + name: startTransaction + - + object: *collection0 + name: updateOne + arguments: + session: *session_no_cc + filter: { _id: 1 } + update: { $inc: { count: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - + object: *session_no_cc + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session_no_cc } + txnNumber: { $$exists: false } + autocommit: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 1 } + u: { $inc: { count: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + # No afterClusterTime + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session_no_cc } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session_no_cc } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1, count: 1 } + - { _id: 2 } diff --git a/source/transactions/tests/unified/client-bulkWrite.json b/source/transactions/tests/unified/client-bulkWrite.json new file mode 100644 index 0000000000..4a8d013f8d --- /dev/null +++ b/source/transactions/tests/unified/client-bulkWrite.json @@ -0,0 +1,593 @@ +{ + "description": "client bulkWrite transactions", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client_with_wmajority", + "uriOptions": { + "w": "majority" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "session": { + "id": "session_with_wmajority", + "client": "client_with_wmajority" + } + } + ], + "_yamlAnchors": { + "namespace": "transaction-tests.coll0" + }, + "initialData": [ + { + "databaseName": "transaction-tests", + "collectionName": "coll0", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + } + ] + } + ], + "tests": [ + { + "description": "client bulkWrite in a transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "transaction-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + }, + { + "updateOne": { + "namespace": "transaction-tests.coll0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "namespace": "transaction-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "update": { + "$inc": { + "x": 2 + } + } + } + }, + { + "replaceOne": { + "namespace": "transaction-tests.coll0", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 44 + }, + "upsert": true + } + }, + { + "deleteOne": { + "namespace": "transaction-tests.coll0", + "filter": { + "_id": 5 + } + } + }, + { + "deleteMany": { + "namespace": "transaction-tests.coll0", + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 1, + "matchedCount": 3, + "modifiedCount": 3, + "deletedCount": 3, + "insertResults": { + "0": { + "insertedId": 8 + } + }, + "updateResults": { + "1": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedId": { + "$$exists": false + } + }, + "2": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedId": { + "$$exists": false + } + }, + "3": { + "matchedCount": 1, + "modifiedCount": 0, + "upsertedId": 4 + } + }, + "deleteResults": { + "4": { + "deletedCount": 1 + }, + "5": { + "deletedCount": 2 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + }, + { + "update": 0, + "filter": { + "_id": 1 + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "multi": false + }, + { + "update": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 1 + } + }, + { + "_id": { + "$lte": 3 + } + } + ] + }, + "updateMods": { + "$inc": { + "x": 2 + } + }, + "multi": true + }, + { + "update": 0, + "filter": { + "_id": 4 + }, + "updateMods": { + "x": 44 + }, + "upsert": true, + "multi": false + }, + { + "delete": 0, + "filter": { + "_id": 5 + }, + "multi": false + }, + { + "delete": 0, + "filter": { + "$and": [ + { + "_id": { + "$gt": 5 + } + }, + { + "_id": { + "$lte": 7 + } + } + ] + }, + "multi": true + } + ], + "nsInfo": [ + { + "ns": "transaction-tests.coll0" + } + ] + } + } + }, + { + "commandStartedEvent": { + "commandName": "commitTransaction", + "databaseName": "admin", + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 35 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 8, + "x": 88 + } + ] + } + ] + }, + { + "description": "client writeConcern ignored for client bulkWrite in transaction", + "operations": [ + { + "object": "session_with_wmajority", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 1 + } + } + }, + { + "object": "client_with_wmajority", + "name": "clientBulkWrite", + "arguments": { + "session": "session_with_wmajority", + "models": [ + { + "insertOne": { + "namespace": "transaction-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "$$unsetOrMatches": {} + }, + "updateResults": { + "$$unsetOrMatches": {} + }, + "deleteResults": { + "$$unsetOrMatches": {} + } + } + }, + { + "object": "session_with_wmajority", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client_with_wmajority", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "lsid": { + "$$sessionLsid": "session_with_wmajority" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "bulkWrite": 1, + "errorsOnly": true, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 8, + "x": 88 + } + } + ], + "nsInfo": [ + { + "ns": "transaction-tests.coll0" + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session_with_wmajority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": 1 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + }, + { + "_id": 7, + "x": 77 + }, + { + "_id": 8, + "x": 88 + } + ] + } + ] + }, + { + "description": "client bulkWrite with writeConcern in a transaction causes a transaction error", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "session": "session0", + "writeConcern": { + "w": 1 + }, + "models": [ + { + "insertOne": { + "namespace": "transaction-tests.coll0", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "isClientError": true, + "errorContains": "Cannot set write concern after starting a transaction" + } + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/client-bulkWrite.yml b/source/transactions/tests/unified/client-bulkWrite.yml new file mode 100644 index 0000000000..d80e618728 --- /dev/null +++ b/source/transactions/tests/unified/client-bulkWrite.yml @@ -0,0 +1,263 @@ +description: "client bulkWrite transactions" +schemaVersion: "1.4" # To support `serverless: forbid` +runOnRequirements: + - minServerVersion: "8.0" + topologies: + - replicaset + - sharded + - load-balanced + serverless: forbid + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - session: + id: &session0 session0 + client: *client0 + - client: + id: &client_with_wmajority client_with_wmajority + uriOptions: + w: majority + observeEvents: + - commandStartedEvent + - session: + id: &session_with_wmajority session_with_wmajority + client: *client_with_wmajority + +_yamlAnchors: + namespace: &namespace "transaction-tests.coll0" + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + - { _id: 7, x: 77 } + +tests: + - description: "client bulkWrite in a transaction" + operations: + - object: *session0 + name: startTransaction + - object: *client0 + name: clientBulkWrite + arguments: + session: *session0 + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + - updateOne: + namespace: *namespace + filter: { _id: 1 } + update: { $inc: { x: 1 } } + - updateMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + update: { $inc: { x: 2 } } + - replaceOne: + namespace: *namespace + filter: { _id: 4 } + replacement: { x: 44 } + upsert: true + - deleteOne: + namespace: *namespace + filter: { _id: 5 } + - deleteMany: + namespace: *namespace + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 1 + matchedCount: 3 + modifiedCount: 3 + deletedCount: 3 + insertResults: + 0: + insertedId: 8 + updateResults: + 1: + matchedCount: 1 + modifiedCount: 1 + upsertedId: { $$exists: false } + 2: + matchedCount: 2 + modifiedCount: 2 + upsertedId: { $$exists: false } + 3: + matchedCount: 1 + modifiedCount: 0 + upsertedId: 4 + deleteResults: + 4: + deletedCount: 1 + 5: + deletedCount: 2 + - object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 8, x: 88 } + - update: 0 + filter: { _id: 1 } + updateMods: { $inc: { x: 1 } } + multi: false + - update: 0 + filter: + $and: [ { _id: { $gt: 1 } }, { _id: { $lte: 3 } } ] + updateMods: { $inc: { x: 2 } } + multi: true + - update: 0 + filter: { _id: 4 } + updateMods: { x: 44 } + upsert: true + multi: false + - delete: 0 + filter: { _id: 5 } + multi: false + - delete: 0 + filter: + $and: [ { _id: { $gt: 5 } }, { _id: { $lte: 7 } } ] + multi: true + nsInfo: + - ns: *namespace + - commandStartedEvent: + commandName: commitTransaction + databaseName: admin + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 24 } + - { _id: 3, x: 35 } + - { _id: 4, x: 44 } + - { _id: 8, x: 88 } + - description: 'client writeConcern ignored for client bulkWrite in transaction' + operations: + - object: *session_with_wmajority + name: startTransaction + arguments: + writeConcern: + w: 1 + - object: *client_with_wmajority + name: clientBulkWrite + arguments: + session: *session_with_wmajority + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + $$unsetOrMatches: {} + updateResults: + $$unsetOrMatches: {} + deleteResults: + $$unsetOrMatches: {} + - object: *session_with_wmajority + name: commitTransaction + expectEvents: + - + client: *client_with_wmajority + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + lsid: { $$sessionLsid: *session_with_wmajority } + txnNumber: 1 + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + bulkWrite: 1 + errorsOnly: true + ordered: true + ops: + - insert: 0 + document: { _id: 8, x: 88 } + nsInfo: + - ns: *namespace + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session_with_wmajority } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: 1 + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + - { _id: 7, x: 77 } + - { _id: 8, x: 88 } + - description: "client bulkWrite with writeConcern in a transaction causes a transaction error" + operations: + - object: *session0 + name: startTransaction + - object: *client0 + name: clientBulkWrite + arguments: + session: *session0 + writeConcern: + w: 1 + models: + - insertOne: + namespace: *namespace + document: { _id: 8, x: 88 } + expectError: + isClientError: true + errorContains: "Cannot set write concern after starting a transaction" diff --git a/source/transactions/tests/unified/commit.json b/source/transactions/tests/unified/commit.json new file mode 100644 index 0000000000..de3be30470 --- /dev/null +++ b/source/transactions/tests/unified/commit.json @@ -0,0 +1,1250 @@ +{ + "description": "commit", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commit", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "rerun commit after empty transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "multiple commits in a row", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "write concern error on commit", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 10 + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commit without start", + "operations": [ + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorContains": "no transaction started" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "commit after no-op abort", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorContains": "Cannot call commitTransaction after calling abortTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "commit after abort", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorContains": "Cannot call commitTransaction after calling abortTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "multiple commits after empty transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "reset session state commit", + "operations": [ + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorContains": "no transaction started" + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "reset session state abort", + "operations": [ + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction", + "expectError": { + "errorContains": "no transaction started" + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + }, + "level": { + "$$exists": false + } + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/commit.yml b/source/transactions/tests/unified/commit.yml new file mode 100644 index 0000000000..e0f12fdb09 --- /dev/null +++ b/source/transactions/tests/unified/commit.yml @@ -0,0 +1,719 @@ +description: commit + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + # Define a second set of entities for a retryWrites=false client + - + client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - + database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + session: + id: &session1 session1 + client: *client1 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: commit + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + # Again, to verify that txnNumber is incremented + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - + description: 'rerun commit after empty transaction' + operations: + - + object: *session0 + name: startTransaction + - + object: *session0 + name: commitTransaction + # Rerun the commit (which does not increment the txnNumber) + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'multiple commits in a row' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'write concern error on commit' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: 10 + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + # { + # 'ok': 1.0, + # 'writeConcernError': { + # 'code': 100, + # 'codeName': 'UnsatisfiableWriteConcern', + # 'errmsg': 'Not enough data-bearing nodes' + # } + # } + errorLabelsOmit: + - TransientTransactionError + - UnknownTransactionCommitResult + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commit without start' + operations: + - + object: *session0 + name: commitTransaction + expectError: + errorContains: 'no transaction started' + expectEvents: + - + client: *client0 + events: [] + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'commit after no-op abort' + operations: + - + object: *session0 + name: startTransaction + - + object: *session0 + name: abortTransaction + - + object: *session0 + name: commitTransaction + expectError: + errorContains: 'Cannot call commitTransaction after calling abortTransaction' + expectEvents: + - + client: *client0 + events: [] + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'commit after abort' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + - + object: *session0 + name: commitTransaction + expectError: + errorContains: 'Cannot call commitTransaction after calling abortTransaction' + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + description: 'multiple commits after empty transaction' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + # Increments txnNumber + - + object: *session0 + name: startTransaction + # These commits aren't sent to server, transaction is empty + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: commitTransaction + # Verify that previous, empty transaction incremented txnNumber + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + # txnNumber 2 was skipped + txnNumber: { $numberLong: '3' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '3' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'reset session state commit' + operations: + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + # Running any operation after an ended transaction resets the session state to "no transaction" + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + # Calling commit again should error instead of re-running the commit + - + object: *session1 + name: commitTransaction + expectError: + errorContains: 'no transaction started' + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $$exists: false } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + commandName: insert + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - + description: 'reset session state abort' + operations: + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: abortTransaction + # Running any operation after an ended transaction resets the session state to "no transaction" + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + # Calling abort should error with "no transaction started" instead of "cannot call abortTransaction twice" + - + object: *session1 + name: abortTransaction + expectError: + errorContains: 'no transaction started' + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + level: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $$exists: false } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + commandName: insert + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 2 } diff --git a/source/transactions/tests/unified/count.json b/source/transactions/tests/unified/count.json new file mode 100644 index 0000000000..404b06beb6 --- /dev/null +++ b/source/transactions/tests/unified/count.json @@ -0,0 +1,177 @@ +{ + "description": "count", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0.2", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ], + "tests": [ + { + "description": "count", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "count", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorCodeName": "OperationNotSupportedInTransaction", + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "count": "test", + "query": { + "_id": 1 + }, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "count", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/count.yml b/source/transactions/tests/unified/count.yml new file mode 100644 index 0000000000..c78d5b2ae3 --- /dev/null +++ b/source/transactions/tests/unified/count.yml @@ -0,0 +1,102 @@ +description: count + +schemaVersion: '1.3' + +runOnRequirements: + # SERVER-35388 introduced OperationNotSupportedInTransaction in 4.0.2 + - + minServerVersion: 4.0.2 + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + +tests: + - + description: count + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: count + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorCodeName: OperationNotSupportedInTransaction + errorLabelsOmit: + - TransientTransactionError + - UnknownTransactionCommitResult + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + count: *collection_name + query: { _id: 1 } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: count + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data \ No newline at end of file diff --git a/source/transactions/tests/unified/create-collection.json b/source/transactions/tests/unified/create-collection.json new file mode 100644 index 0000000000..e190088b3b --- /dev/null +++ b/source/transactions/tests/unified/create-collection.json @@ -0,0 +1,282 @@ +{ + "description": "create-collection", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "explicitly create collection using create command", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database0", + "name": "createCollection", + "arguments": { + "session": "session0", + "collection": "test" + } + }, + { + "object": "testRunner", + "name": "assertCollectionNotExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertCollectionExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "create", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "implicitly create collection using insert", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "assertCollectionNotExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertCollectionExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/create-collection.yml b/source/transactions/tests/unified/create-collection.yml new file mode 100644 index 0000000000..f795716865 --- /dev/null +++ b/source/transactions/tests/unified/create-collection.yml @@ -0,0 +1,177 @@ +description: create-collection + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.4 + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'explicitly create collection using create command' + operations: + - + object: *database0 + name: dropCollection + arguments: + collection: *collection_name + - + object: *session0 + name: startTransaction + - + object: *database0 + name: createCollection + arguments: + session: *session0 + collection: *collection_name + - + object: testRunner + name: assertCollectionNotExists + arguments: + databaseName: *database_name + collectionName: *collection_name + - + object: *session0 + name: commitTransaction + - + object: testRunner + name: assertCollectionExists + arguments: + databaseName: *database_name + collectionName: *collection_name + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + drop: *collection_name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database_name + - + commandStartedEvent: + command: + create: *collection_name + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: create + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + description: 'implicitly create collection using insert' + operations: + - + object: *database0 + name: dropCollection + arguments: + collection: *collection_name + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: testRunner + name: assertCollectionNotExists + arguments: + databaseName: *database_name + collectionName: *collection_name + - + object: *session0 + name: commitTransaction + - + object: testRunner + name: assertCollectionExists + arguments: + databaseName: *database_name + collectionName: *collection_name + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + drop: *collection_name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/create-index.json b/source/transactions/tests/unified/create-index.json new file mode 100644 index 0000000000..98d6e11547 --- /dev/null +++ b/source/transactions/tests/unified/create-index.json @@ -0,0 +1,313 @@ +{ + "description": "create-index", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "create index on a non-existing collection", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "session": "session0", + "name": "t_1", + "keys": { + "x": 1 + } + } + }, + { + "object": "testRunner", + "name": "assertIndexNotExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "t_1" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertIndexExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "t_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "test", + "indexes": [ + { + "name": "t_1", + "key": { + "x": 1 + } + } + ], + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "createIndexes", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "create index on a collection created within the same transaction", + "operations": [ + { + "object": "database0", + "name": "dropCollection", + "arguments": { + "collection": "test" + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database0", + "name": "createCollection", + "arguments": { + "session": "session0", + "collection": "test" + } + }, + { + "object": "collection0", + "name": "createIndex", + "arguments": { + "session": "session0", + "name": "t_1", + "keys": { + "x": 1 + } + } + }, + { + "object": "testRunner", + "name": "assertIndexNotExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "t_1" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertIndexExists", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "t_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "create", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "test", + "indexes": [ + { + "name": "t_1", + "key": { + "x": 1 + } + } + ], + "lsid": { + "$$sessionLsid": "session0" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "createIndexes", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/create-index.yml b/source/transactions/tests/unified/create-index.yml new file mode 100644 index 0000000000..4465d0922e --- /dev/null +++ b/source/transactions/tests/unified/create-index.yml @@ -0,0 +1,202 @@ +description: create-index + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.4 + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] +tests: + - + description: 'create index on a non-existing collection' + operations: + - + object: *database0 + name: dropCollection + arguments: + collection: *collection_name + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: createIndex + arguments: + session: *session0 + name: &index_name t_1 + keys: + x: 1 + - + object: testRunner + name: assertIndexNotExists + arguments: + databaseName: *database_name + collectionName: *collection_name + indexName: *index_name + - + object: *session0 + name: commitTransaction + - + object: testRunner + name: assertIndexExists + arguments: + databaseName: *database_name + collectionName: *collection_name + indexName: *index_name + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + drop: *collection_name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database_name + - + commandStartedEvent: + command: + createIndexes: *collection_name + indexes: + - + name: *index_name + key: + x: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: createIndexes + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + description: 'create index on a collection created within the same transaction' + operations: + - + object: *database0 + name: dropCollection + arguments: + collection: *collection_name + - + object: *session0 + name: startTransaction + - + object: *database0 + name: createCollection + arguments: + session: *session0 + collection: *collection_name + - + object: *collection0 + name: createIndex + arguments: + session: *session0 + name: *index_name + keys: + x: 1 + - + object: testRunner + name: assertIndexNotExists + arguments: + databaseName: *database_name + collectionName: *collection_name + indexName: *index_name + - + object: *session0 + name: commitTransaction + - + object: testRunner + name: assertIndexExists + arguments: + databaseName: *database_name + collectionName: *collection_name + indexName: *index_name + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + drop: *collection_name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database_name + - + commandStartedEvent: + command: + create: *collection_name + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: create + databaseName: *database_name + - + commandStartedEvent: + command: + createIndexes: *collection_name + indexes: + - + name: *index_name + key: + x: 1 + lsid: { $$sessionLsid: *session0 } + writeConcern: { $$exists: false } + commandName: createIndexes + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/delete.json b/source/transactions/tests/unified/delete.json new file mode 100644 index 0000000000..4c1cae0a4e --- /dev/null +++ b/source/transactions/tests/unified/delete.json @@ -0,0 +1,425 @@ +{ + "description": "delete", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + ], + "tests": [ + { + "description": "delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "collection0", + "name": "deleteMany", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$lte": 3 + } + } + }, + "expectResult": { + "deletedCount": 2 + } + }, + { + "object": "collection0", + "name": "deleteOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$lte": 3 + } + }, + "limit": 0 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 4 + }, + "limit": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 5 + } + ] + } + ] + }, + { + "description": "collection writeConcern ignored for delete", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection_wc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection_wc_majority", + "name": "deleteOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "collection_wc_majority", + "name": "deleteMany", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$lte": 3 + } + } + }, + "expectResult": { + "deletedCount": 2 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": { + "$lte": 3 + } + }, + "limit": 0 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/delete.yml b/source/transactions/tests/unified/delete.yml new file mode 100644 index 0000000000..49bbf16137 --- /dev/null +++ b/source/transactions/tests/unified/delete.yml @@ -0,0 +1,240 @@ +description: delete + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + +tests: + - + description: delete + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: deleteOne + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + deletedCount: 1 + - + object: *collection0 + name: deleteMany + arguments: + session: *session0 + filter: { _id: { $lte: 3 } } + expectResult: + deletedCount: 2 + - + object: *collection0 + name: deleteOne + arguments: + session: *session0 + filter: { _id: 4 } + expectResult: + deletedCount: 1 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 1 } + limit: 1 + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: { $lte: 3 } } + limit: 0 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 4 } + limit: 1 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 5 } + - + description: 'collection writeConcern ignored for delete' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection_wc_majority + name: deleteOne + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + deletedCount: 1 + - + object: *collection_wc_majority + name: deleteMany + arguments: + session: *session0 + filter: { _id: { $lte: 3 } } + expectResult: + deletedCount: 2 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 1 } + limit: 1 + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: { $lte: 3 } } + limit: 0 + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: delete + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/do-not-retry-read-in-transaction.json b/source/transactions/tests/unified/do-not-retry-read-in-transaction.json new file mode 100644 index 0000000000..6d9dc704b8 --- /dev/null +++ b/source/transactions/tests/unified/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/source/transactions/tests/unified/do-not-retry-read-in-transaction.yml b/source/transactions/tests/unified/do-not-retry-read-in-transaction.yml new file mode 100644 index 0000000000..e30df0b290 --- /dev/null +++ b/source/transactions/tests/unified/do-not-retry-read-in-transaction.yml @@ -0,0 +1,64 @@ +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 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + uriOptions: { retryReads: true } + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-read-in-transaction-test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - session: + id: &session0 session0 + client: *client0 + +tests: + - description: "find does not retry in a transaction" + operations: + + - name: startTransaction + object: *session0 + + - name: failPoint # fail the following find command + 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: *collectionName + filter: {} + startTransaction: true + commandName: find + databaseName: *databaseName diff --git a/source/transactions/tests/unified/error-labels-blockConnection.json b/source/transactions/tests/unified/error-labels-blockConnection.json new file mode 100644 index 0000000000..8da04d1005 --- /dev/null +++ b/source/transactions/tests/unified/error-labels-blockConnection.json @@ -0,0 +1,235 @@ +{ + "description": "error-labels-blockConnection", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.2", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "uriOptions": { + "socketTimeoutMS": 100 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "blockConnection": true, + "blockTimeMS": 150 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/error-labels-blockConnection.yml b/source/transactions/tests/unified/error-labels-blockConnection.yml new file mode 100644 index 0000000000..7fed7fc92b --- /dev/null +++ b/source/transactions/tests/unified/error-labels-blockConnection.yml @@ -0,0 +1,158 @@ +# This file contains a single test that should be in error-labels.yml. The test +# was moved from error-labels.yml during the spec work for client-side +# operations timeout because it uses the blockConnection parameter in +# failCommand, which is only available in server versions 4.2+. It should be +# merged back into error-labels.yml when that test file is ported to the +# unified test format as the format allows for per-test runOn requirements. +description: error-labels-blockConnection + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.2' + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + uriOptions: + socketTimeoutMS: 100 + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + # This test previously used failCommand with closeConnection=true to force a + # network error, but this does not work after CSOT is implemented because + # network errors are retried indefinitely. It has been changed to use + # socketTimeoutMS with blockConnection to force a network error because + # drivers only retry socketTimeoutMS-related errors once rather than + # indefinitely. + - + description: 'add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + # Drivers stop retrying after two socket timeouts that occur due to the use of socketTimeoutMS + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + blockConnection: true + blockTimeMS: 150 + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/error-labels-errorLabels.json b/source/transactions/tests/unified/error-labels-errorLabels.json new file mode 100644 index 0000000000..1f95ad3419 --- /dev/null +++ b/source/transactions/tests/unified/error-labels-errorLabels.json @@ -0,0 +1,423 @@ +{ + "description": "error-labels-errorLabels", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "serverless": "forbid", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to retryable commit errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 11602, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to writeConcernError ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/error-labels-errorLabels.yml b/source/transactions/tests/unified/error-labels-errorLabels.yml new file mode 100644 index 0000000000..4af816549d --- /dev/null +++ b/source/transactions/tests/unified/error-labels-errorLabels.yml @@ -0,0 +1,257 @@ +description: error-labels-errorLabels + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: forbid + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'add RetryableWriteError and UnknownTransactionCommitResult labels to retryable commit errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + errorCode: 11602 # InterruptedDueToReplStateChange + errorLabels: + - RetryableWriteError + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'add RetryableWriteError and UnknownTransactionCommitResult labels to writeConcernError ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/error-labels.json b/source/transactions/tests/unified/error-labels.json new file mode 100644 index 0000000000..74ed750b07 --- /dev/null +++ b/source/transactions/tests/unified/error-labels.json @@ -0,0 +1,2263 @@ +{ + "description": "error-labels", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "DuplicateKey errors do not contain transient label", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 1 + }, + { + "_id": 1 + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ], + "errorContains": "E11000" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + }, + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "NotWritablePrimary errors contain transient label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 10107 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "WriteConflict errors contain transient label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 112 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "NoSuchTransaction errors contain transient label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 251 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "NoSuchTransaction errors on commit contain transient label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 251 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "add TransientTransactionError label to connection errors, but do not add RetryableWriteError label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 4 + }, + "data": { + "failCommands": [ + "insert", + "find", + "aggregate", + "distinct" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "session": "session0" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": {}, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "do not add RetryableWriteError label to writeConcernError ShutdownInProgress that occurs within transaction", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 64, + "errmsg": "multiple errors reported" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernTimeout with wtimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 64, + "errmsg": "waiting for replication timed out", + "errInfo": { + "wtimeout": true + } + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "omit UnknownTransactionCommitResult label from writeConcernError UnsatisfiableWriteConcern", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 100, + "errmsg": "Not enough data-bearing nodes" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "omit UnknownTransactionCommitResult label from writeConcernError UnknownReplWriteConcern", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 79, + "errmsg": "No write concern mode named 'blah' found in replica set configuration" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteConcern", + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "do not add UnknownTransactionCommitResult label to MaxTimeMSExpired inside transactions", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 50 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "maxTimeMS": 60000, + "session": "session0" + }, + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError", + "UnknownTransactionCommitResult", + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": {}, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "maxTimeMS": 60000 + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "add UnknownTransactionCommitResult label to MaxTimeMSExpired", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 50 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + }, + "maxCommitTimeMS": 60000 + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "add UnknownTransactionCommitResult label to writeConcernError MaxTimeMSExpired", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 50, + "errmsg": "operation exceeded time limit" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + }, + "maxCommitTimeMS": 60000 + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/error-labels.yml b/source/transactions/tests/unified/error-labels.yml new file mode 100644 index 0000000000..64e241d55d --- /dev/null +++ b/source/transactions/tests/unified/error-labels.yml @@ -0,0 +1,1314 @@ +description: error-labels + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'DuplicateKey errors do not contain transient label' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertMany + arguments: + session: *session0 + documents: + - { _id: 1 } + - { _id: 1 } + expectError: + errorLabelsOmit: + - TransientTransactionError + - UnknownTransactionCommitResult + # DuplicateKey error code included in the bulk write error message + # returned by the server + errorContains: E11000 + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'NotWritablePrimary errors contain transient label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + errorCode: 10107 # NotWritablePrimary + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + # Note, the server will return the errorLabel in this case + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'WriteConflict errors contain transient label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + errorCode: 112 # WriteConflict + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + # Note, the server will return the errorLabel in this case + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'NoSuchTransaction errors contain transient label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + errorCode: 251 # NoSuchTransaction + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + # Note, the server will return the errorLabel in this case + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'NoSuchTransaction errors on commit contain transient label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 251 # NoSuchTransaction + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + # Note, the server will return the errorLabel in this case + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'add TransientTransactionError label to connection errors, but do not add RetryableWriteError label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 4 } + data: + failCommands: + - insert + - find + - aggregate + - distinct + closeConnection: true + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: &transient_label_only + errorLabelsContain: + - TransientTransactionError + # While a connection error would normally be retryable, these are not because they occur within a transaction. + # Ensure the driver does not add the RetryableWriteError label to these errors. + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + - + object: *collection0 + name: find + arguments: + filter: {} + session: *session0 + expectError: *transient_label_only + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + session: *session0 + expectError: *transient_label_only + - + object: *collection0 + name: distinct + arguments: + fieldName: _id + filter: {} + session: *session0 + expectError: *transient_label_only + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: { } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: distinct + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + closeConnection: true + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'do not add RetryableWriteError label to writeConcernError ShutdownInProgress that occurs within transaction' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - UnknownTransactionCommitResult + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'add UnknownTransactionCommitResult label to writeConcernError WriteConcernTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 64 # WriteConcernTimeout without wtimeout + errmsg: 'multiple errors reported' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - UnknownTransactionCommitResult + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'add UnknownTransactionCommitResult label to writeConcernError WriteConcernTimeout with wtimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 64 + errmsg: 'waiting for replication timed out' + errInfo: + wtimeout: true + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - UnknownTransactionCommitResult + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'omit UnknownTransactionCommitResult label from writeConcernError UnsatisfiableWriteConcern' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 100 # UnsatisfiableWriteConcern + errmsg: 'Not enough data-bearing nodes' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - UnknownTransactionCommitResult + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'omit UnknownTransactionCommitResult label from writeConcernError UnknownReplWriteConcern' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 79 # UnknownReplWriteConcern + errmsg: "No write concern mode named 'blah' found in replica set configuration" + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsOmit: + - RetryableWriteConcern + - TransientTransactionError + - UnknownTransactionCommitResult + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'do not add UnknownTransactionCommitResult label to MaxTimeMSExpired inside transactions' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - aggregate + errorCode: 50 # MaxTimeMSExpired + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + maxTimeMS: 60000 + session: *session0 + expectError: + errorLabelsOmit: + - RetryableWriteError + - UnknownTransactionCommitResult + - TransientTransactionError + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: { } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + autocommit: false + maxTimeMS: 60000 + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'add UnknownTransactionCommitResult label to MaxTimeMSExpired' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 50 # MaxTimeMSExpired + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + maxCommitTimeMS: 60000 + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - UnknownTransactionCommitResult + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'add UnknownTransactionCommitResult label to writeConcernError MaxTimeMSExpired' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 50 # MaxTimeMSExpired + errmsg: 'operation exceeded time limit' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + maxCommitTimeMS: 60000 + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsContain: + - UnknownTransactionCommitResult + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/errors-client.json b/source/transactions/tests/unified/errors-client.json new file mode 100644 index 0000000000..00f1497c2d --- /dev/null +++ b/source/transactions/tests/unified/errors-client.json @@ -0,0 +1,142 @@ +{ + "description": "errors-client", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Client side error in command starting transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "x": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "object": "testRunner", + "name": "assertSessionTransactionState", + "arguments": { + "session": "session0", + "state": "starting" + } + } + ] + }, + { + "description": "Client side error when transaction is in progress", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "x": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "object": "testRunner", + "name": "assertSessionTransactionState", + "arguments": { + "session": "session0", + "state": "in_progress" + } + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/errors-client.yml b/source/transactions/tests/unified/errors-client.yml new file mode 100644 index 0000000000..d3f451b98c --- /dev/null +++ b/source/transactions/tests/unified/errors-client.yml @@ -0,0 +1,96 @@ +description: errors-client + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'Client side error in command starting transaction' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: updateOne + arguments: + session: *session0 + filter: { _id: 1 } + update: + x: 1 + expectError: + isError: true + - + object: testRunner + name: assertSessionTransactionState + arguments: + session: *session0 + state: starting + - + description: 'Client side error when transaction is in progress' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: updateOne + arguments: + session: *session0 + filter: { _id: 1 } + update: + x: 1 + expectError: + isError: true + - + object: testRunner + name: assertSessionTransactionState + arguments: + session: *session0 + state: in_progress diff --git a/source/transactions/tests/unified/errors.json b/source/transactions/tests/unified/errors.json new file mode 100644 index 0000000000..94a9cac207 --- /dev/null +++ b/source/transactions/tests/unified/errors.json @@ -0,0 +1,285 @@ +{ + "description": "errors", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "start insert start", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "expectError": { + "isClientError": true, + "errorContains": "transaction already in progress" + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ] + }, + { + "description": "start twice", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "startTransaction", + "expectError": { + "isClientError": true, + "errorContains": "transaction already in progress" + } + } + ] + }, + { + "description": "commit and start twice", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session0", + "name": "startTransaction", + "expectError": { + "isClientError": true, + "errorContains": "transaction already in progress" + } + } + ] + }, + { + "description": "write conflict commit", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorCodeName": "WriteConflict", + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorCodeName": "NoSuchTransaction", + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + } + ] + }, + { + "description": "write conflict abort", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorCodeName": "WriteConflict", + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "abortTransaction" + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/errors.yml b/source/transactions/tests/unified/errors.yml new file mode 100644 index 0000000000..ce6ecfcff7 --- /dev/null +++ b/source/transactions/tests/unified/errors.yml @@ -0,0 +1,187 @@ +description: errors + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + - + session: + id: &session1 session1 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'start insert start' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: startTransaction + expectError: + isClientError: true + errorContains: 'transaction already in progress' + # Just to clean up + - + object: *session0 + name: commitTransaction + - + description: 'start twice' + operations: + - + object: *session0 + name: startTransaction + - + object: *session0 + name: startTransaction + expectError: + isClientError: true + errorContains: 'transaction already in progress' + - + description: 'commit and start twice' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: startTransaction + - + object: *session0 + name: startTransaction + expectError: + isClientError: true + errorContains: 'transaction already in progress' + - + description: 'write conflict commit' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectError: + errorCodeName: WriteConflict + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + - + object: *session0 + name: commitTransaction + - + object: *session1 + name: commitTransaction + expectError: + errorCodeName: NoSuchTransaction + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + - + description: 'write conflict abort' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectError: + errorCodeName: WriteConflict + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + - + object: *session0 + name: commitTransaction + # Driver ignores "NoSuchTransaction" error + - + object: *session1 + name: abortTransaction diff --git a/source/transactions/tests/unified/findOneAndDelete.json b/source/transactions/tests/unified/findOneAndDelete.json new file mode 100644 index 0000000000..7db9c872af --- /dev/null +++ b/source/transactions/tests/unified/findOneAndDelete.json @@ -0,0 +1,317 @@ +{ + "description": "findOneAndDelete", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndDelete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + } + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "collection0", + "name": "findOneAndDelete", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "remove": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 4 + }, + "remove": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "collection writeConcern ignored for findOneAndDelete", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection_wc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection_wc_majority", + "name": "findOneAndDelete", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + } + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "remove": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/findOneAndDelete.yml b/source/transactions/tests/unified/findOneAndDelete.yml new file mode 100644 index 0000000000..446318970f --- /dev/null +++ b/source/transactions/tests/unified/findOneAndDelete.yml @@ -0,0 +1,183 @@ +description: findOneAndDelete + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + +tests: + - + description: findOneAndDelete + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: findOneAndDelete + arguments: + session: *session0 + filter: { _id: 3 } + expectResult: + _id: 3 + - + object: *collection0 + name: findOneAndDelete + arguments: + session: *session0 + filter: { _id: 4 } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + remove: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 4 } + remove: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - + description: 'collection writeConcern ignored for findOneAndDelete' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection_wc_majority + name: findOneAndDelete + arguments: + session: *session0 + filter: { _id: 3 } + expectResult: + _id: 3 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + remove: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/findOneAndReplace.json b/source/transactions/tests/unified/findOneAndReplace.json new file mode 100644 index 0000000000..f0742f0c60 --- /dev/null +++ b/source/transactions/tests/unified/findOneAndReplace.json @@ -0,0 +1,356 @@ +{ + "description": "findOneAndReplace", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndReplace", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "replacement": { + "x": 1 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "collection0", + "name": "findOneAndReplace", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + }, + "replacement": { + "x": 1 + }, + "upsert": true, + "returnDocument": "After" + }, + "expectResult": { + "_id": 4, + "x": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "x": 1 + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 4 + }, + "update": { + "x": 1 + }, + "new": true, + "upsert": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3, + "x": 1 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + }, + { + "description": "collection writeConcern ignored for findOneAndReplace", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection_wc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection_wc_majority", + "name": "findOneAndReplace", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "replacement": { + "x": 1 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "x": 1 + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/findOneAndReplace.yml b/source/transactions/tests/unified/findOneAndReplace.yml new file mode 100644 index 0000000000..f1b79c9588 --- /dev/null +++ b/source/transactions/tests/unified/findOneAndReplace.yml @@ -0,0 +1,202 @@ +description: findOneAndReplace + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + +tests: + - + description: findOneAndReplace + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: findOneAndReplace + arguments: + session: *session0 + filter: { _id: 3 } + replacement: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + - + object: *collection0 + name: findOneAndReplace + arguments: + session: *session0 + filter: { _id: 4 } + replacement: + x: 1 + upsert: true + returnDocument: After + expectResult: + _id: 4 + x: 1 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { x: 1 } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 4 } + update: { x: 1 } + new: true + upsert: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3, x: 1 } + - { _id: 4, x: 1 } + - + description: 'collection writeConcern ignored for findOneAndReplace' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection_wc_majority + name: findOneAndReplace + arguments: + session: *session0 + filter: { _id: 3 } + replacement: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { x: 1 } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/findOneAndUpdate.json b/source/transactions/tests/unified/findOneAndUpdate.json new file mode 100644 index 0000000000..f5308efef3 --- /dev/null +++ b/source/transactions/tests/unified/findOneAndUpdate.json @@ -0,0 +1,546 @@ +{ + "description": "findOneAndUpdate", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndUpdate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true, + "returnDocument": "After" + }, + "expectResult": { + "_id": 4, + "x": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3, + "x": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3, + "x": 2 + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": true, + "upsert": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3, + "x": 2 + }, + { + "_id": 4, + "x": 1 + } + ] + } + ] + }, + { + "description": "collection writeConcern ignored for findOneAndUpdate", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection_wc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection_wc_majority", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 3 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 3 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/findOneAndUpdate.yml b/source/transactions/tests/unified/findOneAndUpdate.yml new file mode 100644 index 0000000000..49d68a2090 --- /dev/null +++ b/source/transactions/tests/unified/findOneAndUpdate.yml @@ -0,0 +1,301 @@ +description: findOneAndUpdate + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + +tests: + - + description: findOneAndUpdate + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 3 } + update: + $inc: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + - + object: *collection0 + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 4 } + update: + $inc: + x: 1 + upsert: true + returnDocument: After + expectResult: + _id: 4 + x: 1 + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: startTransaction + # Test a second time to ensure txnNumber is incremented + - + object: *collection0 + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 3 } + update: + $inc: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + x: 1 + - + object: *session0 + name: commitTransaction + # Test a third time to ensure abort works + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 3 } + update: + $inc: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + x: 2 + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 4 } + update: { $inc: { x: 1 } } + new: true + upsert: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '3' } + startTransaction: true + autocommit: false + readConcern: + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '3' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3, x: 2 } + - { _id: 4, x: 1 } + - + description: 'collection writeConcern ignored for findOneAndUpdate' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection_wc_majority + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 3 } + update: + $inc: + x: 1 + returnDocument: Before + expectResult: + _id: 3 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 3 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: findAndModify + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/insert.json b/source/transactions/tests/unified/insert.json new file mode 100644 index 0000000000..9a80d8bf4b --- /dev/null +++ b/source/transactions/tests/unified/insert.json @@ -0,0 +1,895 @@ +{ + "description": "insert", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 5 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 5 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 5 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + ] + }, + { + "description": "insert with session1", + "operations": [ + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "session": "session1" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 4 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + }, + { + "description": "collection writeConcern without transaction", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection_wc_majority", + "database": "database1", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + }, + { + "session": { + "id": "session2", + "client": "client1" + } + } + ] + } + }, + { + "object": "collection_wc_majority", + "name": "insertOne", + "arguments": { + "session": "session2", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session2" + }, + "txnNumber": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "collection writeConcern ignored for insert", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection_wc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection_wc_majority", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection_wc_majority", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 2, + "1": 3 + } + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + }, + { + "_id": 3 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/insert.yml b/source/transactions/tests/unified/insert.yml new file mode 100644 index 0000000000..1e97069715 --- /dev/null +++ b/source/transactions/tests/unified/insert.yml @@ -0,0 +1,485 @@ +description: insert + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + - + session: + id: &session1 session1 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: insert + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2 } + - { _id: 3 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 5 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 5 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 5 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + # This test proves that the driver uses "session1" correctly in operations + # and APM expectations + - + description: 'insert with session1' + operations: + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 2 } + - { _id: 3 } + session: *session1 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 4 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + # This test proves that the driver parses the collectionOptions writeConcern + - + description: 'collection writeConcern without transaction' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database1 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - session: + id: &session2 session2 + client: *client1 + - + object: *collection_wc_majority + name: insertOne + arguments: + session: *session2 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session2 } + txnNumber: { $$exists: false } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + writeConcern: + w: majority + commandName: insert + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'collection writeConcern ignored for insert' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection_wc_majority collection_wc_majority + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection_wc_majority + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection_wc_majority + name: insertMany + arguments: + documents: + - { _id: 2 } + - { _id: 3 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 2 + '1': 3 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } diff --git a/source/transactions/tests/unified/isolation.json b/source/transactions/tests/unified/isolation.json new file mode 100644 index 0000000000..5d0a0139fb --- /dev/null +++ b/source/transactions/tests/unified/isolation.json @@ -0,0 +1,281 @@ +{ + "description": "isolation", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "one transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [] + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "two transactions", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [] + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectResult": [] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/isolation.yml b/source/transactions/tests/unified/isolation.yml new file mode 100644 index 0000000000..ff8b14f873 --- /dev/null +++ b/source/transactions/tests/unified/isolation.yml @@ -0,0 +1,174 @@ +# Test snapshot isolation. +# This test doesn't check contents of command-started events. +description: isolation + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + - + session: + id: &session1 session1 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'one transaction' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: find + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + - { _id: 1 } + - + object: *collection0 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectResult: [] + - + object: *collection0 + name: find + arguments: + filter: { _id: 1 } + expectResult: [] + - + object: *session0 + name: commitTransaction + - + object: *collection0 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectResult: + - { _id: 1 } + - + object: *collection0 + name: find + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1 } + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'two transactions' + operations: + - + object: *session0 + name: startTransaction + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection0 + name: find + arguments: + session: *session0 + filter: { _id: 1 } + expectResult: + - { _id: 1 } + - + object: *collection0 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectResult: [] + - + object: *collection0 + name: find + arguments: + filter: { _id: 1 } + expectResult: [] + - + object: *session0 + name: commitTransaction + # Snapshot isolation in session1, not read-committed. + - + object: *collection0 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectResult: [] + - + object: *collection0 + name: find + arguments: + filter: { _id: 1 } + expectResult: + - { _id: 1 } + - + object: *session1 + name: commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/mongos-pin-auto-tests.py b/source/transactions/tests/unified/mongos-pin-auto-tests.py new file mode 100644 index 0000000000..09306780fa --- /dev/null +++ b/source/transactions/tests/unified/mongos-pin-auto-tests.py @@ -0,0 +1,353 @@ +import itertools +import sys + +# Require Python 3.7+ for ordered dictionaries so that the order of the +# generated tests remain the same. +# Usage: +# python3.7 mongos-pin-auto-tests.py > mongos-pin-auto.yml +if sys.version_info[:2] < (3, 7): + print('ERROR: This script requires Python >= 3.7, not:') + print(sys.version) + print('Usage: python3.7 mongos-pin-auto-tests.py > mongos-pin-auto.yml') + exit(1) + +HEADER = '''# Autogenerated tests that transient errors in a transaction unpin the session. +# See mongos-pin-auto-tests.py + +description: mongos-pin-auto + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: "4.1.8" + # Note: tests utilize targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: "forbid" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + +tests: + - description: remain pinned after non-transient Interrupted error on insertOne + operations: + - &startTransaction + object: session0 + name: startTransaction + - &initialCommand + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 3 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } } + - object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "insert" ] + errorCode: 11601 + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + errorCodeName: Interrupted + - &assertSessionPinned + object: testRunner + name: assertSessionPinned + arguments: + session: *session0 + - &commitTransaction + object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + + - description: 'unpin after transient error within a transaction' + operations: + - *startTransaction + - *initialCommand + - object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "insert" ] + closeConnection: true + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + errorLabelsOmit: ["UnknownTransactionCommitResult"] + - &assertSessionUnpinned + object: testRunner + name: assertSessionUnpinned + arguments: + session: *session0 + - &abortTransaction + object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: abortTransaction + databaseName: admin + outcome: &outcome + - collectionName: *collection_name + databaseName: *database_name + documents: *data + + # The rest of the tests in this file test every operation type against + # multiple types of transient errors (connection and error code).''' + +TEMPLATE = ''' + - description: {test_name} {error_name} error on {op_name} {command_name} + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {{times: 1}} + data: + failCommands: ["{command_name}"] + {error_data} + - name: {op_name} + object: {object_name} + arguments: + session: *session0 + {op_args} + expectError: + {error_labels}: ["TransientTransactionError"] + - *{assertion} + - *abortTransaction + outcome: *outcome +''' + + +# Maps from op_name to (command_name, object_name, op_args) +OPS = { + # Write ops: + 'insertOne': ('insert', '*collection0', r'document: { _id: 4 }'), + 'insertMany': ('insert', '*collection0', r'documents: [ { _id: 4 }, { _id: 5 } ]'), + 'updateOne': ('update', '*collection0', r'''filter: { _id: 1 } + update: { $inc: { x: 1 } }'''), + 'replaceOne': ('update', '*collection0', r'''filter: { _id: 1 } + replacement: { y: 1 }'''), + 'updateMany': ('update', '*collection0', r'''filter: { _id: { $gte: 1 } } + update: {$set: { z: 1 } }'''), + 'deleteOne': ('delete', '*collection0', r'filter: { _id: 1 }'), + 'deleteMany': ('delete', '*collection0', r'filter: { _id: { $gte: 1 } }'), + 'findOneAndDelete': ('findAndModify', '*collection0', r'filter: { _id: 1 }'), + 'findOneAndUpdate': ('findAndModify', '*collection0', r'''filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before'''), + 'findOneAndReplace': ('findAndModify', '*collection0', r'''filter: { _id: 1 } + replacement: { y: 1 } + returnDocument: Before'''), + # Bulk write insert/update/delete: + 'bulkWrite insert': ('insert', '*collection0', r'''requests: + - insertOne: + document: { _id: 1 }'''), + 'bulkWrite update': ('update', '*collection0', r'''requests: + - updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } }'''), + 'bulkWrite delete': ('delete', '*collection0', r'''requests: + - deleteOne: + filter: { _id: 1 }'''), + # Read ops: + 'find': ('find', '*collection0', r'filter: { _id: 1 }'), + 'countDocuments': ('aggregate', '*collection0', r'filter: {}'), + 'aggregate': ('aggregate', '*collection0', r'pipeline: []'), + 'distinct': ('distinct', '*collection0', r'''fieldName: _id + filter: {}'''), + # runCommand: + 'runCommand': ('insert', '*database0', r'''commandName: insert + command: + insert: *collection_name + documents: + - { _id : 1 }'''), + # clientBulkWrite: + 'clientBulkWrite': ('bulkWrite', '*client0', r'''models: + - insertOne: + namespace: database0.collection0 + document: { _id: 8, x: 88 }'''), +} + +# Maps from error_name to error_data. +NON_TRANSIENT_ERRORS = { + 'Interrupted': 'errorCode: 11601', +} + +# Maps from error_name to error_data. +TRANSIENT_ERRORS = { + 'connection': 'closeConnection: true', + 'ShutdownInProgress': 'errorCode: 91', +} + + +def create_pin_test(op_name, error_name): + test_name = 'remain pinned after non-transient' + assertion = 'assertSessionPinned' + error_labels = 'errorLabelsOmit' + command_name, object_name, op_args = OPS[op_name] + error_data = NON_TRANSIENT_ERRORS[error_name] + if op_name.startswith('bulkWrite'): + op_name = 'bulkWrite' + test = TEMPLATE.format(**locals()) + if op_name == 'clientBulkWrite': + test += ' runOnRequirements:\n' + test += ' - minServerVersion: "8.0" # `bulkWrite` added to server 8.0"\n' + test += ' serverless: forbid\n' + return test + + +def create_unpin_test(op_name, error_name): + test_name = 'unpin after transient' + assertion = 'assertSessionUnpinned' + error_labels = 'errorLabelsContain' + command_name, object_name, op_args = OPS[op_name] + error_data = TRANSIENT_ERRORS[error_name] + if op_name.startswith('bulkWrite'): + op_name = 'bulkWrite' + test = TEMPLATE.format(**locals()) + if op_name == 'clientBulkWrite': + test += ' runOnRequirements:\n' + test += ' - minServerVersion: "8.0" # `bulkWrite` added to server 8.0"\n' + test += ' serverless: forbid\n' + return test + + + +tests = [] +for op_name, error_name in itertools.product(OPS, NON_TRANSIENT_ERRORS): + tests.append(create_pin_test(op_name, error_name)) +for op_name, error_name in itertools.product(OPS, TRANSIENT_ERRORS): + tests.append(create_unpin_test(op_name, error_name)) + +print(HEADER) +print(''.join(tests)) diff --git a/source/transactions/tests/unified/mongos-pin-auto.json b/source/transactions/tests/unified/mongos-pin-auto.json new file mode 100644 index 0000000000..27db520401 --- /dev/null +++ b/source/transactions/tests/unified/mongos-pin-auto.json @@ -0,0 +1,5474 @@ +{ + "description": "mongos-pin-auto", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ], + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "remain pinned after non-transient Interrupted error on insertOne", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ], + "errorCodeName": "Interrupted" + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + }, + { + "description": "unpin after transient error within a transaction", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on insertOne insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on insertMany insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on updateOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on replaceOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on updateMany update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + }, + "update": { + "$set": { + "z": 1 + } + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on deleteOne delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on deleteMany delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on findOneAndDelete findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on findOneAndUpdate findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on findOneAndReplace findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on bulkWrite insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on bulkWrite update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on bulkWrite delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on find find", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on countDocuments aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on aggregate aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on distinct distinct", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session0", + "fieldName": "_id", + "filter": {} + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on runCommand insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "session": "session0", + "commandName": "insert", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient Interrupted error on clientBulkWrite bulkWrite", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "database0.collection0", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ] + }, + { + "description": "unpin after transient connection error on insertOne insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on insertOne insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on insertMany insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on insertMany insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on updateOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on updateOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on replaceOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on replaceOne update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on updateMany update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + }, + "update": { + "$set": { + "z": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on updateMany update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + }, + "update": { + "$set": { + "z": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on deleteOne delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "closeConnection": true + } + } + } + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on deleteOne delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on deleteMany delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "closeConnection": true + } + } + } + }, + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on deleteMany delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 1 + } + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on findOneAndDelete findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on findOneAndDelete findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on findOneAndUpdate findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on findOneAndUpdate findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on findOneAndReplace findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "closeConnection": true + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on findOneAndReplace findAndModify", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "findAndModify" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "replacement": { + "y": 1 + }, + "returnDocument": "Before" + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on bulkWrite insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on bulkWrite insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on bulkWrite update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "closeConnection": true + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on bulkWrite update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "update" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "updateOne": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 1 + } + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on bulkWrite delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "closeConnection": true + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on bulkWrite delete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "delete" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "session": "session0", + "requests": [ + { + "deleteOne": { + "filter": { + "_id": 1 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on find find", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on find find", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on countDocuments aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on countDocuments aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "countDocuments", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": {} + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on aggregate aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "closeConnection": true + } + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on aggregate aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "session": "session0", + "pipeline": [] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on distinct distinct", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "closeConnection": true + } + } + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session0", + "fieldName": "_id", + "filter": {} + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on distinct distinct", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "distinct" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "distinct", + "object": "collection0", + "arguments": { + "session": "session0", + "fieldName": "_id", + "filter": {} + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on runCommand insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "session": "session0", + "commandName": "insert", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on runCommand insert", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "session": "session0", + "commandName": "insert", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient connection error on clientBulkWrite bulkWrite", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "closeConnection": true + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "database0.collection0", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ] + }, + { + "description": "unpin after transient ShutdownInProgress error on clientBulkWrite bulkWrite", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "bulkWrite" + ], + "errorCode": 91 + } + } + } + }, + { + "name": "clientBulkWrite", + "object": "client0", + "arguments": { + "session": "session0", + "models": [ + { + "insertOne": { + "namespace": "database0.collection0", + "document": { + "_id": 8, + "x": 88 + } + } + } + ] + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "testRunner", + "name": "assertSessionUnpinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/mongos-pin-auto.yml b/source/transactions/tests/unified/mongos-pin-auto.yml new file mode 100644 index 0000000000..a80dd62031 --- /dev/null +++ b/source/transactions/tests/unified/mongos-pin-auto.yml @@ -0,0 +1,1706 @@ +# Autogenerated tests that transient errors in a transaction unpin the session. +# See mongos-pin-auto-tests.py + +description: mongos-pin-auto + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: "4.1.8" + # Note: tests utilize targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: "forbid" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + +tests: + - description: remain pinned after non-transient Interrupted error on insertOne + operations: + - &startTransaction + object: session0 + name: startTransaction + - &initialCommand + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 3 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } } + - object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "insert" ] + errorCode: 11601 + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"] + errorCodeName: Interrupted + - &assertSessionPinned + object: testRunner + name: assertSessionPinned + arguments: + session: *session0 + - &commitTransaction + object: *session0 + name: commitTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + + - description: 'unpin after transient error within a transaction' + operations: + - *startTransaction + - *initialCommand + - object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ "insert" ] + closeConnection: true + - object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + errorLabelsOmit: ["UnknownTransactionCommitResult"] + - &assertSessionUnpinned + object: testRunner + name: assertSessionUnpinned + arguments: + session: *session0 + - &abortTransaction + object: *session0 + name: abortTransaction + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: abortTransaction + databaseName: admin + outcome: &outcome + - collectionName: *collection_name + databaseName: *database_name + documents: *data + + # The rest of the tests in this file test every operation type against + # multiple types of transient errors (connection and error code). + + - description: remain pinned after non-transient Interrupted error on insertOne insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 11601 + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on insertMany insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 11601 + - name: insertMany + object: *collection0 + arguments: + session: *session0 + documents: [ { _id: 4 }, { _id: 5 } ] + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on updateOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 11601 + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on replaceOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 11601 + - name: replaceOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on updateMany update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 11601 + - name: updateMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + update: {$set: { z: 1 } } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on deleteOne delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 11601 + - name: deleteOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on deleteMany delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 11601 + - name: deleteMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on findOneAndDelete findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 11601 + - name: findOneAndDelete + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on findOneAndUpdate findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 11601 + - name: findOneAndUpdate + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on findOneAndReplace findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 11601 + - name: findOneAndReplace + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + returnDocument: Before + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on bulkWrite insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 11601 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - insertOne: + document: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on bulkWrite update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 11601 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on bulkWrite delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 11601 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - deleteOne: + filter: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on find find + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["find"] + errorCode: 11601 + - name: find + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on countDocuments aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + errorCode: 11601 + - name: countDocuments + object: *collection0 + arguments: + session: *session0 + filter: {} + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on aggregate aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + errorCode: 11601 + - name: aggregate + object: *collection0 + arguments: + session: *session0 + pipeline: [] + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on distinct distinct + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["distinct"] + errorCode: 11601 + - name: distinct + object: *collection0 + arguments: + session: *session0 + fieldName: _id + filter: {} + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on runCommand insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 11601 + - name: runCommand + object: *database0 + arguments: + session: *session0 + commandName: insert + command: + insert: *collection_name + documents: + - { _id : 1 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + + - description: remain pinned after non-transient Interrupted error on clientBulkWrite bulkWrite + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["bulkWrite"] + errorCode: 11601 + - name: clientBulkWrite + object: *client0 + arguments: + session: *session0 + models: + - insertOne: + namespace: database0.collection0 + document: { _id: 8, x: 88 } + expectError: + errorLabelsOmit: ["TransientTransactionError"] + - *assertSessionPinned + - *abortTransaction + outcome: *outcome + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0" + + - description: unpin after transient connection error on insertOne insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + closeConnection: true + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on insertOne insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 91 + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on insertMany insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + closeConnection: true + - name: insertMany + object: *collection0 + arguments: + session: *session0 + documents: [ { _id: 4 }, { _id: 5 } ] + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on insertMany insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 91 + - name: insertMany + object: *collection0 + arguments: + session: *session0 + documents: [ { _id: 4 }, { _id: 5 } ] + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on updateOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + closeConnection: true + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on updateOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 91 + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on replaceOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + closeConnection: true + - name: replaceOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on replaceOne update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 91 + - name: replaceOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on updateMany update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + closeConnection: true + - name: updateMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + update: {$set: { z: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on updateMany update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 91 + - name: updateMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + update: {$set: { z: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on deleteOne delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + closeConnection: true + - name: deleteOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on deleteOne delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 91 + - name: deleteOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on deleteMany delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + closeConnection: true + - name: deleteMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on deleteMany delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 91 + - name: deleteMany + object: *collection0 + arguments: + session: *session0 + filter: { _id: { $gte: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on findOneAndDelete findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + closeConnection: true + - name: findOneAndDelete + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on findOneAndDelete findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 91 + - name: findOneAndDelete + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on findOneAndUpdate findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + closeConnection: true + - name: findOneAndUpdate + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on findOneAndUpdate findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 91 + - name: findOneAndUpdate + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on findOneAndReplace findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + closeConnection: true + - name: findOneAndReplace + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + returnDocument: Before + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on findOneAndReplace findAndModify + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["findAndModify"] + errorCode: 91 + - name: findOneAndReplace + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + replacement: { y: 1 } + returnDocument: Before + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on bulkWrite insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + closeConnection: true + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - insertOne: + document: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on bulkWrite insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 91 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - insertOne: + document: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on bulkWrite update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + closeConnection: true + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on bulkWrite update + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["update"] + errorCode: 91 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - updateOne: + filter: { _id: 1 } + update: { $set: { x: 1 } } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on bulkWrite delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + closeConnection: true + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - deleteOne: + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on bulkWrite delete + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["delete"] + errorCode: 91 + - name: bulkWrite + object: *collection0 + arguments: + session: *session0 + requests: + - deleteOne: + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on find find + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["find"] + closeConnection: true + - name: find + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on find find + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["find"] + errorCode: 91 + - name: find + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on countDocuments aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + closeConnection: true + - name: countDocuments + object: *collection0 + arguments: + session: *session0 + filter: {} + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on countDocuments aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + errorCode: 91 + - name: countDocuments + object: *collection0 + arguments: + session: *session0 + filter: {} + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on aggregate aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + closeConnection: true + - name: aggregate + object: *collection0 + arguments: + session: *session0 + pipeline: [] + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on aggregate aggregate + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["aggregate"] + errorCode: 91 + - name: aggregate + object: *collection0 + arguments: + session: *session0 + pipeline: [] + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on distinct distinct + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["distinct"] + closeConnection: true + - name: distinct + object: *collection0 + arguments: + session: *session0 + fieldName: _id + filter: {} + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on distinct distinct + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["distinct"] + errorCode: 91 + - name: distinct + object: *collection0 + arguments: + session: *session0 + fieldName: _id + filter: {} + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on runCommand insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + closeConnection: true + - name: runCommand + object: *database0 + arguments: + session: *session0 + commandName: insert + command: + insert: *collection_name + documents: + - { _id : 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient ShutdownInProgress error on runCommand insert + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["insert"] + errorCode: 91 + - name: runCommand + object: *database0 + arguments: + session: *session0 + commandName: insert + command: + insert: *collection_name + documents: + - { _id : 1 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + + - description: unpin after transient connection error on clientBulkWrite bulkWrite + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["bulkWrite"] + closeConnection: true + - name: clientBulkWrite + object: *client0 + arguments: + session: *session0 + models: + - insertOne: + namespace: database0.collection0 + document: { _id: 8, x: 88 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0" + + - description: unpin after transient ShutdownInProgress error on clientBulkWrite bulkWrite + operations: + - *startTransaction + - *initialCommand + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: {times: 1} + data: + failCommands: ["bulkWrite"] + errorCode: 91 + - name: clientBulkWrite + object: *client0 + arguments: + session: *session0 + models: + - insertOne: + namespace: database0.collection0 + document: { _id: 8, x: 88 } + expectError: + errorLabelsContain: ["TransientTransactionError"] + - *assertSessionUnpinned + - *abortTransaction + outcome: *outcome + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0" + diff --git a/source/transactions/tests/unified/mongos-recovery-token-errorLabels.json b/source/transactions/tests/unified/mongos-recovery-token-errorLabels.json new file mode 100644 index 0000000000..13345c6a29 --- /dev/null +++ b/source/transactions/tests/unified/mongos-recovery-token-errorLabels.json @@ -0,0 +1,211 @@ +{ + "description": "mongos-recovery-token-errorLabels", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "serverless": "forbid", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction retry succeeds on new mongos", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/mongos-recovery-token-errorLabels.yml b/source/transactions/tests/unified/mongos-recovery-token-errorLabels.yml new file mode 100644 index 0000000000..e93c89b6d6 --- /dev/null +++ b/source/transactions/tests/unified/mongos-recovery-token-errorLabels.yml @@ -0,0 +1,136 @@ +description: mongos-recovery-token-errorLabels + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + # serverless proxy doesn't use recovery tokens + serverless: forbid + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'commitTransaction retry succeeds on new mongos' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # Enable the fail point only on the mongos that session0 is pinned to + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + # The client sees a retryable writeConcernError on the first + # commitTransaction due to the fail point but it actually succeeds on the + # server (SERVER-39346). The retry will succeed both on a new mongos and + # on the original. + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/mongos-recovery-token.json b/source/transactions/tests/unified/mongos-recovery-token.json new file mode 100644 index 0000000000..bb88aa16bd --- /dev/null +++ b/source/transactions/tests/unified/mongos-recovery-token.json @@ -0,0 +1,568 @@ +{ + "description": "mongos-recovery-token", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.1.8", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction explicit retries include recoveryToken", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction retry fails on new mongos", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": true, + "uriOptions": { + "heartbeatFrequencyMS": 30000, + "appName": "transactionsClient" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 7 + }, + "data": { + "failCommands": [ + "commitTransaction", + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "transactionsClient" + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ], + "errorCodeName": "NoSuchTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction sends recoveryToken", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/mongos-recovery-token.yml b/source/transactions/tests/unified/mongos-recovery-token.yml new file mode 100644 index 0000000000..9eed826ec5 --- /dev/null +++ b/source/transactions/tests/unified/mongos-recovery-token.yml @@ -0,0 +1,353 @@ +description: mongos-recovery-token + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: 4.1.8 + # serverless proxy doesn't use recovery tokens + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'commitTransaction explicit retries include recoveryToken' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: commitTransaction + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction retry fails on new mongos' + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + runOnRequirements: + - topologies: [ sharded ] + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: true + uriOptions: + # Increase heartbeatFrequencyMS to avoid the race condition where an in + # flight heartbeat refreshes the first mongoes' SDAM state in between + # the initial commitTransaction and the retry attempt. + heartbeatFrequencyMS: 30000 + appName: &appName transactionsClient + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # Enable the fail point only on the Mongos that session0 is pinned to. + # Fail hello/legacy hello to prevent the heartbeat requested directly after the + # retryable commit error from racing with server selection for the retry. + # Note: times: 7 is slightly artbitrary but it accounts for one failed + # commit and some SDAM heartbeats. A test runner will have multiple + # clients connected to this server so this fail point configuration + # is also racy. + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session1 + failPoint: + configureFailPoint: failCommand + mode: { times: 7 } + data: + failCommands: + - commitTransaction + - isMaster + - hello + closeConnection: true + appName: *appName + # The first commitTransaction sees a retryable connection error due to + # the fail point and also fails on the server. The retry attempt on a + # new mongos will wait for the transaction to timeout and will fail + # because the transaction was aborted. Note that the retry attempt should + # not select the original mongos because that server's SDAM state is + # reset by the connection error, heartbeatFrequencyMS is high, and + # subsequent heartbeats should fail. + - + object: *session1 + name: commitTransaction + expectError: + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + errorCodeName: NoSuchTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction sends recoveryToken' + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + runOnRequirements: + - topologies: [ sharded ] + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # Enable the fail point only on the mongos that session0 is pinned to + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + closeConnection: true + # The first abortTransaction sees a retryable connection error due to + # the fail point. The retry attempt on a new mongos will send the + # recoveryToken. Note that the retry attempt will also fail because the + # server does not yet support aborting from a new mongos, however this + # operation should "succeed" since abortTransaction ignores errors. + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/mongos-unpin.json b/source/transactions/tests/unified/mongos-unpin.json new file mode 100644 index 0000000000..4d1ebc87bc --- /dev/null +++ b/source/transactions/tests/unified/mongos-unpin.json @@ -0,0 +1,450 @@ +{ + "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", + "topologies": [ + "sharded" + ] + } + ], + "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", + "topologies": [ + "sharded" + ] + } + ], + "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", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "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/source/transactions/tests/unified/mongos-unpin.yml b/source/transactions/tests/unified/mongos-unpin.yml new file mode 100644 index 0000000000..44844a1858 --- /dev/null +++ b/source/transactions/tests/unified/mongos-unpin.yml @@ -0,0 +1,185 @@ +description: mongos-unpin + +schemaVersion: '1.4' + +runOnRequirements: + - minServerVersion: '4.2' + topologies: [ sharded ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name mongos-unpin-db + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +_yamlAnchors: + anchors: + # LockTimeout will cause the server to add a TransientTransactionError label. It is not retryable. + &lockTimeoutErrorCode 24 + +tests: + - description: unpin after TransientTransactionError error on commit + runOnRequirements: + - + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: "forbid" + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + operations: + - &startTransaction + name: startTransaction + object: *session0 + - &insertOne + 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: *lockTimeoutErrorCode + - name: commitTransaction + object: *session0 + expectError: + # LockTimeout is not retryable, so the commit fails. + errorCode: *lockTimeoutErrorCode + errorLabelsContain: [ TransientTransactionError ] + errorLabelsOmit: [ UnknownTransactionCommitResult ] + - &assertNoPinnedServer + name: assertSessionUnpinned + object: testRunner + arguments: + session: *session0 + # Cleanup the potentionally open server transaction by starting and + # aborting a new transaction on the same session. + - *startTransaction + - *insertOne + - &abortTransaction + name: abortTransaction + object: *session0 + + - description: unpin on successful abort + operations: + - *startTransaction + - *insertOne + - *abortTransaction + - *assertNoPinnedServer + + - description: unpin after non-transient error on abort + runOnRequirements: + - + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: "forbid" + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + operations: + - *startTransaction + - *insertOne + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ abortTransaction ] + errorCode: *lockTimeoutErrorCode + - *abortTransaction + - *assertNoPinnedServer + # Cleanup the potentionally open server transaction by starting and + # aborting a new transaction on the same session. + - *startTransaction + - *insertOne + - *abortTransaction + + - description: unpin after TransientTransactionError error on abort + runOnRequirements: + - + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + topologies: [ sharded ] + operations: + - *startTransaction + - *insertOne + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ abortTransaction ] + errorCode: 91 # ShutdownInProgress + - *abortTransaction + - *assertNoPinnedServer + # Cleanup the potentionally open server transaction by starting and + # aborting a new transaction on the same session. + - *startTransaction + - *insertOne + - *abortTransaction + + - description: unpin when a new transaction is started + operations: + - *startTransaction + - *insertOne + - name: commitTransaction + object: *session0 + - &assertPinnedServer + name: assertSessionPinned + object: testRunner + arguments: + session: *session0 + - *startTransaction + - *assertNoPinnedServer + + - description: unpin when a non-transaction write operation uses a session + operations: + - *startTransaction + - *insertOne + - name: commitTransaction + object: *session0 + - *assertPinnedServer + - *insertOne + - *assertNoPinnedServer + + - description: unpin when a non-transaction read operation uses a session + operations: + - *startTransaction + - *insertOne + - name: commitTransaction + object: *session0 + - *assertPinnedServer + - name: find + object: *collection0 + arguments: + filter: { x: 1 } + session: *session0 + - *assertNoPinnedServer diff --git a/source/transactions/tests/unified/pin-mongos.json b/source/transactions/tests/unified/pin-mongos.json new file mode 100644 index 0000000000..c96f3f341f --- /dev/null +++ b/source/transactions/tests/unified/pin-mongos.json @@ -0,0 +1,1466 @@ +{ + "description": "pin-mongos", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.1.8", + "serverless": "forbid", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "countDocuments", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "distinct", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2 + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "find", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "filter": { + "_id": 2 + }, + "session": "session0" + }, + "expectResult": [ + { + "_id": 2 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "insertOne", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 4 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 5 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 5 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 6 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 6 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 7 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 7 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 8 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 8 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 9 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 9 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 10 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 10 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + }, + { + "_id": 6 + }, + { + "_id": 7 + }, + { + "_id": 8 + }, + { + "_id": 9 + }, + { + "_id": 10 + } + ] + } + ] + }, + { + "description": "mixed read write operations", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": 3 + }, + "session": "session0" + }, + "expectResult": 1 + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 4 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 4 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 5 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 5 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 6 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 6 + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "document": { + "_id": 7 + }, + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 7 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + }, + { + "_id": 5 + }, + { + "_id": 6 + }, + { + "_id": 7 + } + ] + } + ] + }, + { + "description": "multiple commits", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "1": 4 + } + } + } + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "remain pinned after non-transient error on commit", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "1": 4 + } + } + } + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 51 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError" + ], + "errorCode": 51 + } + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "testRunner", + "name": "assertSessionPinned", + "arguments": { + "session": "session0" + } + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "unpin after transient error within a transaction", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unpin after transient error within a transaction and commit", + "runOnRequirements": [ + { + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": true, + "uriOptions": { + "heartbeatFrequencyMS": 30000, + "appName": "transactionsClient" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "testRunner", + "name": "targetedFailPoint", + "arguments": { + "session": "session1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 7 + }, + "data": { + "failCommands": [ + "insert", + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "transactionsClient" + } + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ], + "errorCodeName": "NoSuchTransaction" + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$exists": true + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/pin-mongos.yml b/source/transactions/tests/unified/pin-mongos.yml new file mode 100644 index 0000000000..4869ac3c16 --- /dev/null +++ b/source/transactions/tests/unified/pin-mongos.yml @@ -0,0 +1,660 @@ +# Test that all the operations go to the same mongos. +# +# In tests that don't include command-started events the assertion is implicit: +# that all the read operations succeed. If the driver does not properly pin to +# a single mongos then one of the operations in a transaction will eventually +# be sent to a different mongos, which is unaware of the transaction, and the +# mongos will return a command error. An example of such an error is: +# { +# 'ok': 0.0, +# 'errmsg': 'cannot continue txnId -1 for session 28938f50-9d29-4ca5-8de5-ddaf261267c4 - 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= with txnId 1', +# 'code': 251, +# 'codeName': 'NoSuchTransaction', +# 'errorLabels': ['TransientTransactionError'] +# } +description: pin-mongos + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: 4.1.8 + # serverless proxy doesn't append error labels to errors in transactions + # caused by failpoints (CLOUDP-88216) + serverless: forbid + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + +tests: + - + description: countDocuments + operations: + - &startTransaction + object: *session0 + name: startTransaction + - &countDocuments + object: *collection0 + name: countDocuments + arguments: + filter: { _id: 2 } + session: *session0 + expectResult: 1 + - *countDocuments + - *countDocuments + - *countDocuments + - *countDocuments + - *countDocuments + - *countDocuments + - *countDocuments + - &commitTransaction + object: *session0 + name: commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: distinct + operations: + - *startTransaction + - &distinct + object: *collection0 + name: distinct + arguments: + fieldName: _id + filter: {} + session: *session0 + expectResult: [ 1, 2 ] + - *distinct + - *distinct + - *distinct + - *distinct + - *distinct + - *distinct + - *distinct + - *commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: find + operations: + - + object: *session0 + name: startTransaction + - &find + object: *collection0 + name: find + arguments: + filter: { _id: 2 } + session: *session0 + expectResult: + - { _id: 2 } + - *find + - *find + - *find + - *find + - *find + - *find + - *find + - *commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: insertOne + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 5 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 5 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 6 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 6 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 7 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 7 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 8 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 8 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 9 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 9 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 10 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 10 } } + - *commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + - { _id: 6 } + - { _id: 7 } + - { _id: 8 } + - { _id: 9 } + - { _id: 10 } + - + description: 'mixed read write operations' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 3 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + - &countDocuments + object: *collection0 + name: countDocuments + arguments: + filter: { _id: 3 } + session: *session0 + expectResult: 1 + - *countDocuments + - *countDocuments + - *countDocuments + - *countDocuments + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 4 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 5 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 5 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 6 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 6 } } + - + object: *collection0 + name: insertOne + arguments: + document: { _id: 7 } + session: *session0 + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 7 } } + - *commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - { _id: 5 } + - { _id: 6 } + - { _id: 7 } + - + description: 'multiple commits' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 3 + '1': 4 + # Session is pinned and remains pinned after successful commits + - &assertSessionPinned + object: testRunner + name: assertSessionPinned + arguments: + session: *session0 + - *commitTransaction + - *assertSessionPinned + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *commitTransaction + - *assertSessionPinned + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - + description: 'remain pinned after non-transient error on commit' + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + runOnRequirements: + - topologies: [ sharded ] + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 3 + '1': 4 + - *assertSessionPinned + # Fail the commit with a non-transient error + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 51 # ManualInterventionRequired + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsOmit: + - TransientTransactionError + errorCode: 51 + - *assertSessionPinned + # The next commit should succeed + - + object: *session0 + name: commitTransaction + - *assertSessionPinned + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + - + description: 'unpin after transient error within a transaction' + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + runOnRequirements: + - topologies: [ sharded ] + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 3 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + # Enable the fail point only on the mongos that session0 is pinned to + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + closeConnection: true + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + # Session unpins from the first mongos after the insert error and + # abortTransaction succeeds immediately on any mongos. + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + # Applications should not run commitTransaction after transient errors but + # the transactions API allows it and this test confirms unpinning behavior. + # In a sharded cluster, a transient error within a transaction unpins the + # session. This way a subsequent abort can "succeed" immediately instead of + # blocking for serverSelectionTimeoutMS in the case the mongos went down. + # However since the abortTransaction helper ignores errors, this test uses + # commitTransaction to prove the session was unpinned. + - + description: 'unpin after transient error within a transaction and commit' + # Note: test utilizes targetedFailPoint, which is incompatible with + # load-balanced and useMultipleMongoses:true + runOnRequirements: + - topologies: [ sharded ] + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: true + uriOptions: + # Increase heartbeatFrequencyMS to avoid the race condition where an in + # flight heartbeat refreshes the first mongoes' SDAM state in between + # the insert connection error and the single commit attempt. + heartbeatFrequencyMS: 30000 + appName: &appName transactionsClient + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 3 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + # Enable the fail point only on the Mongos that session0 is pinned to. + # Fail hello/legacy hello to prevent the heartbeat requested directly after the + # insert error from racing with server selection for the commit. + # Note: times: 7 is slightly artbitrary but it accounts for one failed + # insert and some SDAM heartbeats. A test runner will have multiple + # clients connected to this server so this fail point configuration + # is also racy. + - + object: testRunner + name: targetedFailPoint + arguments: + session: *session1 + failPoint: + configureFailPoint: failCommand + mode: { times: 7 } + data: + failCommands: + - insert + - isMaster + - hello + closeConnection: true + appName: *appName + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 4 } + expectError: + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + # Session unpins from the first mongos after the insert error and + # commitTransaction selects the second mongos which is unaware of the + # transaction and therefore fails with NoSuchTransaction error. If this + # commit succeeds it indicates a bug, either: + # - the driver mistakenly remained pinned even after the insert error, or + # - the test client was initialized with a single mongos seed + # + # Note that the commit attempt should not select the original mongos + # because that server's SDAM state is reset by the connection error, + # heartbeatFrequencyMS is high, and subsequent heartbeats + # should fail. + - + object: *session1 + name: commitTransaction + expectError: + errorLabelsContain: + - TransientTransactionError + errorLabelsOmit: + - UnknownTransactionCommitResult + errorCodeName: NoSuchTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + recoveryToken: { $$exists: true } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/transactions/tests/unified/read-concern.json b/source/transactions/tests/unified/read-concern.json new file mode 100644 index 0000000000..b3bd967c09 --- /dev/null +++ b/source/transactions/tests/unified/read-concern.json @@ -0,0 +1,1924 @@ +{ + "description": "read-concern", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "database": { + "id": "database_rc_majority", + "client": "client0", + "databaseName": "transaction-tests", + "databaseOptions": { + "readConcern": { + "level": "majority" + } + } + } + }, + { + "collection": { + "id": "collection_rc_majority", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "readConcern": { + "level": "majority" + } + } + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ], + "tests": [ + { + "description": "only first countDocuments includes readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + } + } + }, + { + "object": "collection_rc_majority", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": { + "$gte": 2 + } + }, + "session": "session0" + }, + "expectResult": 3 + }, + { + "object": "collection_rc_majority", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": { + "$gte": 2 + } + }, + "session": "session0" + }, + "expectResult": 3 + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": { + "$gte": 2 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "cursor": {}, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "level": "majority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": { + "$gte": 2 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "cursor": {}, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "only first find includes readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + } + } + }, + { + "object": "collection_rc_majority", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rc_majority", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "level": "majority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "only first aggregate includes readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + } + } + }, + { + "object": "collection_rc_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rc_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "level": "majority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "only first distinct includes readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + } + } + }, + { + "object": "collection_rc_majority", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2, + 3, + 4 + ] + }, + { + "object": "collection_rc_majority", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2, + 3, + 4 + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "level": "majority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "only first runCommand includes readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + } + } + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "commandName": "find" + } + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "commandName": "find" + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "level": "majority" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "countDocuments ignores collection readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_rc_majority", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": { + "$gte": 2 + } + }, + "session": "session0" + }, + "expectResult": 3 + }, + { + "object": "collection_rc_majority", + "name": "countDocuments", + "arguments": { + "filter": { + "_id": { + "$gte": 2 + } + }, + "session": "session0" + }, + "expectResult": 3 + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": { + "$gte": 2 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "cursor": {}, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$match": { + "_id": { + "$gte": 2 + } + } + }, + { + "$group": { + "_id": 1, + "n": { + "$sum": 1 + } + } + } + ], + "cursor": {}, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "find ignores collection readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_rc_majority", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rc_majority", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "aggregate ignores collection readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_rc_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rc_majority", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "distinct ignores collection readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_rc_majority", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2, + 3, + 4 + ] + }, + { + "object": "collection_rc_majority", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2, + 3, + 4 + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "runCommand ignores database readConcern", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database_rc_majority", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "commandName": "find" + } + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "commandName": "find" + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/read-concern.yml b/source/transactions/tests/unified/read-concern.yml new file mode 100644 index 0000000000..3eff32a0dc --- /dev/null +++ b/source/transactions/tests/unified/read-concern.yml @@ -0,0 +1,634 @@ +description: read-concern + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + database: + id: &database_rc_majority database_rc_majority + client: *client0 + databaseName: &database_name transaction-tests + databaseOptions: + readConcern: { level: majority } + - + collection: + id: &collection_rc_majority collection_rc_majority + database: *database0 + collectionName: &collection_name test + collectionOptions: + readConcern: { level: majority } + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + +tests: + - + description: 'only first countDocuments includes readConcern' + operations: + - &startTransaction + object: *session0 + name: startTransaction + arguments: + readConcern: + level: majority + - &countDocuments + object: *collection_rc_majority + name: countDocuments + arguments: + filter: { _id: { $gte: 2 } } + session: *session0 + expectResult: 3 + - *countDocuments + - &commitTransaction + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $match: { _id: { $gte: 2 } } } + - { $group: { _id: 1, n: { $sum: 1 } } } + cursor: { } + lsid: { $$sessionLsid: *session0 } + readConcern: + level: majority + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $match: { _id: { $gte: 2 } } } + - { $group: { _id: 1, n: { $sum: 1 } } } + cursor: { } + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - &commitTransactionEvent + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: 'only first find includes readConcern' + operations: + - *startTransaction + - &find + object: *collection_rc_majority + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectResult: *data + - *find + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: + level: majority + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - *commitTransactionEvent + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: 'only first aggregate includes readConcern' + operations: + - *startTransaction + - &aggregate + object: *collection_rc_majority + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectResult: *data + - *aggregate + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: + level: majority + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'only first distinct includes readConcern' + operations: + - *startTransaction + - &distinct + object: *collection_rc_majority + name: distinct + arguments: + fieldName: _id + filter: {} + session: *session0 + expectResult: [ 1, 2, 3, 4 ] + - *distinct + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: + level: majority + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: distinct + databaseName: *database_name + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: distinct + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'only first runCommand includes readConcern' + operations: + - *startTransaction + - &runCommand + object: *database0 + name: runCommand + arguments: + session: *session0 + command: + find: *collection_name + commandName: find + - *runCommand + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + lsid: { $$sessionLsid: *session0 } + readConcern: + level: majority + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: find + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'countDocuments ignores collection readConcern' + operations: + - &startTransactionNoReadConcern + object: *session0 + name: startTransaction + - *countDocuments + - *countDocuments + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $match: { _id: { $gte: 2 } } } + - { $group: { _id: 1, n: { $sum: 1 } } } + cursor: { } + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $match: { _id: { $gte: 2 } } } + - { $group: { _id: 1, n: { $sum: 1 } } } + cursor: { } + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'find ignores collection readConcern' + operations: + - *startTransactionNoReadConcern + - *find + - *find + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'aggregate ignores collection readConcern' + operations: + - *startTransactionNoReadConcern + - *aggregate + - *aggregate + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'distinct ignores collection readConcern' + operations: + - *startTransactionNoReadConcern + - *distinct + - *distinct + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: distinct + databaseName: *database_name + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: distinct + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome + - + description: 'runCommand ignores database readConcern' + operations: + - *startTransactionNoReadConcern + - + object: *database_rc_majority + name: runCommand + arguments: + session: *session0 + command: + find: *collection_name + commandName: find + - *runCommand + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: find + databaseName: *database_name + - *commitTransactionEvent + outcome: *outcome \ No newline at end of file diff --git a/source/transactions/tests/unified/read-pref.json b/source/transactions/tests/unified/read-pref.json new file mode 100644 index 0000000000..eda00bd10d --- /dev/null +++ b/source/transactions/tests/unified/read-pref.json @@ -0,0 +1,728 @@ +{ + "description": "read-pref", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "collection": { + "id": "collection_rp_primary", + "database": "database0", + "collectionName": "test" + } + }, + { + "collection": { + "id": "collection_rp_secondary", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "default readPreference", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2, + "2": 3, + "3": 4 + } + } + } + } + }, + { + "object": "collection_rp_secondary", + "name": "aggregate", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + }, + { + "$count": "count" + } + ] + }, + "expectResult": [ + { + "count": 1 + } + ] + }, + { + "object": "collection_rp_secondary", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rp_secondary", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "primary readPreference", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "primary" + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2, + "2": 3, + "3": 4 + } + } + } + } + }, + { + "object": "collection_rp_secondary", + "name": "aggregate", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + }, + { + "$count": "count" + } + ] + }, + "expectResult": [ + { + "count": 1 + } + ] + }, + { + "object": "collection_rp_secondary", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection_rp_secondary", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "secondary readPreference", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "secondary" + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2, + "2": 3, + "3": 4 + } + } + } + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + }, + { + "$count": "count" + } + ] + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "primaryPreferred readPreference", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "primaryPreferred" + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2, + "2": 3, + "3": 4 + } + } + } + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + }, + { + "$count": "count" + } + ] + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "nearest readPreference", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "nearest" + } + } + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2, + "2": 3, + "3": 4 + } + } + } + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "session": "session0", + "pipeline": [ + { + "$match": { + "_id": 1 + } + }, + { + "$count": "count" + } + ] + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "collection_rp_primary", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "secondary write only", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "secondary" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/read-pref.yml b/source/transactions/tests/unified/read-pref.yml new file mode 100644 index 0000000000..5217fea471 --- /dev/null +++ b/source/transactions/tests/unified/read-pref.yml @@ -0,0 +1,394 @@ +# This test doesn't check contents of command-started events. +description: read-pref + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + collection: + id: &collection_rp_primary collection_rp_primary + database: *database0 + collectionName: *collection_name + - + collection: + id: &collection_rp_secondary collection_rp_secondary + database: *database0 + collectionName: *collection_name + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'default readPreference' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertMany + arguments: + documents: &insertedDocs + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + '2': 3 + '3': 4 + - + object: *collection_rp_secondary + name: aggregate + arguments: + session: *session0 + pipeline: + - { $match: { _id: 1 } } + - { $count: count } + expectResult: + - { count: 1 } + - + object: *collection_rp_secondary + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectResult: *insertedDocs + - + object: *collection_rp_secondary + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectResult: *insertedDocs + - + object: *session0 + name: commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *insertedDocs + - + description: 'primary readPreference' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: primary + - + object: *collection0 + name: insertMany + arguments: + documents: &insertedDocs + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + '2': 3 + '3': 4 + - + object: *collection_rp_secondary + name: aggregate + arguments: + session: *session0 + pipeline: + - { $match: { _id: 1 } } + - { $count: count } + expectResult: + - { count: 1 } + - + object: *collection_rp_secondary + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectResult: *insertedDocs + - + object: *collection_rp_secondary + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectResult: *insertedDocs + - + object: *session0 + name: commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *insertedDocs + - + description: 'secondary readPreference' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: secondary + - + object: *collection0 + name: insertMany + arguments: + documents: &insertedDocs + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + '2': 3 + '3': 4 + - + object: *collection_rp_primary + name: aggregate + arguments: + session: *session0 + pipeline: + - { $match: { _id: 1 } } + - { $count: count } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session0 + name: abortTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'primaryPreferred readPreference' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: primaryPreferred + - + object: *collection0 + name: insertMany + arguments: + documents: &insertedDocs + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + '2': 3 + '3': 4 + - + object: *collection_rp_primary + name: aggregate + arguments: + session: *session0 + pipeline: + - { $match: { _id: 1 } } + - { $count: count } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session0 + name: abortTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'nearest readPreference' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: nearest + - + object: *collection0 + name: insertMany + arguments: + documents: &insertedDocs + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + '2': 3 + '3': 4 + - + object: *collection_rp_primary + name: aggregate + arguments: + session: *session0 + pipeline: + - { $match: { _id: 1 } } + - { $count: count } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *collection_rp_primary + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session0 + name: abortTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'secondary write only' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: secondary + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/reads.json b/source/transactions/tests/unified/reads.json new file mode 100644 index 0000000000..52e8457634 --- /dev/null +++ b/source/transactions/tests/unified/reads.json @@ -0,0 +1,706 @@ +{ + "description": "reads", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ], + "tests": [ + { + "description": "collection readConcern without transaction", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "readConcern": { + "level": "majority" + } + } + } + } + ] + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "readConcern": { + "level": "majority" + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + } + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "find", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection0", + "name": "find", + "arguments": { + "batchSize": 3, + "filter": {}, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "find", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "aggregate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "collection0", + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "batchSize": 3, + "session": "session0" + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "pipeline": [ + { + "$project": { + "_id": 1 + } + } + ], + "cursor": { + "batchSize": 3 + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "aggregate", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "test", + "batchSize": 3, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false + }, + "commandName": "getMore", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + }, + { + "description": "distinct", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "distinct", + "arguments": { + "fieldName": "_id", + "filter": {}, + "session": "session0" + }, + "expectResult": [ + 1, + 2, + 3, + 4 + ] + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "test", + "key": "_id", + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "distinct", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "readConcern": { + "$$exists": false + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + }, + { + "_id": 4 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/reads.yml b/source/transactions/tests/unified/reads.yml new file mode 100644 index 0000000000..66d7e2f79d --- /dev/null +++ b/source/transactions/tests/unified/reads.yml @@ -0,0 +1,298 @@ +description: reads + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + - { _id: 4 } + +tests: + - + description: 'collection readConcern without transaction' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: *collection_name + collectionOptions: + readConcern: { level: majority } + - + object: *collection1 + name: find + arguments: + filter: {} + session: *session0 + expectResult: *data + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + readConcern: + level: majority + lsid: { $$sessionLsid: *session0 } + txnNumber: { $$exists: false } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + commandName: find + databaseName: *database_name + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: find + operations: + - &startTransaction + object: *session0 + name: startTransaction + - &find + object: *collection0 + name: find + arguments: + batchSize: 3 + filter: {} + session: *session0 + expectResult: *data + - *find + - &commitTransaction + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + find: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: find + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: aggregate + operations: + - *startTransaction + - &aggregate + object: *collection0 + name: aggregate + arguments: + pipeline: + - { $project: { _id: 1 } } + batchSize: 3 + session: *session0 + expectResult: *data + - *aggregate + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + aggregate: *collection_name + pipeline: + - { $project: { _id: 1 } } + cursor: + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: aggregate + databaseName: *database_name + - + commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection_name + batchSize: 3 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + commandName: getMore + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: distinct + operations: + - *startTransaction + - + object: *collection0 + name: distinct + arguments: + fieldName: _id + filter: {} + session: *session0 + expectResult: [ 1, 2, 3, 4] + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + distinct: *collection_name + key: _id + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: distinct + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + readConcern: { $$exists: false } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome \ No newline at end of file diff --git a/source/transactions/tests/unified/retryable-abort-errorLabels.json b/source/transactions/tests/unified/retryable-abort-errorLabels.json new file mode 100644 index 0000000000..77a1b03eb0 --- /dev/null +++ b/source/transactions/tests/unified/retryable-abort-errorLabels.json @@ -0,0 +1,2436 @@ +{ + "description": "retryable-abort-errorLabels", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "abortTransaction only retries once with RetryableWriteError from server", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction does not retry without RetryableWriteError label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 10107, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 13436, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 13435, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 11602, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 11600, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 91, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 6, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 9001, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after WriteConcernError InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11600, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11602, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after WriteConcernError PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 189, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/retryable-abort-errorLabels.yml b/source/transactions/tests/unified/retryable-abort-errorLabels.yml new file mode 100644 index 0000000000..8a4ddf994a --- /dev/null +++ b/source/transactions/tests/unified/retryable-abort-errorLabels.yml @@ -0,0 +1,1381 @@ +description: retryable-abort-errorLabels + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'abortTransaction only retries once with RetryableWriteError from server' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - abortTransaction + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + # Driver retries abort once + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction does not retry without RetryableWriteError label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + # Override server behavior: do not send RetryableWriteError label with retryable code + errorLabels: [] + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + # Driver does not retry abort + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 10107 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 13436 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 13435 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 11602 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 11600 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 189 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 91 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 7 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 6 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 9001 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 89 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after WriteConcernError InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11600 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11602 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after WriteConcernError PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 189 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after WriteConcernError ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/retryable-abort-handshake.json b/source/transactions/tests/unified/retryable-abort-handshake.json new file mode 100644 index 0000000000..4ad56e2f2f --- /dev/null +++ b/source/transactions/tests/unified/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/source/transactions/tests/unified/retryable-abort-handshake.yml b/source/transactions/tests/unified/retryable-abort-handshake.yml new file mode 100644 index 0000000000..2d91b75a99 --- /dev/null +++ b/source/transactions/tests/unified/retryable-abort-handshake.yml @@ -0,0 +1,117 @@ +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 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent, connectionCheckOutStartedEvent] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-handshake-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - session: + # This session will be used to execute the transaction + id: &session0 session0 + client: *client0 + - session: + # This session will be used to create the failPoint, and empty the pool + id: &session1 session1 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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 } + + # The following failPoint and ping utilize session1 so that + # the transaction won't be failed by the intentional erroring of ping + # and it will have an empty pool when it goes to run abortTransaction + - name: failPoint # fail the next connection establishment + object: testRunner + arguments: + client: *client0 + session: *session1 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + # use saslContinue here to avoid SDAM errors + # this failPoint itself will create a usable connection in the connection pool + # so we run a ping (with closeConnection: true) in order to discard the connection + # before testing that abortTransaction will fail a handshake but will get retried + 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: {} } # startTransaction + - { connectionCheckOutStartedEvent: {} } # insertOne + - { connectionCheckOutStartedEvent: {} } # failPoint + - { connectionCheckOutStartedEvent: {} } # abortTransaction + - { connectionCheckOutStartedEvent: {} } # abortTransaction retry + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [{ _id: 2, x: 22 }] + startTransaction: true + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + ping: 1 + databaseName: *databaseName + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + commandName: abortTransaction + databaseName: admin + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } diff --git a/source/transactions/tests/unified/retryable-abort.json b/source/transactions/tests/unified/retryable-abort.json new file mode 100644 index 0000000000..381cfa91f8 --- /dev/null +++ b/source/transactions/tests/unified/retryable-abort.json @@ -0,0 +1,600 @@ +{ + "description": "retryable-abort", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "abortTransaction only performs a single retry", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction does not retry after Interrupted", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "errorCode": 11601, + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction does not retry after WriteConcernError Interrupted", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "writeConcernError": { + "code": 11601, + "errmsg": "operation was interrupted" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "abortTransaction succeeds after connection error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "abortTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/retryable-abort.yml b/source/transactions/tests/unified/retryable-abort.yml new file mode 100644 index 0000000000..602c92aec0 --- /dev/null +++ b/source/transactions/tests/unified/retryable-abort.yml @@ -0,0 +1,352 @@ +description: retryable-abort + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + # Define a second set of entities for a retryWrites=false client + - + client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - + database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + session: + id: &session1 session1 + client: *client1 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'abortTransaction only performs a single retry' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - abortTransaction + closeConnection: true + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction does not retry after Interrupted' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + errorCode: 11601 + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction does not retry after WriteConcernError Interrupted' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + writeConcernError: + code: 11601 + errmsg: 'operation was interrupted' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'abortTransaction succeeds after connection error' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - abortTransaction + closeConnection: true + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/retryable-commit-errorLabels.json b/source/transactions/tests/unified/retryable-commit-errorLabels.json new file mode 100644 index 0000000000..d3ce8b148e --- /dev/null +++ b/source/transactions/tests/unified/retryable-commit-errorLabels.json @@ -0,0 +1,2564 @@ +{ + "description": "retryable-commit-errorLabels", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction does not retry error without RetryableWriteError label", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 11600, + "errorLabels": [] + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError" + ] + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "commitTransaction retries once with RetryableWriteError from server", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 112, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after NotWritablePrimary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 10107, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after NotPrimaryOrSecondary", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 13436, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after NotPrimaryNoSecondaryOk", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 13435, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 11602, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after HostNotFound", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 7, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after HostUnreachable", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 6, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after SocketException", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 9001, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after NetworkTimeout", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 89, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after WriteConcernError InterruptedAtShutdown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11600, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 11602, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after WriteConcernError PrimarySteppedDown", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 189, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after WriteConcernError ShutdownInProgress", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after InterruptedAtShutdown", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 11600, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after ShutdownInProgress", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 91, + "errorLabels": [ + "RetryableWriteError" + ], + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/retryable-commit-errorLabels.yml b/source/transactions/tests/unified/retryable-commit-errorLabels.yml new file mode 100644 index 0000000000..a29f565160 --- /dev/null +++ b/source/transactions/tests/unified/retryable-commit-errorLabels.yml @@ -0,0 +1,1474 @@ +description: retryable-commit-errorLabels + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: 4.3.1 # failCommand errorLabels option + topologies: + - replicaset + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + # Define a second set of entities for a retryWrites=false client + - + client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - + database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + session: + id: &session1 session1 + client: *client1 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'commitTransaction does not retry error without RetryableWriteError label' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 11600 # InterruptedAtShutdown, normally a retryable error code + errorLabels: [] # Override server behavior: do not send RetryableWriteError label with retryable code + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + expectError: + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + # Driver does not retry commit because there was no RetryableWriteError label on response + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'commitTransaction retries once with RetryableWriteError from server' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 112 # WriteConflict, not a retryable error code + # Override server behavior: send RetryableWriteError label with non-retryable error code + errorLabels: + - RetryableWriteError + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + # Driver retries commit and it succeeds + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after NotWritablePrimary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 10107 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after NotPrimaryOrSecondary' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 13436 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after NotPrimaryNoSecondaryOk' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 13435 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 11602 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 189 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after HostNotFound' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 7 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after HostUnreachable' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 6 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after SocketException' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 9001 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after NetworkTimeout' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 89 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after WriteConcernError InterruptedAtShutdown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11600 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 11602 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after WriteConcernError PrimarySteppedDown' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 189 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after WriteConcernError ShutdownInProgress' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorLabels: + - RetryableWriteError + writeConcernError: + code: 91 + errmsg: 'Replication is being shut down' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after InterruptedAtShutdown' + # Fail points using state change errors (e.g. InterruptedAtShutdown, + # ShutdownInProgress) may remain enabled indefinitely (CLOUDP-84573). + runOnRequirements: + - serverless: forbid + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 11600 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after ShutdownInProgress' + # Fail points using state change errors (e.g. InterruptedAtShutdown, + # ShutdownInProgress) may remain enabled indefinitely (CLOUDP-84573). + runOnRequirements: + - serverless: forbid + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 91 + errorLabels: + - RetryableWriteError + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/retryable-commit-handshake.json b/source/transactions/tests/unified/retryable-commit-handshake.json new file mode 100644 index 0000000000..d9315a8fc6 --- /dev/null +++ b/source/transactions/tests/unified/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/source/transactions/tests/unified/retryable-commit-handshake.yml b/source/transactions/tests/unified/retryable-commit-handshake.yml new file mode 100644 index 0000000000..6a03ba6b6d --- /dev/null +++ b/source/transactions/tests/unified/retryable-commit-handshake.yml @@ -0,0 +1,117 @@ +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 client0 + useMultipleMongoses: false + observeEvents: [commandStartedEvent, connectionCheckOutStartedEvent] + uriOptions: { retryWrites: false } # commitTransaction is retryable regardless of this option being set + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-handshake-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - session: + id: &session0 session0 + client: *client0 + - session: + id: &session1 session1 + client: *client0 + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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 } + + # The following failPoint and ping utilize session1 so that + # the transaction won't be failed by the intentional erroring of ping + # and it will have an empty pool when it goes to run commitTransaction + - name: failPoint # fail the next connection establishment + object: testRunner + arguments: + client: *client0 + session: *session1 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + # use saslContinue here to avoid SDAM errors + # this failPoint itself will create a usable connection in the connection pool + # so we run a ping (that also fails) in order to discard the connection + # before testing that commitTransaction gets retried + 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: {} } # startTransaction + - { connectionCheckOutStartedEvent: {} } # insertOne + - { connectionCheckOutStartedEvent: {} } # failPoint + - { connectionCheckOutStartedEvent: {} } # commitTransaction + - { connectionCheckOutStartedEvent: {} } # commitTransaction retry + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [{ _id: 2, x: 22 }] + startTransaction: true + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + ping: 1 + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + commandName: commitTransaction + databaseName: admin + + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } # The write was still applied diff --git a/source/transactions/tests/unified/retryable-commit.json b/source/transactions/tests/unified/retryable-commit.json new file mode 100644 index 0000000000..b794c1c55c --- /dev/null +++ b/source/transactions/tests/unified/retryable-commit.json @@ -0,0 +1,868 @@ +{ + "description": "retryable-commit", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "commitTransaction fails after Interrupted", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "errorCode": 11601, + "closeConnection": false + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorCodeName": "Interrupted", + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "commitTransaction is not retried after UnsatisfiableWriteConcern error", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "writeConcernError": { + "code": 100, + "errmsg": "Not enough data-bearing nodes" + } + } + } + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction", + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError", + "TransientTransactionError", + "UnknownTransactionCommitResult" + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction fails after two errors", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction applies majority write concern on retries", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 2, + "journal": true, + "wtimeoutMS": 5000 + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction", + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError", + "UnknownTransactionCommitResult" + ], + "errorLabelsOmit": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": 2, + "j": true, + "wtimeout": 5000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "j": true, + "wtimeout": 5000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "j": true, + "wtimeout": 5000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commitTransaction succeeds after connection error", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "commitTransaction" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority", + "wtimeout": 10000 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/retryable-commit.yml b/source/transactions/tests/unified/retryable-commit.yml new file mode 100644 index 0000000000..916944065a --- /dev/null +++ b/source/transactions/tests/unified/retryable-commit.yml @@ -0,0 +1,521 @@ +description: retryable-commit + +schemaVersion: '1.4' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + # Define a second set of entities for a retryWrites=false client + - + client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + retryWrites: false + observeEvents: + - commandStartedEvent + - + database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - + collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - + session: + id: &session1 session1 + client: *client1 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'commitTransaction fails after Interrupted' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + errorCode: 11601 + closeConnection: false + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorCodeName: Interrupted + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - UnknownTransactionCommitResult + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'commitTransaction is not retried after UnsatisfiableWriteConcern error' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + writeConcernError: + code: 100 + errmsg: 'Not enough data-bearing nodes' + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectError: + errorLabelsOmit: + - RetryableWriteError + - TransientTransactionError + - UnknownTransactionCommitResult + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction fails after two errors' + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309). + runOnRequirements: + - serverless: forbid + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + closeConnection: true + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # First call to commit fails after a single retry attempt. + - + object: *session1 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + # Second call to commit succeeds because the failpoint was disabled. + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction applies majority write concern on retries' + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309). + runOnRequirements: + - serverless: forbid + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: + - commitTransaction + closeConnection: true + - + object: *session1 + name: startTransaction + arguments: + writeConcern: + w: 2 + journal: true + wtimeoutMS: 5000 + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + # First call to commit fails after a single retry attempt. + - + object: *session1 + name: commitTransaction + expectError: + errorLabelsContain: + - RetryableWriteError + - UnknownTransactionCommitResult + errorLabelsOmit: + - TransientTransactionError + # Second call to commit succeeds because the failpoint was disabled. + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: 2 + j: true + wtimeout: 5000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + j: true + wtimeout: 5000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + j: true + wtimeout: 5000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'commitTransaction succeeds after connection error' + # Failing commitTransaction with closeConnection:true may abort the + # transaction (CLOUDP-202309). + runOnRequirements: + - serverless: forbid + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - commitTransaction + closeConnection: true + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + # commitTransaction applies w:majority on retries + writeConcern: + w: majority + wtimeout: 10000 + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/retryable-writes.json b/source/transactions/tests/unified/retryable-writes.json new file mode 100644 index 0000000000..21ef22b8ab --- /dev/null +++ b/source/transactions/tests/unified/retryable-writes.json @@ -0,0 +1,472 @@ +{ + "description": "retryable-writes", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "increment txnNumber", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + }, + { + "object": "collection0", + "name": "insertMany", + "arguments": { + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ], + "session": "session0" + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 4, + "1": 5 + } + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "3" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + }, + { + "_id": 5 + } + ], + "ordered": true, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "4" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 4 + }, + { + "_id": 5 + } + ] + } + ] + }, + { + "description": "writes are not retried", + "operations": [ + { + "object": "testRunner", + "name": "failPoint", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ] + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/retryable-writes.yml b/source/transactions/tests/unified/retryable-writes.yml new file mode 100644 index 0000000000..81339c9ad9 --- /dev/null +++ b/source/transactions/tests/unified/retryable-writes.yml @@ -0,0 +1,261 @@ +description: retryable-writes + +schemaVersion: '1.3' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'increment txnNumber' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 3 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } + - + object: *session0 + name: abortTransaction + - + object: *collection0 + name: insertMany + arguments: + documents: + - { _id: 4 } + - { _id: 5 } + session: *session0 + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 4 + '1': 5 + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 3 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '3' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '3' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 4 } + - { _id: 5 } + ordered: true + readConcern: + afterClusterTime: { $$exists: true } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '4' } + startTransaction: { $$exists: false } + autocommit: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 4 } + - { _id: 5 } + - + description: 'writes are not retried' + operations: + - + object: testRunner + name: failPoint + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: + - insert + closeConnection: true + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectError: + errorLabelsContain: + - TransientTransactionError + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] diff --git a/source/transactions/tests/unified/run-command.json b/source/transactions/tests/unified/run-command.json new file mode 100644 index 0000000000..7bd420ef74 --- /dev/null +++ b/source/transactions/tests/unified/run-command.json @@ -0,0 +1,421 @@ +{ + "description": "run-command", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "run command with default read preference", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + }, + "commandName": "insert" + }, + "expectResult": { + "n": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "run command with secondary read preference in client option and primary read preference in transaction options", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readPreference": "secondary" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "primary" + } + } + }, + { + "object": "database1", + "name": "runCommand", + "arguments": { + "session": "session1", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + }, + "commandName": "insert" + }, + "expectResult": { + "n": 1 + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "run command with explicit primary read preference", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ] + }, + "readPreference": { + "mode": "primary" + }, + "commandName": "insert" + }, + "expectResult": { + "n": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "run command fails with explicit secondary read preference", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "readPreference": { + "mode": "secondary" + }, + "commandName": "find" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + } + ] + }, + { + "description": "run command fails with secondary read preference from transaction options", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "secondary" + } + } + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "session": "session0", + "command": { + "find": "test" + }, + "commandName": "find" + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/run-command.yml b/source/transactions/tests/unified/run-command.yml new file mode 100644 index 0000000000..b9e1033d4c --- /dev/null +++ b/source/transactions/tests/unified/run-command.yml @@ -0,0 +1,254 @@ +description: run-command + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'run command with default read preference' + operations: + - + object: *session0 + name: startTransaction + - + object: *database0 + name: runCommand + arguments: + session: *session0 + command: + insert: *collection_name + documents: + - { _id: 1 } + commandName: insert + expectResult: + 'n': 1 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + description: 'run command with secondary read preference in client option and primary read preference in transaction options' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: { readPreference: secondary } + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + arguments: + readPreference: + mode: primary + - + object: *database1 + name: runCommand + arguments: + session: *session1 + command: + insert: *collection_name + documents: + - { _id: 1 } + commandName: insert + expectResult: + 'n': 1 + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + description: 'run command with explicit primary read preference' + operations: + - + object: *session0 + name: startTransaction + - + object: *database0 + name: runCommand + arguments: + session: *session0 + command: + insert: *collection_name + documents: + - { _id: 1 } + readPreference: + mode: primary + commandName: insert + expectResult: + 'n': 1 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + description: 'run command fails with explicit secondary read preference' + operations: + - + object: *session0 + name: startTransaction + - + object: *database0 + name: runCommand + arguments: + session: *session0 + command: + find: *collection_name + readPreference: + mode: secondary + commandName: find + expectError: + errorContains: 'read preference in a transaction must be primary' + - + description: 'run command fails with secondary read preference from transaction options' + operations: + - + object: *session0 + name: startTransaction + arguments: + readPreference: + mode: secondary + - + object: *database0 + name: runCommand + arguments: + session: *session0 + command: + find: *collection_name + commandName: find + expectError: + errorContains: 'read preference in a transaction must be primary' diff --git a/source/transactions/tests/unified/transaction-options-repl.json b/source/transactions/tests/unified/transaction-options-repl.json new file mode 100644 index 0000000000..dc2cb77582 --- /dev/null +++ b/source/transactions/tests/unified/transaction-options-repl.json @@ -0,0 +1,267 @@ +{ + "description": "transaction-options-repl", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "readConcern snapshot in startTransaction options", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "session": { + "id": "session1", + "client": "client0", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "majority" + } + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "snapshot" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "snapshot" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "snapshot" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "snapshot", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/transaction-options-repl.yml b/source/transactions/tests/unified/transaction-options-repl.yml new file mode 100644 index 0000000000..f801c13dd8 --- /dev/null +++ b/source/transactions/tests/unified/transaction-options-repl.yml @@ -0,0 +1,155 @@ +description: transaction-options-repl + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'readConcern snapshot in startTransaction options' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - session: + id: &session1 session1 + client: *client0 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: majority } + - + object: *session1 + name: startTransaction + arguments: + readConcern: + level: snapshot + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + # Now test abort + - + object: *session1 + name: startTransaction + arguments: + readConcern: + level: snapshot + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: snapshot + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: snapshot + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/transaction-options.json b/source/transactions/tests/unified/transaction-options.json new file mode 100644 index 0000000000..78e4c8207b --- /dev/null +++ b/source/transactions/tests/unified/transaction-options.json @@ -0,0 +1,2081 @@ +{ + "description": "transaction-options", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "no transaction options set", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + }, + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "transaction options inherited from client", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "transaction options inherited from defaultTransactionOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "session": { + "id": "session1", + "client": "client0", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + }, + "maxCommitTimeMS": 60000 + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "startTransaction options override defaults", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "snapshot" + }, + "writeConcern": { + "w": 1 + }, + "maxCommitTimeMS": 30000 + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": "majority" + }, + "maxCommitTimeMS": 60000 + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "defaultTransactionOptions override client options", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": "majority" + }, + "maxCommitTimeMS": 60000 + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": 60000 + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": "majority" + }, + "maxTimeMS": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "readConcern local in defaultTransactionOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "w": 1 + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "local" + } + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session1", + "name": "commitTransaction" + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 2 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 2 + } + } + } + }, + { + "object": "session1", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local", + "afterClusterTime": { + "$$exists": true + } + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "2" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "w": 1 + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "client writeConcern ignored for bulk", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "w": "majority" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 1 + } + } + }, + { + "object": "collection1", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ], + "session": "session1" + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": 1 + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "readPreference inherited from client", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readPreference": "secondary" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "readPreference inherited from defaultTransactionOptions", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readPreference": "primary" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1", + "sessionOptions": { + "defaultTransactionOptions": { + "readPreference": { + "mode": "secondary" + } + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction" + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "startTransaction overrides readPreference", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "readPreference": "primary" + }, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session1", + "client": "client1", + "sessionOptions": { + "defaultTransactionOptions": { + "readPreference": { + "mode": "primary" + } + } + } + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "arguments": { + "readPreference": { + "mode": "secondary" + } + } + }, + { + "object": "collection1", + "name": "insertOne", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "collection1", + "name": "find", + "arguments": { + "session": "session1", + "filter": { + "_id": 1 + } + }, + "expectError": { + "errorContains": "read preference in a transaction must be primary" + } + }, + { + "object": "session1", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/transaction-options.yml b/source/transactions/tests/unified/transaction-options.yml new file mode 100644 index 0000000000..f3ab07cfc4 --- /dev/null +++ b/source/transactions/tests/unified/transaction-options.yml @@ -0,0 +1,1116 @@ +description: transaction-options + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + +tests: + - + description: 'no transaction options set' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: commitTransaction + # Now test abort + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: &outcome + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'transaction options inherited from client' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readConcernLevel: local + w: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: local + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + maxTimeMS: { $$exists: false } + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: local + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + maxTimeMS: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: *outcome + - + description: 'transaction options inherited from defaultTransactionOptions' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - session: + id: &session1 session1 + client: *client0 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: majority } + writeConcern: { w: 1 } + maxCommitTimeMS: 60000 + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: majority + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: majority + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + commandName: abortTransaction + databaseName: admin + outcome: *outcome + - + description: 'startTransaction options override defaults' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readConcernLevel: local + w: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: snapshot } + writeConcern: { w: 1 } + maxCommitTimeMS: 30000 + - + object: *session1 + name: startTransaction + arguments: + readConcern: + level: majority + writeConcern: + w: majority + maxCommitTimeMS: 60000 + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + arguments: + readConcern: + level: majority + writeConcern: + w: majority + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: majority + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: majority + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + maxTimeMS: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: *outcome + - + description: 'defaultTransactionOptions override client options' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readConcernLevel: local + w: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: majority } + writeConcern: { w: majority } + maxCommitTimeMS: 60000 + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: majority + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + maxTimeMS: 60000 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: majority + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + maxTimeMS: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: majority + maxTimeMS: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: *outcome + - + description: 'readConcern local in defaultTransactionOptions' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + w: 1 + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: local } + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session1 + name: commitTransaction + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 2 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } + - + object: *session1 + name: abortTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: + level: local + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + commandName: commitTransaction + databaseName: admin + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: true + autocommit: false + readConcern: + level: local + afterClusterTime: { $$exists: true } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '2' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: + w: 1 + commandName: abortTransaction + databaseName: admin + outcome: *outcome + - + description: 'client writeConcern ignored for bulk' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + w: majority + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + arguments: + writeConcern: + w: 1 + - + object: *collection1 + name: bulkWrite + arguments: + requests: + - + insertOne: + document: { _id: 1 } + session: *session1 + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: + $$unsetOrMatches: + '0': 1 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + # No writeConcern + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: 1 + commandName: commitTransaction + databaseName: admin + outcome: *outcome + - + description: 'readPreference inherited from client' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readPreference: secondary + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection1 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'readPreference inherited from defaultTransactionOptions' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readPreference: primary + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + sessionOptions: + defaultTransactionOptions: + readPreference: { mode: secondary } + - + object: *session1 + name: startTransaction + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection1 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - + description: 'startTransaction overrides readPreference' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: + readPreference: primary + observeEvents: + - commandStartedEvent + - database: + id: &database1 database1 + client: *client1 + databaseName: *database_name + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collection_name + - session: + id: &session1 session1 + client: *client1 + sessionOptions: + defaultTransactionOptions: + readPreference: { mode: primary } + - + object: *session1 + name: startTransaction + arguments: + readPreference: + mode: secondary + - + object: *collection1 + name: insertOne + arguments: + session: *session1 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *collection1 + name: find + arguments: + session: *session1 + filter: { _id: 1 } + expectError: + errorContains: 'read preference in a transaction must be primary' + - + object: *session1 + name: commitTransaction + expectEvents: + - + client: *client1 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } diff --git a/source/transactions/tests/unified/update.json b/source/transactions/tests/unified/update.json new file mode 100644 index 0000000000..8090fc9087 --- /dev/null +++ b/source/transactions/tests/unified/update.json @@ -0,0 +1,565 @@ +{ + "description": "update", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "update", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + }, + { + "object": "collection0", + "name": "replaceOne", + "arguments": { + "session": "session0", + "filter": { + "x": 1 + }, + "replacement": { + "y": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "collection0", + "name": "updateMany", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 3 + } + }, + "update": { + "$set": { + "z": 1 + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": true, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "x": 1 + }, + "u": { + "y": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gte": 3 + } + }, + "u": { + "$set": { + "z": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3, + "z": 1 + }, + { + "_id": 4, + "y": 1, + "z": 1 + } + ] + } + ] + }, + { + "description": "collections writeConcern ignored for update", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + }, + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection1", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 4 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 1, + "upsertedId": 4 + } + }, + { + "object": "collection1", + "name": "replaceOne", + "arguments": { + "session": "session0", + "filter": { + "x": 1 + }, + "replacement": { + "y": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "collection1", + "name": "updateMany", + "arguments": { + "session": "session0", + "filter": { + "_id": { + "$gte": 3 + } + }, + "update": { + "$set": { + "z": 1 + } + } + }, + "expectResult": { + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 4 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": true, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "x": 1 + }, + "u": { + "y": 1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": { + "$gte": 3 + } + }, + "u": { + "$set": { + "z": 1 + } + }, + "multi": true, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/update.yml b/source/transactions/tests/unified/update.yml new file mode 100644 index 0000000000..4e49350653 --- /dev/null +++ b/source/transactions/tests/unified/update.yml @@ -0,0 +1,299 @@ +description: update + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + +tests: + - + description: update + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: updateOne + arguments: + session: *session0 + filter: { _id: 4 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + - + object: *collection0 + name: replaceOne + arguments: + session: *session0 + filter: { x: 1 } + replacement: { y: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - + object: *collection0 + name: updateMany + arguments: + session: *session0 + filter: { _id: { $gte: 3 } } + update: { $set: { z: 1 } } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 4 } + u: { $inc: { x: 1 } } + upsert: true + multi: { $$unsetOrMatches: false } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { x: 1 } + u: { y: 1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: { $gte: 3 } } + u: { $set: { z: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3, z: 1 } + - { _id: 4, y: 1, z: 1 } + - + description: 'collections writeConcern ignored for update' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: *collection_name + collectionOptions: + writeConcern: { w: majority } + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection1 + name: updateOne + arguments: + session: *session0 + filter: { _id: 4 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 1 + upsertedId: 4 + - + object: *collection1 + name: replaceOne + arguments: + session: *session0 + filter: { x: 1 } + replacement: { y: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - + object: *collection1 + name: updateMany + arguments: + session: *session0 + filter: { _id: { $gte: 3 } } + update: { $set: { z: 1 } } + expectResult: + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + - + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 4 } + u: { $inc: { x: 1 } } + upsert: true + multi: { $$unsetOrMatches: false } + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { x: 1 } + u: { y: 1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: { $gte: 3 } } + u: { $set: { z: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: update + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin diff --git a/source/transactions/tests/unified/write-concern.json b/source/transactions/tests/unified/write-concern.json new file mode 100644 index 0000000000..29d1977a82 --- /dev/null +++ b/source/transactions/tests/unified/write-concern.json @@ -0,0 +1,1588 @@ +{ + "description": "write-concern", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "collection": { + "id": "collection_w0", + "database": "database0", + "collectionName": "test", + "collectionOptions": { + "writeConcern": { + "w": 0 + } + } + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + } + ] + } + ], + "tests": [ + { + "description": "commit with majority", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "commit with default", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "abort with majority", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": "majority" + } + } + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "w": "majority" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + } + ] + } + ] + }, + { + "description": "abort with default", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "abortTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + } + ] + } + ] + }, + { + "description": "start with unacknowledged write concern", + "operations": [ + { + "object": "session0", + "name": "startTransaction", + "arguments": { + "writeConcern": { + "w": 0 + } + }, + "expectError": { + "isClientError": true, + "errorContains": "transactions do not support unacknowledged write concern" + } + } + ] + }, + { + "description": "start with implicit unacknowledged write concern", + "operations": [ + { + "object": "testRunner", + "name": "createEntities", + "arguments": { + "entities": [ + { + "client": { + "id": "client1", + "useMultipleMongoses": false, + "uriOptions": { + "w": 0 + } + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + } + ] + } + }, + { + "object": "session1", + "name": "startTransaction", + "expectError": { + "isClientError": true, + "errorContains": "transactions do not support unacknowledged write concern" + } + } + ] + }, + { + "description": "unacknowledged write concern coll insertOne", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "insertOne", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll insertMany", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "insertMany", + "arguments": { + "session": "session0", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 1, + "1": 2 + } + } + } + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll bulkWrite", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "bulkWrite", + "arguments": { + "session": "session0", + "requests": [ + { + "insertOne": { + "document": { + "_id": 1 + } + } + } + ] + }, + "expectResult": { + "deletedCount": 0, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 1 + } + }, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0 + }, + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll deleteOne", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "deleteOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 0 + }, + "limit": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "unacknowledged write concern coll deleteMany", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "deleteMany", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + } + }, + "expectResult": { + "deletedCount": 1 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "test", + "deletes": [ + { + "q": { + "_id": 0 + }, + "limit": 0 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "delete", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "unacknowledged write concern coll updateOne", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "updateOne", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 0 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "upsert": true, + "multi": { + "$$unsetOrMatches": false + } + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0, + "x": 1 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll updateMany", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "updateMany", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "upsert": true + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "test", + "updates": [ + { + "q": { + "_id": 0 + }, + "u": { + "$inc": { + "x": 1 + } + }, + "multi": true, + "upsert": true + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "update", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0, + "x": 1 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll findOneAndDelete", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "findOneAndDelete", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + } + }, + "expectResult": { + "_id": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 0 + }, + "remove": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ] + }, + { + "description": "unacknowledged write concern coll findOneAndReplace", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "findOneAndReplace", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + }, + "replacement": { + "x": 1 + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 0 + }, + "update": { + "x": 1 + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0, + "x": 1 + } + ] + } + ] + }, + { + "description": "unacknowledged write concern coll findOneAndUpdate", + "operations": [ + { + "object": "session0", + "name": "startTransaction" + }, + { + "object": "collection_w0", + "name": "findOneAndUpdate", + "arguments": { + "session": "session0", + "filter": { + "_id": 0 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 0 + } + }, + { + "object": "session0", + "name": "commitTransaction" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "test", + "query": { + "_id": 0 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "new": { + "$$unsetOrMatches": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "findAndModify", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": { + "$numberLong": "1" + }, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 0, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/source/transactions/tests/unified/write-concern.yml b/source/transactions/tests/unified/write-concern.yml new file mode 100644 index 0000000000..1eb27cf1d1 --- /dev/null +++ b/source/transactions/tests/unified/write-concern.yml @@ -0,0 +1,655 @@ +# Assumes the default for transactions is the same as for all ops, tests +# setting the writeConcern to "majority". +description: write-concern + +schemaVersion: '1.9' + +runOnRequirements: + - + minServerVersion: '4.0' + topologies: + - replicaset + - + minServerVersion: 4.1.8 + topologies: + - sharded + - load-balanced + +createEntities: + - + client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - + database: + id: &database0 database0 + client: *client0 + databaseName: &database_name transaction-tests + - + collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection_name test + - + collection: + id: &collection_w0 collection_w0 + database: *database0 + collectionName: &collection_name test + collectionOptions: + writeConcern: { w: 0 } + - + session: + id: &session0 session0 + client: *client0 + +initialData: + - + collectionName: *collection_name + databaseName: *database_name + documents: &data + - { _id: 0 } + +tests: + - + description: 'commit with majority' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - &commitTransaction + object: *session0 + name: commitTransaction + expectEvents: + - + client: *client0 + events: + - &insertOneEvent + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + <<: &transactionCommandArgs + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: true + autocommit: false + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0 } + - { _id: 1 } + - + description: 'commit with default' + operations: + - &startTransaction + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + <<: *transactionCommandArgs + commandName: insert + databaseName: *database_name + - &commitWithDefaultWCEvent + commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0 } + - { _id: 1 } + - + description: 'abort with majority' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: majority + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + <<: *transactionCommandArgs + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: + w: majority + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: 'abort with default' + operations: + - + object: *session0 + name: startTransaction + - + object: *collection0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - + object: *session0 + name: abortTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + ordered: true + <<: *transactionCommandArgs + commandName: insert + databaseName: *database_name + - + commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: { $numberLong: '1' } + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: abortTransaction + databaseName: admin + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: *data + - + description: 'start with unacknowledged write concern' + operations: + - + object: *session0 + name: startTransaction + arguments: + writeConcern: + w: 0 + expectError: + isClientError: true + errorContains: 'transactions do not support unacknowledged write concern' + - + description: 'start with implicit unacknowledged write concern' + operations: + - + object: testRunner + name: createEntities + arguments: + entities: + - client: + id: &client1 client1 + useMultipleMongoses: false + uriOptions: { w: 0 } + - session: + id: &session1 session1 + client: *client1 + - + object: *session1 + name: startTransaction + expectError: + isClientError: true + errorContains: 'transactions do not support unacknowledged write concern' + - + description: 'unacknowledged write concern coll insertOne' + operations: + - *startTransaction + - + object: *collection_w0 + name: insertOne + arguments: + session: *session0 + document: { _id: 1 } + expectResult: + $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - *insertOneEvent + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0 } + - { _id: 1 } + - + description: 'unacknowledged write concern coll insertMany' + operations: + - *startTransaction + - + object: *collection_w0 + name: insertMany + arguments: + session: *session0 + documents: + - { _id: 1 } + - { _id: 2 } + expectResult: + $$unsetOrMatches: + insertedIds: + $$unsetOrMatches: + '0': 1 + '1': 2 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + insert: *collection_name + documents: + - { _id: 1 } + - { _id: 2 } + ordered: true + <<: *transactionCommandArgs + commandName: insert + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0 } + - { _id: 1 } + - { _id: 2 } + - + description: 'unacknowledged write concern coll bulkWrite' + operations: + - *startTransaction + - + object: *collection_w0 + name: bulkWrite + arguments: + session: *session0 + requests: + - + insertOne: + document: { _id: 1 } + expectResult: + deletedCount: 0 + insertedCount: 1 + insertedIds: + $$unsetOrMatches: + '0': 1 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - *insertOneEvent + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0 } + - { _id: 1 } + - + description: 'unacknowledged write concern coll deleteOne' + operations: + - *startTransaction + - + object: *collection_w0 + name: deleteOne + arguments: + session: *session0 + filter: { _id: 0 } + expectResult: + deletedCount: 1 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 0 } + limit: 1 + ordered: true + <<: *transactionCommandArgs + commandName: delete + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'unacknowledged write concern coll deleteMany' + operations: + - *startTransaction + - + object: *collection_w0 + name: deleteMany + arguments: + session: *session0 + filter: { _id: 0 } + expectResult: + deletedCount: 1 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + delete: *collection_name + deletes: + - + q: { _id: 0 } + limit: 0 + ordered: true + <<: *transactionCommandArgs + commandName: delete + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'unacknowledged write concern coll updateOne' + operations: + - *startTransaction + - + object: *collection_w0 + name: updateOne + arguments: + session: *session0 + filter: { _id: 0 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 0 } + u: { $inc: { x: 1 } } + upsert: true + multi: { $$unsetOrMatches: false } + ordered: true + <<: *transactionCommandArgs + commandName: update + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0, x: 1 } + - + description: 'unacknowledged write concern coll updateMany' + operations: + - *startTransaction + - + object: *collection_w0 + name: updateMany + arguments: + session: *session0 + filter: { _id: 0 } + update: { $inc: { x: 1 } } + upsert: true + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + update: *collection_name + updates: + - + q: { _id: 0 } + u: { $inc: { x: 1 } } + multi: true + upsert: true + ordered: true + <<: *transactionCommandArgs + commandName: update + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0, x: 1 } + - + description: 'unacknowledged write concern coll findOneAndDelete' + operations: + - *startTransaction + - + object: *collection_w0 + name: findOneAndDelete + arguments: + session: *session0 + filter: { _id: 0 } + expectResult: + _id: 0 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 0 } + remove: true + <<: *transactionCommandArgs + commandName: findAndModify + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: [] + - + description: 'unacknowledged write concern coll findOneAndReplace' + operations: + - *startTransaction + - + object: *collection_w0 + name: findOneAndReplace + arguments: + session: *session0 + filter: { _id: 0 } + replacement: { x: 1 } + returnDocument: Before + expectResult: + _id: 0 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 0 } + update: { x: 1 } + new: { $$unsetOrMatches: false } + <<: *transactionCommandArgs + commandName: findAndModify + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0, x: 1 } + - + description: 'unacknowledged write concern coll findOneAndUpdate' + operations: + - *startTransaction + - + object: *collection_w0 + name: findOneAndUpdate + arguments: + session: *session0 + filter: { _id: 0 } + update: { $inc: { x: 1 } } + returnDocument: Before + expectResult: + _id: 0 + - *commitTransaction + expectEvents: + - + client: *client0 + events: + - + commandStartedEvent: + command: + findAndModify: *collection_name + query: { _id: 0 } + update: { $inc: { x: 1 } } + new: { $$unsetOrMatches: false } + <<: *transactionCommandArgs + commandName: findAndModify + databaseName: *database_name + - *commitWithDefaultWCEvent + outcome: + - + collectionName: *collection_name + databaseName: *database_name + documents: + - { _id: 0, x: 1 } diff --git a/source/transactions/tests/update.json b/source/transactions/tests/update.json deleted file mode 100644 index 26590c565a..0000000000 --- a/source/transactions/tests/update.json +++ /dev/null @@ -1,436 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "update", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - } - }, - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "x": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 3 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": false, - "upsert": true - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "x": 1 - }, - "u": { - "y": 1 - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 3 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "z": 1 - }, - { - "_id": 4, - "y": 1, - "z": 1 - } - ] - } - } - }, - { - "description": "collections writeConcern ignored for update", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "updateOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - } - }, - { - "name": "replaceOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "x": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 3 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": false, - "upsert": true - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "x": 1 - }, - "u": { - "y": 1 - }, - "multi": false, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 3 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true, - "upsert": false - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/update.yml b/source/transactions/tests/update.yml deleted file mode 100644 index b325b44022..0000000000 --- a/source/transactions/tests/update.yml +++ /dev/null @@ -1,246 +0,0 @@ -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: - - _id: 1 - - _id: 2 - - _id: 3 - -tests: - - description: update - - operations: - - name: startTransaction - object: session0 - - name: updateOne - object: collection - arguments: - session: session0 - filter: {_id: 4} - update: - $inc: {x: 1} - upsert: true - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - - name: replaceOne - object: collection - arguments: - session: session0 - filter: {x: 1} - replacement: {y: 1} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - - name: updateMany - object: collection - arguments: - session: session0 - filter: - _id: {$gte: 3} - update: - $set: {z: 1} - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 4} - u: {$inc: {x: 1}} - multi: false - upsert: true - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {x: 1} - u: {y: 1} - multi: false - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: {$gte: 3}} - u: {$set: {z: 1}} - multi: true - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - {_id: 1} - - {_id: 2} - - {_id: 3, z: 1} - - {_id: 4, y: 1, z: 1} - - - description: collections writeConcern ignored for update - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: updateOne - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: {_id: 4} - update: - $inc: {x: 1} - upsert: true - result: - matchedCount: 0 - modifiedCount: 0 - upsertedCount: 1 - upsertedId: 4 - - name: replaceOne - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: {x: 1} - replacement: {y: 1} - result: - matchedCount: 1 - modifiedCount: 1 - upsertedCount: 0 - - name: updateMany - object: collection - collectionOptions: - writeConcern: - w: majority - arguments: - session: session0 - filter: - _id: {$gte: 3} - update: - $set: {z: 1} - result: - matchedCount: 2 - modifiedCount: 2 - upsertedCount: 0 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: 4} - u: {$inc: {x: 1}} - multi: false - upsert: true - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {x: 1} - u: {y: 1} - multi: false - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - update: *collection_name - updates: - - q: {_id: {$gte: 3}} - u: {$set: {z: 1}} - multi: true - upsert: false - ordered: true - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: update - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin diff --git a/source/transactions/tests/write-concern.json b/source/transactions/tests/write-concern.json deleted file mode 100644 index dfc75f76b4..0000000000 --- a/source/transactions/tests/write-concern.json +++ /dev/null @@ -1,355 +0,0 @@ -{ - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commit with majority", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit with default", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "abort with majority", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort with default", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "start with unacknowledged write concern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 0 - } - } - }, - "result": { - "errorContains": "transactions do not support unacknowledged write concern" - } - } - ] - }, - { - "description": "start with implicit unacknowledged write concern", - "clientOptions": { - "w": 0 - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transactions do not support unacknowledged write concern" - } - } - ] - } - ] -} diff --git a/source/transactions/tests/write-concern.yml b/source/transactions/tests/write-concern.yml deleted file mode 100644 index adadbd8e2c..0000000000 --- a/source/transactions/tests/write-concern.yml +++ /dev/null @@ -1,236 +0,0 @@ -# Assumes the default for transactions is the same as for all ops, tests -# setting the writeConcern to "majority". -database_name: &database_name "transaction-tests" -collection_name: &collection_name "test" - -data: [] - -tests: - - description: commit with majority - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - _id: 1 - - - description: commit with default - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: commitTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - commitTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: commitTransaction - database_name: admin - - outcome: - collection: - data: - - - _id: 1 - - - description: abort with majority - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: majority - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - w: majority - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: abort with default - - operations: - - name: startTransaction - object: session0 - - name: insertOne - object: collection - arguments: - session: session0 - document: - _id: 1 - result: - insertedId: 1 - - name: abortTransaction - object: session0 - - expectations: - - command_started_event: - command: - insert: *collection_name - documents: - - _id: 1 - ordered: true - readConcern: - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: true - autocommit: false - writeConcern: - command_name: insert - database_name: *database_name - - command_started_event: - command: - abortTransaction: 1 - lsid: session0 - txnNumber: - $numberLong: "1" - startTransaction: - autocommit: false - writeConcern: - command_name: abortTransaction - database_name: admin - - outcome: - collection: - data: [] - - - description: start with unacknowledged write concern - - operations: - - name: startTransaction - object: session0 - arguments: - options: - writeConcern: - w: 0 - result: - # Client-side error. - errorContains: transactions do not support unacknowledged write concern - - - description: start with implicit unacknowledged write concern - - clientOptions: - w: 0 - - operations: - - name: startTransaction - object: session0 - result: - # Client-side error. - errorContains: transactions do not support unacknowledged write concern diff --git a/source/transactions/transactions.md b/source/transactions/transactions.md new file mode 100644 index 0000000000..a00a68e64b --- /dev/null +++ b/source/transactions/transactions.md @@ -0,0 +1,1192 @@ +# Transactions Specification + +- Status: Accepted +- Minimum Server Version: 4.0 + +______________________________________________________________________ + +## **Abstract** + +Version 4.0 of the server introduces multi-statement transactions. This spec builds upon the +[Driver Sessions Specification](../sessions/driver-sessions.md) to define how an application uses transactions and how a +driver interacts with the server to implement transactions. + +The API for transactions must be specified to ensure that all drivers and the mongo shell are consistent with each +other, and to provide a natural interface for application developers and DBAs who use multi-statement transactions. + +## **META** + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## **Specification** + +### **Terms** + +This specification uses the terms defined in the [Driver Sessions Specification](../sessions/driver-sessions.md) and +[Retryable Writes Specification](../retryable-writes/retryable-writes.md). Additional terms are defined below. + +#### Resource Management Block + +Some programming languages have a concept of a code block that automatically frees resources when control exits the +block. Such a pattern is known as context managers, "using" statements, RAII, etc.. This spec refers to such a pattern +as a resource management block, regardless of the programming language. + +#### Read operation + +Any CRUD method that uses a read preference. The listIndexes, listCollections, and listDatabases, and RunCommand helpers +are also considered read operations. + +#### Write operation + +All operations that write and accept a ClientSession argument. All MongoClient, Database, Collection helpers that write +including (but not limited to) creating, updating, or deleting databases, collections, indexes, and users. Aggregate +(even with a write stage) is considered a read operation, see +[Aggregate with write stage is a read operation](#aggregate-with-write-stage-is-a-read-operation). + +#### Retryable Error + +An error considered retryable by the [Retryable Writes Specification](../retryable-writes/retryable-writes.md). + +#### Command Error + +A server response with ok:0. A server response with ok:1 and writeConcernError or writeErrors is not considered a +command error. + +#### Network Error + +Any error or timeout that occurs while selecting a server or reading from or writing to a network socket. + +#### Error Label + +Starting in MongoDB 4.0, any command error may include a top level "errorLabels" field. The field contains an array of +string error labels. Drivers may also add error labels to errors that they return. + +#### Transient Transaction Error + +Any command, network, or driver error that includes the "TransientTransactionError" error label in the "errorLabels" +field. + +### **Naming variations** + +This specification defines names for new methods and types. To the extent possible you SHOULD use these names in your +driver. However, where your driver's and/or language's naming conventions differ you SHOULD continue to use them +instead. For example, you might use StartTransaction or start_transaction instead of startTransaction. + +A non-exhaustive list of acceptable naming deviations are as follows: + +- Using "maxCommitTimeMS" as an example, .NET would use "MaxCommitTime" where it's type is a TimeSpan structure that + includes units. However, calling it "MaximumCommitTime" would not be acceptable. + +### **Transaction API** + +Transactions are built on top of Driver Session API. Applications can run a transaction like this: + +```python +with client.start_session() as s: + s.start_transaction() + collection_one.insert_one(doc_one, session=s) + collection_two.insert_one(doc_two, session=s) + s.commit_transaction() +``` + +This section is an overview of the public API for transactions: + +```typescript +class TransactionOptions { + /** + * The readConcern to use for this transaction. + */ + Optional readConcern; + + /** + * The writeConcern to use for this transaction. + */ + Optional writeConcern; + + /** + * The readPreference to use for this transaction. + */ + Optional readPreference; + + /** + * The maximum amount of time to allow a single commitTransaction + * command to run. + * + * NOTE: This option is deprecated in favor of timeoutMS. + */ + Optional maxCommitTimeMS; +} + +class SessionOptions { + /** + * The default TransactionOptions to use for transactions started + * on this session. + */ + Optional defaultTransactionOptions; + + // Options defined in other specifications... +} + +interface ClientSession { + /** + * Starts a new transaction with the given options. This session's + * defaultTransactionOptions is used when options is omitted. + * Raises an error if this session is already in a transaction. + * + * The return type MAY be non-void if necessary to participate in + * the programming language's resource management block idiom. The + * type of the returned object, if any, MUST NOT be named + * Transaction, see "Why is there no Transaction object?" + */ + void startTransaction(Optional options); + + /** + * Commits the currently active transaction in this session. + * Raises an error if this session has no transaction. + */ + void commitTransaction(); + + /** + * Aborts the currently active transaction in this session. + * Raises an error if this session has no transaction. + */ + void abortTransaction(); + + /** + * Aborts any currently active transaction and ends this session. + * MUST NOT raise an error. + */ + void endSession(); + + // Methods defined in other specifications... +} +``` + +Each new member is documented below. + +### **TransactionOptions** + +It is expected that the set of TransactionOptions will grow over time, TransactionOptions MUST be designed such that +future options can be added without breaking backward compatibility. + +#### readConcern + +The readConcern to use for the first command, and only the first command, in a transaction. Server transactions are +started lazily with the first command using this session. For supported values see +[Behavior of the readConcern field](#behavior-of-the-readconcern-field). + +Note that the readConcern property is optional. The default value is NULL. If readConcern is NULL the value will be +inherited from this session’s defaultTransactionOptions. If defaultTransactionOptions itself is NULL or the readConcern +in defaultTransactionOptions is NULL, the readConcern is inherited from the MongoClient associated with this session. + +If the user supplies an explicit readConcern via a method option, the driver MUST raise an error with the message +"Cannot set read concern after starting a transaction." See +[Users cannot pass readConcern or writeConcern to operations in transactions](#users-cannot-pass-readconcern-or-writeconcern-to-operations-in-transactions). + +#### writeConcern + +The writeConcern to use for the commitTransaction and abortTransaction commands. Note that the writeConcern property is +optional. The default value is NULL. If writeConcern is NULL the value will be inherited from this session’s +defaultTransactionOptions. If defaultTransactionOptions itself is NULL or the writeConcern in defaultTransactionOptions +is NULL, the writeConcern is inherited from the MongoClient associated with this session. + +If the writeConcern is not the server default, then Drivers MUST add the writeConcern to the commitTransaction and +abortTransaction commands. Drivers MUST NOT add the transaction’s writeConcern or any writeConcern inherited from the +collection, database, or client to any preceding commands in the transaction. + +If the user supplies an explicit writeConcern via a method option, the driver MUST raise an error with the message +"Cannot set write concern after starting a transaction." See +[Users cannot pass readConcern or writeConcern to operations in transactions](#users-cannot-pass-readconcern-or-writeconcern-to-operations-in-transactions). + +Drivers MUST raise an error if the user provides or if defaults would result in an unacknowledged writeConcern. The +Driver Sessions spec disallows using unacknowledged writes in a session. The error message MUST contain "transactions do +not support unacknowledged write concerns". + +If a higher level object, such as a Collection, is created inside a transaction, their write concern option MUST be +ignored if provided and the transaction level write concern used instead. An example of expected behaviour is: + +```python +client = MongoClient("mongodb://host/?readPreference=nearest") +coll = client.db.test +with client.start_session() as s: + with s.start_transaction(): + w_0_coll = db.get_collection("w_0_coll", writeConcern=WriteConcern(w=0)) + # This is allowed, the write concern of the transaction is used instead of w=0 from the collection. + w_0_coll.insert_one({}, session=s) +``` + +#### readPreference + +The read preference to use for all read operations in this transaction. + +Note that the readPreference property is optional. The default value is NULL. If readPreference is NULL the value will +be inherited from this session’s defaultTransactionOptions. If defaultTransactionOptions itself is NULL or the +readPreference in defaultTransactionOptions is NULL, the readPreference is inherited from the MongoClient associated +with this session. + +The transaction’s read preference MUST override all other user configurable read preferences, with the exception of +drivers that allow an operation level read preference. In this case, the driver MUST respect the read preference +specified by the user, allowing the server to report an error. + +In MongoDB 4.0, transactions may only read from the primary. If a read is attempted and the transaction’s read +preference is not Primary drivers MUST raise an error containing the string "read preference in a transaction must be +primary". Drivers MUST NOT validate the read preference during write operations or in startTransaction. See +[Why is readPreference part of TransactionOptions?](#why-is-readpreference-part-of-transactionoptions). + +```python +client = MongoClient("mongodb://host/?readPreference=nearest") +coll = client.db.test +with client.start_session() as s: + with s.start_transaction(): + coll.insert_one({}, session=s) + coll.find_one(session=s) # Error: "read preference in a transaction must be primary" +``` + +#### maxCommitTimeMS + +NOTE: This option is deprecated in favor of +[timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms). + +The maximum amount of time to allow a single commitTransaction command to run. + +This option is only sent with the commitTransaction command(s) and only if the caller explicitly provides a value. The +default is to not send a value. + +Note, this option is an alias for the `maxTimeMS` commitTransaction command option. + +### **SessionOptions changes** + +#### defaultTransactionOptions + +The default TransactionOptions to use for transactions started on this session. + +### **ClientSession changes** + +ClientSession is in one of five states: "no transaction", "starting transaction", "transaction in progress", +"transaction committed", and "transaction aborted". It transitions among these states according to the following +diagram: + +![ClientSession transaction states](client-session-transaction-states.png) +([GraphViz source](client-session-transaction-states.dot)) + +When a ClientSession is created it starts in the "no transaction" state. Starting, committing, and aborting a +transaction transitions the session between the "starting transaction", "transaction in progress", "transaction +committed", and "transaction aborted" states. If the session is in the "transaction aborted" or "transaction committed" +state, then any operation using the session (besides commitTransaction and abortTransaction) MUST reset the session +state to "no transaction". + +Note that "error" is not a state, it represents throwing an error due to an invalid operation. When such errors are +thrown the session state is unchanged. + +Client-side errors MUST NOT change transaction state. For example, if an invalid key or an excessively large document is +provided by the application to an insert when the transaction state is "starting transaction", the transaction state +MUST remain "starting transaction". If the same situation occurs when the transaction state is "transaction in +progress", the state MUST remain "transaction in progress". + +#### startTransaction + +This method starts a new transaction on this session with the given TransactionOptions. When options is omitted or if +particular options are not specified, drivers will use the defaultTransactionOptions from ClientSession.options or +inherit them from the session's client, as described in the text above for each option. This session is in the "starting +transaction" state after this method returns. + +If this session is in the "starting transaction " or "transaction in progress" state, then Drivers MUST raise an error +containing the message "Transaction already in progress" without modifying any session state. + +startTransaction SHOULD report an error if the driver can detect that transactions are not supported by the deployment. +A deployment does not support transactions when the deployment does not support sessions, or maxWireVersion < 7, or the +maxWireVersion < 8 and the topology type is Sharded, see +[How to Tell Whether a Connection Supports Sessions](../sessions/driver-sessions.md#how-to-tell-whether-a-connection-supports-sessions). +Note that checking the maxWireVersion does not guarantee that the deployment supports transactions, for example a +MongoDB 4.0 replica set using MMAPv1 will report maxWireVersion 7 but does not support transactions. In this case, +Drivers rely on the deployment to report an error when a transaction is started. + +Drivers MUST increment the `txnNumber` for the corresponding server session. + +In programming languages that support resource management blocks, startTransaction MAY be used to initiate such a block: + +```python +with client.start_session() as s: + with s.start_transaction(): + collection_one.insert_one(doc1, session=s) + s.commit_transaction() +``` + +The exact API SHOULD match the idioms of the programming language. Depending on the conventions of the programming +language, exiting the block without calling commitTransaction MAY automatically abort the transaction, or MAY abort the +transaction when exiting due to an exception and commit it when exiting normally. The driver MUST NOT automatically +commit the transaction when exiting the block due to an exception. This means that for languages that use an RAII +pattern for resource management blocks, if object destruction can't tell if the containing scope has exited normally or +for an exception, object destruction MUST NOT automatically commit the transaction. + +If the driver returns a type to support resource management blocks, the type MUST NOT be named "Transaction". The type +MAY be named "TransactionContext", "TransactionScopeGuard" or something similar for your language. See +[Why is there no Transaction object?](#why-is-there-no-transaction-object) + +#### commitTransaction + +This method commits the currently active transaction on this session. Drivers MUST run a commitTransaction command with +the writeConcern and, if configured, the maxCommitTimeMS from TransactionOptions. Drivers MUST report an error when the +command fails or the command succeeds but contains a writeConcernError. This session is in the "transaction committed" +state after this method returns — even on error. + +If this session is in the "no transaction" state, then Drivers MUST raise an error containing the message "No +transaction started". + +If this session is in the "transaction aborted" state, then Drivers MUST raise an error containing the message "Cannot +call commitTransaction after calling abortTransaction". + +If this session is already in the "transaction committed" state, then Drivers MUST re-run the previous +commitTransaction. + +It is valid to call commitTransaction when the session is in the "starting transaction" or "transaction in progress" +state. When the session is in the "starting transaction" state, meaning no operations have been performed on this +transaction, drivers MUST NOT run the commitTransaction command. + +commitTransaction is a retryable write command. Drivers MUST retry once after commitTransaction fails with a retryable +error, including a handshake network error, according to the Retryable Writes Specification, regardless of whether +retryWrites is set on the MongoClient or not. If a commitTransaction fails with a +[retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error), the command MUST be +retried as specified in [Interaction with Client Backpressure](#interaction-with-client-backpressure). + +When retrying a commitTransaction attempt, if the previous commitTransaction attempt failed with a +[retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error), drivers MUST apply +the write concern modification rules as outlined in the +[Interaction With Client Backpressure](#interaction-with-client-backpressure) to determine whether or not to modify the +command's writeConcern. If the command failed with any other error, drivers MUST follow the rules for write concern +modification as outlined in [writeConcern for commitTransaction attempts](#writeconcern-for-committransaction-attempts). + +Drivers MUST add error labels to certain errors when commitTransaction fails. See the +[Error reporting changes](#error-reporting-changes) and [Error Labels](#error-labels) sections for a precise +description. + +##### writeConcern for commitTransaction attempts + +When commitTransaction is retried, either by the driver's internal retry logic or explicitly by the user calling +commitTransaction again, drivers MUST apply `w: majority` to the write concern of the commitTransaction command. If the +transaction is using a [writeConcern](#writeconcern) that is not the server default (i.e. specified via +TransactionOptions during the `startTransaction` call or otherwise inherited), any other write concern options (e.g. +`wtimeout`) MUST be left as-is when applying `w: majority`. Finally, if the modified write concern does not include a +`wtimeout` value, drivers MUST also apply `wtimeout: 10000` to the write concern in order to avoid waiting forever (or +until a socket timeout) if the majority write concern cannot be satisfied. See +[Majority write concern is used when retrying commitTransaction](#majority-write-concern-is-used-when-retrying-committransaction). + +#### abortTransaction + +This method aborts the currently active transaction on this session. Drivers MUST run an abortTransaction command with +the transaction’s writeConcern. When this method completes the session moves to the "transaction aborted" state. + +It is only valid to call abortTransaction when the session is in the "starting transaction" or "transaction in progress" +state, otherwise drivers MUST raise an error without modifying transaction state. + +If this session is in the "no transaction" state, then drivers MUST raise an error containing the message "No +transaction started". + +If this session is in the "transaction committed" state, then drivers MUST raise an error containing the message "Cannot +call abortTransaction after calling commitTransaction". + +If this session is already in the "transaction aborted" state, then drivers MUST raise an error containing the message +"Cannot call abortTransaction twice". + +It is valid to call abortTransaction when the session is in the "starting transaction" or "transaction in progress" +state. When the session is in the "starting transaction" state, meaning, no operations have been performed on this +transaction, drivers MUST NOT run the abortTransaction command. + +abortTransaction is a retryable write command. Drivers MUST retry after abortTransaction fails with a retryable error +according to the [Retryable Writes Specification](../retryable-writes/retryable-writes.md), including a handshake +network error, regardless of whether retryWrites is set on the MongoClient or not. If a abortTransaction fails with a +[retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error), the command MUST be +retried as specified in [Interaction with Client Backpressure](#interaction-with-client-backpressure). + +If the operation times out or fails with a non-retryable error, drivers MUST NOT propagate errors from the +abortTransaction command. Errors from abortTransaction are meaningless to the application because they cannot do +anything to recover from the error. The transaction will ultimately be aborted by the server anyway either upon reaching +an age limit or when the application starts a new transaction on this session, see +[Drivers ignore all abortTransaction errors](#drivers-ignore-all-aborttransaction-errors). + +#### endSession changes + +This method ends a ClientSession. Drivers MUST call abortTransaction if this session is in the "transaction in progress" +state in order to release resources on the server. Drivers MUST ignore any errors raised by abortTransaction while +ending a session. + +### Error reporting changes + +This spec introduces the concept of an "error label". Which labels are applied to an error may be communicated from the +server to the client, or determined client-side. Any error reported by the driver in response to a server error, server +selection error, or network error MUST have an API for determining whether it has a given label. In programming +languages that use class inheritance hierarchies for exceptions, the presence of an error label MUST NOT affect an +exception's class. Error labels MUST be expressed as a collection of text strings, and it MUST be possible for +applications to check if an error has a label that is not yet specified in MongoDB 4.0. Drivers MAY define constants for +error label strings that are known at this time. + +Drivers MAY implement an error label API similar to the following: + +```python +try: + session.commit_transaction() +except (OperationFailure, ConnectionFailure) as exc: + if exc.has_error_label("UnknownTransactionCommitResult"): + print("tried to commit, don't know the outcome") +``` + +Drivers MAY expose the list of all error labels for an exception object. + +Drivers MUST add the error label "TransientTransactionError" to network errors thrown in a transaction except for +network errors thrown during commitTransaction. + +## Transactions Wire Protocol + +The server requires each operation executed within a transaction to provide an `lsid` and `txnNumber` in its command +document. Each field is obtained from the ClientSession object passed to the operation from the application. Drivers +will be responsible for maintaining a monotonically increasing transaction number for each ServerSession used by a +ClientSession object. The `txnNumber` is incremented by the call to startTransaction and remains the same for all +commands in the transaction. + +Drivers that pool ServerSessions MUST preserve the transaction number when reusing a server session from the pool with a +new ClientSession (this can be tracked as another property on the driver's object for the server session). + +Drivers MUST ensure that each transaction specifies a transaction number larger than any previously used transaction +number for its session ID. + +### **Constructing commands within a transaction** + +#### Behavior of the startTransaction field + +The first command within a multi-statement transaction MUST include `startTransaction:true`. Subsequent commands MUST +NOT include the `startTransaction` field. + +#### Behavior of the autocommit field + +All operations within a multi-statement transaction (including commitTransaction and abortTransaction) MUST include +`autocommit:false`, to distinguish them from single-statement retryable writes. + +#### Behavior of the readConcern field + +Any command that marks the beginning of a transaction MAY include a `readConcern` argument with an optional `level` and +`afterClusterTime` fields. Read concern level 'local', 'majority', and 'snapshot' are all supported, although they will +all have the same behavior as "snapshot" in MongoDB 4.0. To support causal consistency, if `readConcern` +`afterClusterTime` is specified, then the server will ensure that the transaction’s read timestamp is after the +`afterClusterTime`. + +All commands of a multi-statement transaction subsequent to the initial command MUST NOT specify a `readConcern`, since +the `readConcern` argument is only needed to establish the transaction’s read timestamp. If a `readConcern` argument is +specified on a subsequent (non-initial) command, the server will return an error. + +Read concern level "snapshot" is new in MongoDB 4.0 and can only be used when starting a transaction. The server will +return an error if read concern level "snapshot" is specified on a command that is not the start of a transaction. +Drivers MUST rely on the server to report an error if read concern level snapshot is used incorrectly. + +#### Behavior of the writeConcern field + +The commitTransaction and abortTransaction commands are the only commands of a multi-statement transaction that allow a +`writeConcern` argument. If a `writeConcern` argument is given on any other command of a transaction, the server will +return an error. The `writeConcern` argument of the commitTransaction and abortTransaction commands has semantics +analogous to existing write commands. + +#### Behavior of the recoveryToken field + +Only included for sharded transactions and only when running a commitTransaction or abortTransaction command. See the +[recoveryToken field](#recoverytoken-field) section for more info. + +#### Constructing the first command within a transaction + +When constructing the first command within a transaction, drivers MUST add the `lsid`, `txnNumber`, `startTransaction`, +and `autocommit` fields. + +Drivers MUST include the transaction's readConcern in the first command in a transaction if and only if the readConcern +is supplied and not the server's default. The readConcern MUST NOT be inherited from the collection, database, or client +associated with the driver method that invokes the first command. + +Drivers MUST NOT add to subsequent commands the readConcern from the transaction or any readConcern inherited from the +collection, database, or client. + +This is an example of an insert command that begins a server transaction: + +```typescript +{ + insert : "test", + documents : [{}], + lsid : { id : } + txnNumber: NumberLong(1), + // The "level" is optional, supported values are "local", "majority" + // and "snapshot". "afterClusterTime" is only present in causally + // consistent sessions. + readConcern : { + level : "snapshot", + afterClusterTime : Timestamp(42,1) + }, + startTransaction : true, + autocommit : false +} +``` + +This command uses the readConcern set on the transaction's TransactionOptions during the `startTransaction` call. It is +not inherited from a client, database, or collection at the time of the first command. + +The session transitions to the "transaction in progress" state after completing the first command within a transaction — +even on error. + +#### Constructing any other command within a transaction + +When constructing any other command within a transaction, drivers MUST add the `lsid`, `txnNumber`, and `autocommit` +fields. Drivers MUST NOT automatically add the `writeConcern`, `readConcern`, or `startTransaction` fields. This is an +example of a find command within a transaction: + +```typescript +{ + find : "test", + filter : {}, + lsid : { id : } + txnNumber : NumberLong(1), + autocommit : false +} +``` + +#### Generic RunCommand helper within a transaction + +If your driver offers a generic RunCommand method on your database object, the driver MUST add the `lsid`, `autocommit`, +and `txnNumber` fields. If the RunCommand operation is the first operation in a transaction then the driver MUST also +add the `startTransaction` and `readConcern` fields. A driver MUST do this without modifying any data supplied by the +application (e.g. the command document passed to RunCommand). If the user supplies an explicit readConcern as an +argument to the runCommand method in a transaction, the client MUST raise an error with the message "Cannot set read +concern after starting a transaction." + +The RunCommand method is considered a read operation and MUST use the transaction’s read preference. + +The behavior is not defined if the command document passed to RunCommand already contains some of the transaction +fields. + +### **Interaction with Causal Consistency** + +Drivers MUST add `readConcern.afterClusterTime` to the command that starts a transaction in a causally consistent +session -- even if the command is a write. Drivers MUST NOT add `readConcern.afterClusterTime` to subsequent commands in +a transaction. + +### **Interaction with Retryable Writes** + +In MongoDB 4.0 the only supported retryable write commands within a transaction are commitTransaction and +abortTransaction. Therefore drivers MUST NOT retry write commands within transactions even when retryWrites has been +enabled on the MongoClient, unless the server response is a +[retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error). + +In addition, drivers MUST NOT add the RetryableWriteError label to any error that occurs during a write command within a +transaction (excepting commitTransation and abortTransaction), even when retryWrites has been enabled on the +MongoClient. + +Drivers MUST retry the commitTransaction and abortTransaction commands even when retryWrites has been disabled on the +MongoClient. commitTransaction and abortTransaction are retryable write commands and MUST be retried according to the +[Retryable Writes Specification](../retryable-writes/retryable-writes.md). + +Retryable writes and transactions both use the `txnNumber` associated with a ServerSession. For retryable writes, +`txnNumber` would normally increment before each retryable command, whereas in a transaction, the `txnNumber` is +incremented at the start and then stays constant, even for retryable operations within the transaction. When executing +the commitTransaction and abortTransaction commands within a transaction drivers MUST use the same `txnNumber` used for +all preceding commands in the transaction. + +### **Interaction with Client Backpressure** + +All commands in a transaction are subject to the +[Client Backpressure Specification](../client-backpressure/client-backpressure.md), and MUST be retried accordingly. +This includes the initial command with `startTransaction:true`, the abortTransaction and commitTransaction commands, as +well as any read or write commands attempted during the transaction. + +When a commitTransaction attempt fails with a retryable overload error: + +- If a commitTransaction attempt has already failed with an error that is not a + [retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error), drivers MUST + follow the instructions for modifying writeConcern as outlined in the + [writeConcern for commitTransaction attempts](#writeconcern-for-committransaction-attempts) section for the next + retry attempt. +- Otherwise, drivers MUST retry using the same write concern as was used for the most recently failed commitTransaction + attempt. + +See +[Majority write concern is used when retrying commitTransaction](#majority-write-concern-is-used-when-retrying-committransaction) +for discussion on why majority write concern is sometimes needed on commitTransaction retries. + +If executing the first command within a transaction fails with a +[retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error), and another attempt +is executed, the command executed in the retry attempt must be treated as the first command within a transaction. + +### **Server Commands** + +#### commitTransaction + +The commitTransaction server command has the following format: + +```typescript +{ + commitTransaction : 1, + lsid : { id : }, + txnNumber : , + autocommit : false, + writeConcern : {...}, + maxTimeMS: , + recoveryToken : {...} +} +``` + +#### abortTransaction + +The abortTransaction server command has the following format: + +```typescript +{ + abortTransaction : 1, + lsid : { id : }, + txnNumber : , + autocommit : false, + writeConcern : {...}, + recoveryToken : {...} +} +``` + +Both commands MUST be sent to the admin database. + +The server response has the following format: + +```typescript +{ ok : 1 } +``` + +In case of an error, the server response has the following format: + +```typescript +{ ok : 0, errmsg : "...", code : , errorLabels: ["Label"] } +``` + +In case of a write concern error, the server response has the following format: + +```typescript +{ ok : 1, writeConcernError: {code: , errmsg : "..."} } +``` + +## Sharded Transactions + +MongoDB 4.2 (maxWireVersion 8) introduces support for sharded transactions. Sharded transactions support all of the same +features as single replica set transaction but introduce two new driver concepts: mongos pinning and the `recoveryToken` +field. + +### Mongos Pinning + +Drivers MUST send all commands for a single transaction to the same mongos (excluding retries of commitTransaction and +abortTransaction). + +After the driver selects a mongos for the first command within a transaction, the driver MUST pin the ClientSession to +the selected mongos. Drivers MUST send all subsequent commands that are part of the same transaction (excluding certain +retries of commitTransaction and abortTransaction) to the same mongos. + +#### When to unpin + +Drivers MUST unpin a ClientSession in the following situations: + +1. The transaction is aborted. The session MUST be unpinned regardless of whether or the `abortTransaction` command + succeeds or fails, or was executed at all. If the operation fails with a retryable error, the session MUST be + unpinned before performing server selection for the retry. +2. Any operation in the transaction, including `commitTransaction` fails with a TransientTransactionError. Transient + errors indicate that the transaction in question has already been aborted or that the pinnned mongos is + down/unavailable. Unpinning the session ensures that a subsequent `abortTransaction` (or `commitTransaction`) does + not block waiting on a server that is unreachable. +3. Any `commitTransaction` attempt fails with an `UnknownTransactionCommitResult` error label. If the error is also + considered retryable, the session MUST be unpinned before performing server selection for the retry. +4. A new transaction is started on the ClientSession after the previous transaction has been committed. The session MUST + be unpinned before performing server selection for the first operation of the new transaction. +5. A non-transactional operation is performed using the ClientSession. The session MUST be unpinned before performing + server selection for the operation. +6. The ClientSession is ended either explicitly via `endSession` method, or implicitly when supported by the driver. + +Note that committing a transaction on a pinned ClientSession MUST NOT unpin the session as `commitTransaction` may be +called multiple times. + +#### Pinning in Load Balancer Mode + +See the [Load Balancer Specification](../load-balancers/load-balancers.md#connection-pooling) for details. + +### recoveryToken field + +The `recoveryToken` field enables the driver to recover a sharded transaction's outcome on a new mongos when the +original mongos is no longer available.[^1] + +Every successful (`ok:1`) command response in a sharded transaction includes a `recoveryToken` field. Drivers MUST track +the most recently received `recoveryToken` field and MUST append this field to any subsequent commitTransaction or +abortTransaction commands. Tracking the most recently returned `recoveryToken` allows the server to update the +`recoveryToken` mid-transaction if needed. + +Drivers MUST clear a session's cached `recoveryToken` when transitioning to the "no transaction" or "starting +transaction" state. + +Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the +contents of the document. + +## Error Reporting and Retrying Transactions + +### Error Labels + +Starting in MongoDB 4.0, any command error may include a top level "errorLabels" field. The field contains an array of +string error labels. + +### Transient Transaction Error + +- Any command error that includes the "TransientTransactionError" error label in the "errorLabels" field. In the case of + command errors, the server adds the label. +- Any network error or server selection error encountered running any command besides commitTransaction in a + transaction. In the case of network errors or server selection errors where the client receives no server reply, the + client MUST add the label. If a network error occurs while running the commitTransaction command then it is not + known whether the transaction committed or not, and thus the "TransientTransactionError" label MUST NOT be added. +- `PoolClearedError`. Driver MUST add the label to this error. + +#### Retrying transactions that fail with TransientTransactionError + +If an exception with the "TransientTransactionError" label is thrown, an application can retry the entire transaction +from the beginning with a reasonable expectation that it will succeed. For example: + +```python +def run_transaction(client): + with client.start_session() as s: + with s.start_transaction(): + collection_one.insert_one(doc1, session=s) + collection_two.insert_one(doc2, session=s) + +while True: + try: + return run_transaction(client) + except (OperationFailure, ConnectionFailure) as exc: + if exc.has_error_label("TransientTransactionError"): + print("Transient transaction error, retrying...") + continue + raise +``` + +In the above example, a transaction will never be committed twice. The retry loop ends when the transaction commits +successfully or the transaction fails with a non-transient error. + +An example of a non-transient transaction error is DuplicateKeyError, which causes the server to abort the transaction. +Retrying a transaction that causes a DuplicateKeyError will again (likely) abort the transaction, therefore such an +error is not labeled "transient." + +### UnknownTransactionCommitResult + +The commitTransaction command is considered a retryable write. The driver will automatically retry the commitTransaction +after a retryable error. Although this adds a layer of protection, the driver’s retry attempt of a commitTransaction may +again fail with a retryable error. In that case, both the driver and the application do not know the state of the +transaction. + +The driver MUST add the "UnknownTransactionCommitResult" error label when commitTransaction fails with a server +selection error, network error, retryable writes error, MaxTimeMSExpired error, or write concern failed / timeout. (See +[A server selection error is labeled UnknownTransactionCommitResult](#a-server-selection-error-is-labeled-unknowntransactioncommitresult) +for justification.) The approximate meaning of the UnknownTransactionCommitResult label is, "We don't know if your +commit has satisfied the provided write concern." The only write concern errors that are not labeled with +"UnknownTransactionCommitResult" are CannotSatisfyWriteConcern (which will be renamed to the more precise +UnsatisfiableWriteConcern in 4.2, while preserving the current error code) and UnknownReplWriteConcern. These errors +codes mean that the provided write concern is not valid and therefore a retry attempt would fail with the same error. + +In the case that the commitTransaction fails with a retryable writes error, that error will have both an +UnknownTransactionCommitResult label and a RetryableWriteError label. This is currently the only scenario in which an +error can be assigned two error labels. + +#### Retrying commitTransaction + +If an exception with this label is thrown, an application can safely call commitTransaction again. If this attempt +succeeds it means the transaction has committed with the provided write concern. If this attempt fails it may also have +the "UnknownTransactionCommitResult" error label. For example: + +```python +def run_transaction_and_retry_commit(client): + with client.start_session() as s: + with s.start_transaction(): + collection_one.insert_one(doc1, session=s) + collection_two.insert_one(doc2, session=s) + while True: + try: + s.commit_transaction() + break + except (OperationFailure, ConnectionFailure) as exc: + if exc.has_error_label("UnknownTransactionCommitResult"): + print("Unknown commit result, retrying...") + continue + raise + +while True: + try: + return run_transaction_and_retry_commit(client) + except (OperationFailure, ConnectionFailure) as exc: + if exc.has_error_label("TransientTransactionError"): + print("Transient transaction error, retrying...") + continue + raise +``` + +## Handling command errors + +Drivers MUST document that command errors inside a transaction may abort the transaction on the server. An attempt to +commit such transaction will be rejected with `NoSuchTransaction` error. + +## **Test Plan** + +See the [README](tests/README.md) for tests. + +The Python driver serves as a reference implementation. + +## **Design Rationale** + +The design of this specification builds on the [Driver Sessions Specification](../sessions/driver-sessions.md) and +modifies the driver API as little as possible. + +Drivers will rely on the server to yield an error if an unsupported command is executed within a transaction. This will +free drivers from having to maintain a list of supported operations and also allow for forward compatibility when future +server versions begin to support transactions for additional commands. + +### Drivers ignore all abortTransaction errors + +If the driver has cleared its client-side transaction state, then the next operation it performs will be in a new +transaction or no transaction, which will cause any lingering transaction state on the server (associated with this +session) to abort. Therefore abortTransaction can be considered fail-safe, and raising an exception from it only +complicates application code. Applications would have to wrap abortTransaction in an exception-handling block, but have +no useful action to perform in response to the error. + +abortTransaction does, however, raise an error if there is no transaction in progress. We had considered making this +situation raise no error, as well. However, we want to raise an error from abortTransaction if there is no transaction, +because it discourages an antipattern like this: + +```python +s.start_transaction() +try: + coll.insert_one({}, session=s) + s.commit_transaction() +except: + # We don't know if it was the insert_one, the commit, + # or some other operation that failed, so we must not + # commit the transaction. + s.abort_transaction() # Raises a client-side error +``` + +If a user puts "commit" in the same exception handling block as the other operations in the transaction, they don't know +whether to retry the commit or the whole transaction on error. We want such code to raise an exception. One chance we +have to do that is if a commit fails with a network error and enters the exception handling block, where +abortTransaction throws "Cannot call abortTransaction after commitTransaction". + +### Drivers add the "TransientTransactionError" label to network errors + +When any non-commitTransaction command fails with a network error within a transaction Drivers add the +"TransientTransactionError" label because the client doesn't know if it has modified data in the transaction or not. +Therefore it must abort and retry the entire transaction to be certain it has executed each command in the transaction +exactly once. + +Adding the "TransientTransactionError" label allows applications to use the the same error label API for both network +errors and command errors. This also allows applications to distinguish between a network error that occurs within a +transaction from a network error that occurs while committing a transaction. + +### Transactions in GridFS + +The GridFS spec has not been updated to support sessions, however some drivers have already implemented support for it +on their own. When the GridFS spec has been updated to support sessions, then drivers that implement that spec MUST also +support transactions in GridFS because all APIs that allow sessions MUST support transactions. + +Drivers that have already implemented session support in GridFS MUST also support transactions in GridFS. Drivers that +have not implemented ClientSession support in GridFS are not required to support transactions (or sessions) in GridFS. + +This spec does not require all drivers to implement transaction support in GridFS because transactions in GridFS are not +very useful: transactions in 4.0 are too limited in time and space to operate on large GridFS files. Additionally, +GridFS as specified already has some basic guarantees that make transactions less necessary: files are immutable and +they are created "atomically", from the primary's perspective, because the file entry is only saved after all chunks are +uploaded. + +### Causal Consistency with RunCommand helper + +Causal Consistency alone only applies to commands that read, and we don't want to parse the document passed to +runCommand to see if it's a command that reads. In a transaction, however, any command at all that starts a transaction +must include `afterClusterTime`, so we can add `afterClusterTime` to the document passed to runCommand without adding +per-command special logic to runCommand. + +### Calling commitTransaction with the generic runCommand helper is undefined behavior + +Applications should only use the ClientSession API to manage transactions. Applications should not use a generic +runCommand helper to run the commitTransaction or abortTransaction commands directly. This spec does not define the +behavior of calling such commands, consistent with other drivers specifications that do not define the behavior of +calling directly commands for which helper methods are available The purpose of the generic runCommand method is to +execute a command directly with minimum additional client-side logic. + +## **Dependencies** + +This specification depends on: + +1. [Driver Sessions Specification](../sessions/driver-sessions.md) +2. [Retryable Writes Specification](../retryable-writes/retryable-writes.md) + +## **Backwards Compatibility** + +The API changes to support transactions extend the existing API but do not introduce any backward breaking changes. +Existing programs that do not make use of transactions will continue to compile and run correctly. + +## **Reference Implementation** + +The [Python driver](https://github.com/mongodb/mongo-python-driver/) serves as a reference implementation. + +## **Future work** + +- Support retryable writes within a transaction. + +- Support transactions on secondaries. In this case, drivers would be required to pin a transaction to the server + selected for the initial operation. All subsequent operations in the transaction would go to the pinned server. + +- Support for transactions that read from multiple nodes in a replica set. One interesting use case would be to run a + single transaction that performs low-latency reads with readPreference "nearest" followed by some writes. + +- Support for unacknowledged transaction commits. This might be useful when data consistency is paramount but durability + is optional. Imagine a system that increments two counters in two different collections. The system may want to use + transactions to guarantee that both counters are always incremented together or not at all. + +## **Justifications** + +### Why is there no Transaction object? + +In order to use transactions an application already has to create and manage a ClientSession object. Introducing a +Transaction object would result in another object that the application needs to manage. Moreover, a server session can +only have a single transaction in progress at a time. We chose not to introduce a public Transaction object so that +applications only need to manage a single object and to more closely mirror how transactions work on the server. + +Some drivers' startTransaction methods will return an object as part of the language's resource management block +protocol. The object returned by startTransaction MUST NOT be named Transaction, in order to reserve that name for some +future API extension. Additionally, by avoiding the name Transaction, we prevent users from thinking they can run +multiple transactions in a session. Finally, we avoid the temptation to diverge from this spec's API by adding a +commit() or abort() method to the object returned by startTransaction. Committing and aborting a transaction is the +responsibility of the ClientSession object in all drivers. + +### Why is readPreference part of TransactionOptions? + +Providing a read preference for the entire transaction makes it easier for applications that use one or more non-primary +read preferences for non-transactional reads to run transactions under a single, primary read-preference. Applications +only need to set primary read preference on the transaction instead of changing the read preference of all operations. + +Because primary is the only read preference allowed with transactions in MongoDB 4.0, this specification could have +omitted TransactionOptions.readPreference, or at least defaulted the read preference to primary instead of inheriting +the client's read preference. However, this would have required a breaking change circa MongoDB 4.2 when we introduce +secondary reads in transactions: TransactionOptions will inherit the client's read preference in 4.2, so for the sake of +future-compatibility, TransactionOptions inherits the client's read preference now. + +We considered defaulting TransactionOptions.readPreference to primary in 4.0, overriding the client's read preference by +default for convenience. However, for consistency with other options-inheritance rules in our specifications, +transactions MUST inherit the client's read preference. + +In MongoDB 4.0, the error "read preference in a transaction must be primary" is thrown whenever the application attempts +a read operation in a transaction with a non-primary read preference. We considered throwing this error from +startTransaction instead, to make the error more deterministic and reduce the performance burden of re-checking the +TransactionOptions on each operation. However, this behavior will have to change when we introduce secondary reads in +transactions. There will then be new error scenarios, such as a transaction with secondary reads followed by a write. It +won't be possible in the future for startTransaction to check that the read preference is correct for all operations the +application will perform in the transaction. Therefore, we specify now that the readPreference must be checked +per-operation. (However, we have not completely planned how read preference validation will behave in MongoDB 4.2.) + +*Update 28.Oct.20214* + +Note this section is retained in the spec for historical reasons and that the read preference in transactions must +always be primary. + +### Users cannot pass readConcern or writeConcern to operations in transactions + +For drivers that allow readConcern and/or writeConcern to be passed to a particular operation, If the driver did not +prohibit the readConcern parameter to methods in a transaction, the following code would be ambiguous: + +```python +client = MongoClient("mongodb://localhost/?readConcernLevel=majority") +with client.start_session() as s: + # Transaction uses readConcern majority. + with s.start_transaction(): + # The first command in a transaction. Which readConcern? + client.db.collection.distinct( + readConcern={'level': 'snapshot'}, + session=s) +``` + +In this scenario, the driver must choose which of the two possible readConcerns to use for the *first* command in the +transaction. The server will accept either without error, so the ambiguity MUST be resolved by raising a client-side +error. + +We *could* specify that if a user passes an explicit writeConcern to an operation in a transaction, that the driver +passes this writeConcern to the server. The server correctly returns an error in this scenario; there is not the same +ambiguity with an explicit writeConcern as there is with an explicit readConcern passed to the first operation in a +transaction. For consistency, however, we specify that an explicit writeConcern passed to an operation in a transaction +provokes a client-side error, the same as for readConcern. + +Another alternative is to silently ignore the readConcern and/or writeConcern that the user has explicitly provided to a +particular operation in a transaction. This would be a surprising and undetectable deviation from the user's explicit +intent. + +On the other hand, if a user configures the write concern of a client, database, or collection, and then configures the +same option on a transaction, the transaction's configuration overrides the inherited configuration: + +```python +client = MongoClient("mongodb://localhost/?w=majority") +with client.start_session() as s: + with s.start_transaction(writeConcern={'w': 1}): + # Uses w: 1. + client.db.collection.insert_one( + {'_id': 1}, + session=s) +``` + +In this case the transaction options express a more immediate user intent than the client options, so it is not +surprising to override the client options. + +### Aggregate with write stage is a read operation + +We intend to migrate away from designs that require drivers to inspect the contents of the aggregation pipeline and +override user read preferences for aggregate with a write stage (e.g. `$out`, `$merge`). In general, our specifications +should stop defining different behaviors based on the contents of commands. + +### A server selection error is labeled UnknownTransactionCommitResult + +Drivers add the "UnknownTransactionCommitResult" to a server selection error from commitTransaction, even if this is the +first attempt to send commitTransaction. It is true in this case that the driver knows the result: the transaction is +definitely not committed. However, the "UnknownTransactionCommitResult" label properly communicates to the application +that calling commitTransaction again may succeed. + +## FAQ + +### What commands can be run in a transaction? + +The following commands are allowed inside transactions: + +1. find +2. getMore + - Note that it is not possible to start a transaction with a getMore command, the cursor must have been created + within the transaction in order for the getMore to succeed. +3. killCursors +4. insert, including into a non-existing collection that implicitly creates it +5. update +6. delete +7. findAndModify +8. aggregate (including `$lookup`) + - The `$out` and `$merge` stages are prohibited. +9. distinct +10. geoSearch +11. create +12. createIndexes on an empty collection created in the same transaction or on a non-existing collection +13. bulkWrite + +### Why don’t drivers automatically retry commit after a write concern timeout error? + +A write concern timeout error indicates that the command succeeded but failed to meet the specified writeConcern within +the given time limit. Attempting to retry would implicitly double the application’s wtimeout value so drivers do not +automatically retry. + +Note: this applies only to the driver's internal retry-once behavior. Write concern timeout errors will be labeled with +"UnknownTransactionCommitResult", which signals that higher-level code may retry. + +### What happens when a command object passed to RunCommand already contains a transaction field (eg. lsid, txnNumber, etc...)? + +The behavior of running such commands in a transaction are undefined. Applications should not run such commands inside a +transaction. + +### Majority write concern is used when retrying commitTransaction + +When retrying commitTransaction, drivers use a majority write concern to ensure the transaction is not applied twice. + +Consider the following scenario: + +1. The driver is connected to a replica set where node A is primary. +2. The driver sends commitTransaction to A with `w:1`. A commits the transaction but dies before it can reply. This + constitutes a retryable error, which means the driver can retry the commitTransaction command. +3. Node B is briefly elected. +4. The driver retries commitTransaction on B with `w:1`, and B replies with a NoSuchTransaction error code and + TransientTransactionError error label. This implies that the driver may retry the entire transaction. +5. Node A revives before B has done any `w:majority` writes and is reëlected as primary. +6. The driver then retries the entire transaction on A where it commits successfully. + +The outcome of this scenario is that two complete executions of the transaction operations are permanently committed on +node A. + +Drivers can avoid this scenario if they always use a majority write concern when retrying commitTransaction. Applying a +majority write concern to step four in the above scenario would lead to one of the following possible outcomes: + +- Node B replies with failed response, which does not include a TransientTransactionError error label. This does not + constitute a retryable error. Control is returned to the user. +- Node B replies with a successful response (e.g. `ok:1`) indicating that the retried commitTransaction has succeeded + durably and the driver can continue. Control is returned to the user. +- Node B replies with a wtimeout error. This does not constitute a retryable error. Control is returned to the user. +- Node B replies with a failure response that includes the TransientTransactionError label, which indicates it is safe + to retry the entire transaction. Drivers can trust that a server response will not include both a write concern + error and TransientTransactionError label (see: [SERVER-37179](https://jira.mongodb.org/browse/SERVER-37179)). + +Adding a majority write concern only when retrying commitTransaction provides a good compromise of performance and +durability. Applications can use `w:1` for the initial transaction attempt for a performance advantage in the happy +path. In the event of retryable error, the driver can upgrade commitTransaction to use `w:majority` and provide +additional guarantees to the user and avoid any risk of committing the transaction twice. Note that users may still be +vulnerable to rollbacks by using `w:1` (as with any write operation). + +While it's possible that the original write concern may provide greater guarantees than majority (e.g. `w:3` in a +three-node replica set, +[custom write concern](https://www.mongodb.com/docs/manual/tutorial/configure-replica-set-tag-sets/#tag-sets-and-custom-write-concern-behavior)), +drivers are not in a position to make that comparison due to the possibility of hidden members or the opaque nature of +custom write concerns. Excluding the edge case where +[writeConcernMajorityJournalDefault](https://www.mongodb.com/docs/manual/reference/replica-configuration/#rsconf.writeConcernMajorityJournalDefault) +has been disabled, drivers can readily trust that a majority write concern is durable, which achieves the primary +objective of avoiding duplicate commits. + +A [retryable overload error](../client-backpressure/client-backpressure.md#retryable-overload-error) indicates that the +server performed no work when executing the command. As such, the scenario above is irrelevant for commitTransaction +attempts which have only failed with retryable overload errors. However, after a commitTransaction fails with an error +that is not a retryable overload error, we no longer have the guarantee that the server has performed no work, and we +must apply a majority write concern to prevent the transaction from being applied twice. + +## **Changelog** + +- 2026-01-09: Specify the handling of client backpressure. + +- 2024-11-01: Clarify collection options inside txn. + +- 2024-11-01: Specify that ClientSession must be unpinned when ended. + +- 2024-10-31: Clarify when drivers must add TransientTransactionError label. + +- 2024-10-28: Note read preference must always be primary in a transaction. + +- 2024-05-08: Add bulkWrite to the list of commands allowed in transactions. + +- 2024-02-15: Migrated from reStructuredText to Markdown. + +- 2023-11-22: Specify that non-transient transaction errors abort the transaction on the server. + +- 2022-10-05: Remove spec front matter and reformat changelog + +- 2022-01-25: Mention the additional case of a retryable handshake error + +- 2022-01-19: Deprecate maxCommitTimeMS in favor of timeoutMS. + +- 2021-04-12: Adding in behaviour for load balancer mode. + +- 2020-04-07: Clarify that all abortTransaction attempts should unpin the session, even if the command is not executed. + +- 2020-04-07: Specify that sessions should be unpinned once a transaction is aborted. + +- 2019-10-21: Specify that a commit error can have two error labels + +- 2019-07-30: Clarify when the cached recoveryToken should be cleared. + +- 2019-06-10: Client-side errors must not change transaction state. + +- 2019-06-07: Mention `$merge` stage for aggregate alongside `$out` + +- 2019-05-13: Add support for maxTimeMS on transaction commit, MaxTimeMSExpired errors on commit are labelled + UnknownTransactionCommitResult. + +- 2019-02-19: Add support for sharded transaction recoveryToken. + +- 2019-02-19: Clarify FAQ entry for not retrying commit on wtimeout + +- 2019-01-18: Apply majority write concern when retrying commitTransaction + +- 2018-11-13: Add mongos pinning to support sharded transaction. + +- 2018-06-18: Explicit readConcern and/or writeConcern are prohibited within transactions, with a client-side error. + +- 2018-06-07: The count command is not supported within transactions. + +- 2018-06-14: Any retryable writes error raised by commitTransaction must be labelled "UnknownTransactionCommitResult". + +[^1]: In 4.2, a new mongos waits for the *outcome* of the transaction but will never itself cause the transaction to be + committed. If the initial commit on the original mongos itself failed to initiate the transaction's commit sequence, + then a retry attempt on a new mongos will block until the transaction is automatically timed out by the cluster. In + this case, the new mongos will return a transient error indicating that the transaction was aborted. diff --git a/source/transactions/transactions.rst b/source/transactions/transactions.rst index 36703b821d..067b2d6f76 100644 --- a/source/transactions/transactions.rst +++ b/source/transactions/transactions.rst @@ -1,1198 +1,3 @@ -================================= -Driver Transactions Specification -================================= - -:Spec Title: Driver Transactions Specification -:Spec Version: 1.1 -:Author: Shane Harvey -:Spec Lead: A\. Jesse Jiryu Davis -:Advisory Group: A\. Jesse Jiryu Davis, Matt Broadstone, Robert Stam, Jeff Yemin, Spencer Brody -:Approver(s): A\. Jesse Jiryu Davis, Andrew Morrow, Bernie Hackett, Dan Pasette, Jeff Yemin, Robert Stam, Spencer Brody, Tess Avitabile -:Informed: drivers@ -:Status: Accepted (Could be Draft, Accepted, Rejected, Final, or Replaced) -:Type: Standards -:Minimum Server Version: 4.0 (The minimum server version this spec applies to) -:Last Modified: 18-June-2018 - -.. contents:: - --------- - -**Abstract** ------------- - -Version 4.0 of the server introduces multi-statement transactions. -This spec builds upon the `Driver Sessions Specification`_ to define how an -application uses transactions and how a driver interacts with the -server to implement transactions. - -The API for transactions must be specified to ensure that all drivers and -the mongo shell are consistent with each other, and to provide a natural -interface for application developers and DBAs who use multi-statement -transactions. - -**META** --------- - -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", -"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this -document are to be interpreted as described in -`RFC 2119 `_. - -**Specification** ------------------ - -**Terms** -~~~~~~~~~ - -This specification uses the terms defined in the -`Driver Sessions Specification`_ and `Retryable Writes Specification`_. -Additional terms are defined below. - -Resource Management Block -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Some programming languages have a concept of a code block that -automatically frees resources when control exits the block. Such a -pattern is known as context managers, "using" statements, RAII, etc.. -This spec refers to such a pattern as a resource management block, -regardless of the programming language. - -Read operation -^^^^^^^^^^^^^^ - -Any CRUD method that uses a read preference. The listIndexes, -listCollections, and listDatabases, and RunCommand helpers are also -considered read operations. - -Write operation -^^^^^^^^^^^^^^^ - -All operations that write and accept a ClientSession argument. All -MongoClient, Database, Collection helpers that write including (but not -limited to) creating, updating, or deleting databases, collections, -indexes, and users. Aggregate (even with $out) is considered a read -operation, see `Aggregate with $out is a read operation`_. - -Retryable Error -^^^^^^^^^^^^^^^ - -An error considered retryable by the `Retryable Writes Specification`_. - -Command Error -^^^^^^^^^^^^^ - -A server response with ok:0. A server response with ok:1 and -writeConcernError or writeErrors is not considered a command error. - -Network Error -^^^^^^^^^^^^^ - -Any error or timeout that occurs while selecting a server or reading -from or writing to a network socket. - -Error Label -^^^^^^^^^^^ - -Starting in MongoDB 4.0, any command error may include a top level -"errorLabels" field. The field contains an array of string error labels. -Drivers may also add error labels to errors that they return. - -Transient Transaction Error -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Any command error that includes the "TransientTransactionError" error -label in the "errorLabels" field. Any network error encountered running -any command other than commitTransaction in a transaction. If a network -error occurs while running the commitTransaction command then it is not -known whether the transaction committed or not, and thus the -"TransientTransactionError" label MUST NOT be added. - -**Naming variations** -~~~~~~~~~~~~~~~~~~~~~ - -This specification defines names for new methods and types. To the -extent possible you SHOULD use these names in your driver. However, -where your driver's and/or language's naming conventions differ you -SHOULD continue to use them instead. For example, you might use -StartTransaction or start_transaction instead of startTransaction. - -**Transaction API** -~~~~~~~~~~~~~~~~~~~ - -Transactions are built on top of Driver Session API. Applications can -run a transaction like this: - -.. code:: python - - with client.start_session() as s: - s.start_transaction() - collection_one.insert_one(doc_one, session=s) - collection_two.insert_one(doc_two, session=s) - s.commit_transaction() - -This section is an overview of the public API for transactions: - -.. code:: typescript - - class TransactionOptions { - /** - * The readConcern to use for this transaction. - */ - Optional readConcern; - - /** - * The writeConcern to use for this transaction. - */ - Optional writeConcern; - - /** - * The readPreference to use for this transaction. - */ - Optional readPreference; - } - - class SessionOptions { - /** - * The default TransactionOptions to use for transactions started - * on this session. - */ - Optional defaultTransactionOptions; - - // Options defined in other specifications... - } - - interface ClientSession { - /** - * Starts a new transaction with the given options. This session's - * defaultTransactionOptions is used when options is omitted. - * Raises an error if this session is already in a transaction. - * - * The return type MAY be non-void if necessary to participate in - * the programming language's resource management block idiom. The - * type of the returned object, if any, MUST NOT be named - * Transaction, see "Why is there no Transaction object?" - */ - void startTransaction(Optional options); - - /** - * Commits the currently active transaction in this session. - * Raises an error if this session has no transaction. - */ - void commitTransaction(); - - /** - * Aborts the currently active transaction in this session. - * Raises an error if this session has no transaction. - */ - void abortTransaction(); - - /** - * Aborts any currently active transaction and ends this session. - * MUST NOT raise an error. - */ - void endSession(); - - // Methods defined in other specifications... - } - -Each new member is documented below. - -**TransactionOptions** -~~~~~~~~~~~~~~~~~~~~~~ - -It is expected that the set of TransactionOptions will grow over time, -TransactionOptions MUST be designed such that future options can be -added without breaking backward compatibility. - -readConcern -^^^^^^^^^^^ - -The readConcern to use for the first command, and only the first -command, in a transaction. Server transactions are started lazily with -the first command using this session. For supported values see -`Behavior of the readConcern field`_. - -Note that the readConcern property is optional. The default value is -NULL. If readConcern is NULL the value will be inherited from this -session’s defaultTransactionOptions. If defaultTransactionOptions itself -is NULL or the readConcern in defaultTransactionOptions is NULL, the -readConcern is inherited from the MongoClient associated with this -session. - -Drivers MUST override all other collection, database, or client -readConcerns with the transaction’s readConcern. Drivers MUST add this -readConcern to the first command in a transaction if and only if the -readConcern is supplied and not the default. Drivers MUST NOT add to -subsequent commands the readConcern from the transaction or any -readConcern inherited from the collection, database, or client. - -If the user supplies an explicit readConcern via a method option, the driver -MUST raise an error with the message "Cannot set read concern after starting a -transaction." -See `Users cannot pass readConcern or writeConcern to operations in transactions`_. - -writeConcern -^^^^^^^^^^^^ - -The writeConcern to use for the commitTransaction and abortTransaction -commands. Note that the writeConcern property is optional. The default -value is NULL. If writeConcern is NULL the value will be inherited -from this session’s defaultTransactionOptions. If -defaultTransactionOptions itself is NULL or the writeConcern in -defaultTransactionOptions is NULL, the writeConcern is inherited from -the MongoClient associated with this session. - -If the writeConcern is not the server default, then Drivers MUST add -the writeConcern to the commitTransaction and abortTransaction -commands. Drivers MUST NOT add the transaction’s writeConcern or any -writeConcern inherited from the collection, database, or client to any -preceding commands in the transaction. - -If the user supplies an explicit writeConcern via a method option, the driver -MUST raise an error with the message "Cannot set write concern after starting a -transaction." -See `Users cannot pass readConcern or writeConcern to operations in transactions`_. - -Drivers MUST raise an error if the user provides or if defaults would -result in an unacknowledged writeConcern. The Driver Sessions spec -disallows using unacknowledged writes in a session. The error message -MUST contain "transactions do not support unacknowledged write -concerns". - -readPreference -^^^^^^^^^^^^^^ - -The read preference to use for all read operations in this transaction. - -Note that the readPreference property is optional. The default value is -NULL. If readPreference is NULL the value will be inherited from this -session’s defaultTransactionOptions. If defaultTransactionOptions itself -is NULL or the readPreference in defaultTransactionOptions is NULL, the -readPreference is inherited from the MongoClient associated with this -session. - -The transaction’s read preference MUST override all other user -configurable read preferences, with the exception of drivers that allow -an operation level read preference. In this case, the driver MUST respect -the read preference specified by the user, allowing the server to report -an error. - -In MongoDB 4.0, transactions may only read from the primary. If a read -is attempted and the transaction’s read preference is not Primary -drivers MUST raise an error containing the string "read preference in a -transaction must be primary". Drivers MUST NOT validate the read -preference during write operations or in startTransaction. -See `Why is readPreference part of TransactionOptions?`_. - -.. code:: python - - client = MongoClient("mongodb://host/?readPreference=nearest") - coll = client.db.test - with client.start_session() as s: - with s.start_transaction(): - coll.insert_one({}, session=s) - coll.find_one(session=s) # Error: "read preference in a transaction must be primary" - -In the future, we might relax this restriction and allow any read -preference on a transaction. - -**SessionOptions changes** -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -defaultTransactionOptions -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The default TransactionOptions to use for transactions started on this -session. - -**ClientSession changes** -~~~~~~~~~~~~~~~~~~~~~~~~~ - -ClientSession is in one of five states: "no transaction", "starting -transaction", "transaction in progress", "transaction committed", and -"transaction aborted". It transitions among these states according to -the following diagram: - -| |states| -| (`GraphViz source `__) - -.. |states| image:: client-session-transaction-states.png - :width: 6.5in - :height: 3.68056in - - -When a ClientSession is created it starts in the "no transaction" state. -Starting, committing, and aborting a transaction transitions the session -between the "starting transaction", "transaction in progress", -"transaction committed", and "transaction aborted" states. If the -session is in the "transaction aborted" or "transaction committed" -state, then any operation using the session (besides commitTransaction -and abortTransaction) MUST reset the session state to "no transaction". - -Note that "error" is not a state, it represents throwing an error due to -an invalid operation. When such errors are thrown the session state is -unchanged. - -startTransaction -^^^^^^^^^^^^^^^^ - -This method starts a new transaction on this session with the given -TransactionOptions. When options is omitted or if particular options are -not specified, drivers will use the defaultTransactionOptions from -ClientSession.options or inherit them from the session's client, as -described in the text above for each option. This session is in the -"starting transaction" state after this method returns. - -If this session is in the "starting transaction " or "transaction in -progress" state, then Drivers MUST raise an error containing the message -"Transaction already in progress" without modifying any session state. - -startTransaction SHOULD report an error if the driver can detect that -transactions are not supported by the deployment. A deployment does not -support transactions when the deployment does not support sessions, or -maxWireVersion < 7, or the topology type is Sharded, see `How to Check -Whether a Deployment Supports -Sessions `__. -Note that checking the maxWireVersion does not guarantee that the -deployment supports transactions, for example a MongoDB 4.0 replica set -using MMAPv1 will report maxWireVersion 7 but does not support -transactions. In this case, Drivers rely on the deployment to report an -error when a transaction is started. - -Drivers MUST increment the ``txnNumber`` for the corresponding server -session. - -In programming languages that support resource management blocks, -startTransaction MAY be used to initiate such a block: - -.. code:: python - - with client.start_session() as s: - with s.start_transaction(): - collection_one.insert_one(doc1, session=s) - s.commit_transaction() - -The exact API SHOULD match the idioms of the programming language. -Depending on the conventions of the programming language, exiting the -block without calling commitTransaction MAY automatically abort the -transaction, or MAY abort the transaction when exiting due to an -exception and commit it when exiting normally. The driver MUST NOT -automatically commit the transaction when exiting the block due to an -exception. This means that for languages that use an RAII pattern for -resource management blocks, if object destruction can't tell if the -containing scope has exited normally or for an exception, object -destruction MUST NOT automatically commit the transaction. - -If the driver returns a type to support resource management blocks, the -type MUST NOT be named "Transaction". The type MAY be named -"TransactionContext", "TransactionScopeGuard" or something similar for -your language. See `Why is there no Transaction object?`_ - -commitTransaction -^^^^^^^^^^^^^^^^^ - -This method commits the currently active transaction on this session. -Drivers MUST run a commitTransaction command with the writeConcern from -TransactionOptions. Drivers MUST report an error when the command fails -or the command succeeds but contains a writeConcernError. This session -is in the "transaction committed" state after this method returns — even -on error. - -If this session is in the "no transaction" state, then Drivers MUST -raise an error containing the message "No transaction started". - -If this session is in the "transaction aborted" state, then Drivers MUST -raise an error containing the message "Cannot call commitTransaction -after calling abortTransaction". - -If this session is already in the "transaction committed" state, then -Drivers MUST re-run the previous commitTransaction. - -It is valid to call commitTransaction when the session is in the -"starting transaction" or "transaction in progress" state. When the -session is in the "starting transaction" state, meaning no operations -have been performed on this transaction, drivers MUST NOT run the -commitTransaction command. - -commitTransaction is a retryable write command. Drivers MUST retry once -after commitTransaction fails with a retryable error according to the -Retryable Writes Specification, regardless of whether retryWrites is set -on the MongoClient or not. - -Drivers MUST add error labels to certain errors when commitTransaction -fails. See the `Error reporting changes`_ and `Error Labels`_ sections -for a precise description. - -abortTransaction -^^^^^^^^^^^^^^^^ - -This method aborts the currently active transaction on this session. -Drivers MUST run an abortTransaction command with the transaction’s -writeConcern. When this method completes the session moves to the -"transaction aborted" state. - -It is only valid to call abortTransaction when the session is in the -"starting transaction" or "transaction in progress" state, otherwise -drivers MUST raise an error without modifying transaction state. - -If this session is in the "no transaction" state, then drivers MUST -raise an error containing the message "No transaction started". - -If this session is in the "transaction committed" state, then drivers -MUST raise an error containing the message "Cannot call abortTransaction -after calling commitTransaction". - -If this session is already in the "transaction aborted" state, then -drivers MUST raise an error containing the message "Cannot call -abortTransaction twice". - -It is valid to call abortTransaction when the session is in the -"starting transaction" or "transaction in progress" state. When the -session is in the "starting transaction" state, meaning, no operations -have been performed on this transaction, drivers MUST NOT run the -abortTransaction command. - -abortTransaction is a retryable write command. Drivers MUST retry once -after abortTransaction fails with a retryable error according to the -`Retryable Writes Specification`_., regardless of whether retryWrites is set -on the MongoClient or not. - -After the retryable write attempt, drivers MUST ignore all errors from -the abortTransaction command. Errors from abortTransaction are -meaningless to the application because they cannot do anything to -recover from the error. The transaction will ultimately be aborted by -the server anyway either upon reaching an age limit or when the -application starts a new transaction on this session, see `Drivers -ignore all abortTransaction -errors <#drivers-ignore-all-aborttransaction-errors>`__. - -endSession changes -^^^^^^^^^^^^^^^^^^ - -This method ends a ClientSession. Drivers MUST call abortTransaction if -this session is in the "transaction in progress" state in order to -release resources on the server. Drivers MUST ignore any errors raised -by abortTransaction while ending a session. - -Error reporting changes -~~~~~~~~~~~~~~~~~~~~~~~ - -This spec introduces the concept of an "error label". Which labels are -applied to an error may be communicated from the server to the client, -or determined client-side. Any error reported by the driver in response -to a server error, server selection error, or network error MUST have an -API for determining whether it has a given label. In programming -languages that use class inheritance hierarchies for exceptions, the -presence of an error label MUST NOT affect an exception's class. Error -labels MUST be expressed as a collection of text strings, and it MUST be -possible for applications to check if an error has a label that is not -yet specified in MongoDB 4.0. Drivers MAY define constants for error -label strings that are known at this time. - -Drivers MAY implement an error label API similar to the following: - -.. code:: python - - try: - session.commit_transaction() - except (OperationFailure, ConnectionFailure) as exc: - if exc.has_error_label("UnknownTransactionCommitResult"): - print("tried to commit, don't know the outcome") - -Drivers MAY expose the list of all error labels for an exception object. - -Drivers MUST add the error label "TransientTransactionError" to network -errors thrown in a transaction except for network errors thrown during -commitTransaction. - -Transactions Wire Protocol --------------------------- - -The server requires each operation executed within a transaction to -provide an ``lsid`` and ``txnNumber`` in its command document. Each field is -obtained from the ClientSession object passed to the operation from the -application. Drivers will be responsible for maintaining a monotonically -increasing transaction number for each ServerSession used by a -ClientSession object. The ``txnNumber`` is incremented by the call to -startTransaction and remains the same for all commands in the -transaction. - -Drivers that pool ServerSessions MUST preserve the transaction number -when reusing a server session from the pool with a new ClientSession -(this can be tracked as another property on the driver's object for the -server session). - -Drivers MUST ensure that each transaction specifies a transaction number -larger than any previously used transaction number for its session ID. - -**Constructing commands within a transaction** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Behavior of the startTransaction field -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The first command within a multi-statement transaction MUST include -``startTransaction:true``. Subsequent commands MUST NOT include the -``startTransaction`` field. - -Behavior of the autocommit field -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -All operations within a multi-statement transaction (including -commitTransaction and abortTransaction) MUST include ``autocommit:false``, -to distinguish them from single-statement retryable writes. - -Behavior of the readConcern field -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Any command that marks the beginning of a transaction MAY include a -``readConcern`` argument with an optional ``level`` and ``afterClusterTime`` -fields. Read concern level 'local', 'majority', and 'snapshot' are all -supported, although they will all have the same behavior as "snapshot" -in MongoDB 4.0. To support causal consistency, if ``readConcern`` -``afterClusterTime`` is specified, then the server will ensure that the -transaction’s read timestamp is after the ``afterClusterTime``. - -All commands of a multi-statement transaction subsequent to the initial -command MUST NOT specify a ``readConcern``, since the ``readConcern`` argument -is only needed to establish the transaction’s read timestamp. If a -``readConcern`` argument is specified on a subsequent (non-initial) command, -the server will return an error. - -Read concern level "snapshot" is new in MongoDB 4.0 and can only be used -when starting a transaction. The server will return an error if read -concern level "snapshot" is specified on a command that is not the start -of a transaction. Drivers MUST rely on the server to report an error if -read concern level snapshot is used incorrectly. - -Behavior of the writeConcern field -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The commitTransaction and abortTransaction commands are the only -commands of a multi-statement transaction that allow a ``writeConcern`` -argument. If a ``writeConcern`` argument is given on any other command of a -transaction, the server will return an error. The ``writeConcern`` argument -of the commitTransaction and abortTransaction commands has -semantics analogous to existing write commands. - -Constructing the first command within a transaction -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When constructing the first command within a transaction, drivers MUST -add the ``lsid``, ``txnNumber``, ``readConcern``, ``startTransaction`` and -``autocommit`` fields. This is an example of an insert command that -begins a server transaction: - -.. code:: typescript - - { - insert : "test", - documents : [{}], - lsid : { id : } - txnNumber: NumberLong(1), - // The "level" is optional, supported values are "local", "majority" - // and "snapshot". "afterClusterTime" is only present in causally - // consistent sessions. - readConcern : { - level : "snapshot", - afterClusterTime : Timestamp(42,1) - }, - startTransaction : true, - autocommit : false - } - -The session transitions to the "transaction in progress" state after -completing the first command within a transaction — even on error. - -Constructing any other command within a transaction -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When constructing any other command within a transaction, drivers MUST -add the ``lsid``, ``txnNumber``, and ``autocommit`` fields. Drivers MUST NOT -automatically add the ``writeConcern``, ``readConcern``, or -``startTransaction`` fields. This is an example of a find command -within a transaction: - -.. code:: typescript - - { - find : "test", - filter : {}, - lsid : { id : } - txnNumber : NumberLong(1), - autocommit : false - } - -Generic RunCommand helper within a transaction -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your driver offers a generic RunCommand method on your database -object, the driver MUST add the ``lsid``, ``autocommit``, and ``txnNumber`` fields. -If the RunCommand operation is the first operation in a transaction then -the driver MUST also add the ``startTransaction`` and ``readConcern`` fields. A -driver MUST do this without modifying any data supplied by the -application (e.g. the command document passed to RunCommand). -If the user supplies an explicit readConcern as an argument to the runCommand -method in a transaction, the client MUST raise an error with the message -"Cannot set read concern after starting a transaction." - -The RunCommand method is considered a read operation and MUST use the -transaction’s read preference. - -The behavior is not defined if the command document passed to RunCommand -already contains some of the transaction fields. - -**Interaction with Causal Consistency** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers MUST add ``readConcern.afterClusterTime`` to the command that starts -a transaction in a causally consistent session -- even if the command is -a write. Drivers MUST NOT add ``readConcern.afterClusterTime`` to subsequent -commands in a transaction. - -**Interaction with Retryable Writes** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In MongoDB 4.0 the only supported retryable write commands within a -transaction are commitTransaction and abortTransaction. Therefore -drivers MUST NOT retry write commands within transactions even when -retryWrites has been enabled on the MongoClient. Drivers MUST retry the -commitTransaction and abortTransaction commands even when retryWrites -has been disabled on the MongoClient. commitTransaction and -abortTransaction are retryable write commands and MUST be retried -according to the `Retryable Writes Specification`_. - -Retryable writes and transactions both use the ``txnNumber`` associated with -a ServerSession. For retryable writes, ``txnNumber`` would normally -increment before each retryable command, whereas in a transaction, the -``txnNumber`` is incremented at the start and then stays constant, even for -retryable operations within the transaction. When executing the -commitTransaction and abortTransaction commands within a transaction -drivers MUST use the same ``txnNumber`` used for all preceding commands in -the transaction. - -**Server Commands** -~~~~~~~~~~~~~~~~~~~ - -commitTransaction -^^^^^^^^^^^^^^^^^ - -The commitTransaction server command has the following format: - -.. code:: typescript - - { - commitTransaction : 1, - lsid : { id : }, - txnNumber : , - autocommit : false, - writeConcern : {...} - } - -abortTransaction -^^^^^^^^^^^^^^^^ - -The abortTransaction server command has the following format: - -.. code:: typescript - - { - abortTransaction : 1, - lsid : { id : }, - txnNumber : , - autocommit : false, - writeConcern : {...} - } - -Both commands MUST be sent to the admin database. - -The server response has the following format: - -.. code:: typescript - - { ok : 1 } - -In case of an error, the server response has the following format: - -.. code:: typescript - - { ok : 0, errmsg : "...", code : , errorLabels: ["Label"] } - -In case of a write concern error, the server response has the following -format: - -.. code:: typescript - - { ok : 1, writeConcernError: {code: , errmsg : "..."} } - -Error Reporting and Retrying Transactions ------------------------------------------ - -Error Labels -~~~~~~~~~~~~ - -Starting in MongoDB 4.0, any command error may include a top level -"errorLabels" field. The field contains an array of string error labels. - -TransientTransactionError -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Any command error that includes the "TransientTransactionError" error -label in the "errorLabels" field. Any network error or server selection -error encountered running any command besides commitTransaction in a -transaction. In the case of command errors, the server adds the label; -in the case of network errors or server selection errors where the -client receives no server reply, the client adds the label. - -Retrying transactions that fail with TransientTransactionError -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If an exception with the "TransientTransactionError" label is thrown, an -application can retry the entire transaction from the beginning with a -reasonable expectation that it will succeed. For example: - -.. code:: python - - def run_transaction(client): - with client.start_session() as s: - with s.start_transaction(): - collection_one.insert_one(doc1, session=s) - collection_two.insert_one(doc2, session=s) - - while True: - try: - return run_transaction(client) - except (OperationFailure, ConnectionFailure) as exc: - if exc.has_error_label("TransientTransactionError"): - print("Transient transaction error, retrying...") - continue - raise - -In the above example, a transaction will never be committed twice. The -retry loop ends when the transaction commits successfully or the -transaction fails with a non-transient error. - -An example of a non-transient transaction error is DuplicateKeyError, -which causes the server to abort the transaction. Retrying a transaction -that causes a DuplicateKeyError will again (likely) abort the -transaction, therefore such an error is not labeled "transient." - -UnknownTransactionCommitResult -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The commitTransaction command is considered a retryable write. The -driver will automatically retry the commitTransaction once after a -retryable error. Although this adds a layer of protection, the driver’s -retry attempt of a commitTransaction may again fail with a retryable -error. In that case, both the driver and the application do not know the -state of the transaction. - -The driver MUST add the "UnknownTransactionCommitResult" error label when -commitTransaction fails with a server selection error, network error, retryable -writes error, or write concern failed / timeout. (See -`A server selection error is labeled UnknownTransactionCommitResult`_ -for justification.) The approximate meaning of the -UnknownTransactionCommitResult label is, "We don't know if your commit -has satisfied the provided write concern." The only write concern errors -that are not labeled with "UnknownTransactionCommitResult" are -CannotSatisfyWriteConcern (which will be renamed to the more precise -UnsatisfiableWriteConcern in 4.2, while preserving the current error -code) and UnknownReplWriteConcern. These errors codes mean that the -provided write concern is not valid and therefore a retry attempt would -fail with the same error. - -Retrying commitTransaction -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If an exception with this label is thrown, an application can safely -call commitTransaction again. If this attempt succeeds it means the -transaction has committed with the provided write concern. If this -attempt fails it may also have the "UnknownTransactionCommitResult" error -label. For example: - -.. code:: python - - def run_transaction_and_retry_commit(client): - with client.start_session() as s: - with s.start_transaction(): - collection_one.insert_one(doc1, session=s) - collection_two.insert_one(doc2, session=s) - while True: - try: - s.commit_transaction() - break - except (OperationFailure, ConnectionFailure) as exc: - if exc.has_error_label("UnknownTransactionCommitResult"): - print("Unknown commit result, retrying...") - continue - raise - - while True: - try: - return run_transaction_and_retry_commit(client) - except (OperationFailure, ConnectionFailure) as exc: - if exc.has_error_label("TransientTransactionError"): - print("Transient transaction error, retrying...") - continue - raise - -**Test Plan** -------------- - -See the `README `_ for tests. - -The Python driver serves as a reference implementation. - -**Design Rationale** --------------------- - -The design of this specification builds on the `Driver Sessions Specification`_ -and modifies the driver API as little as possible. - -Drivers will rely on the server to yield an error if an unsupported -command is executed within a transaction. This will free drivers from -having to maintain a list of supported operations and also allow for -forward compatibility when future server versions begin to support -transactions for additional commands. - -Drivers ignore all abortTransaction errors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the driver has cleared its client-side transaction state, then the -next operation it performs will be in a new transaction or no -transaction, which will cause any lingering transaction state on the -server (associated with this session) to abort. Therefore -abortTransaction can be considered fail-safe, and raising an exception -from it only complicates application code. Applications would have to -wrap abortTransaction in an exception-handling block, but have no useful -action to perform in response to the error. - -abortTransaction does, however, raise an error if there is no -transaction in progress. We had considered making this situation raise -no error, as well. However, we want to raise an error from -abortTransaction if there is no transaction, because it discourages an -antipattern like this: - -.. code:: python - - s.start_transaction() - try: - coll.insert_one({}, session=s) - s.commit_transaction() - except: - # We don't know if it was the insert_one, the commit, - # or some other operation that failed, so we must not - # commit the transaction. - s.abort_transaction() # Raises a client-side error - - -If a user puts "commit" in the same exception handling block as the -other operations in the transaction, they don't know whether to retry -the commit or the whole transaction on error. We want such code to raise -an exception. One chance we have to do that is if a commit fails with a -network error and enters the exception handling block, where -abortTransaction throws "Cannot call abortTransaction after -commitTransaction". - -Drivers add the "TransientTransactionError" label to network errors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When any non-commitTransaction command fails with a network error within -a transaction Drivers add the "TransientTransactionError" label because -the client doesn't know if it has modified data in the transaction or -not. Therefore it must abort and retry the entire transaction to be -certain it has executed each command in the transaction exactly once. - -Adding the "TransientTransactionError" label allows applications to use -the the same error label API for both network errors and command errors. -This also allows applications to distinguish between a network error -that occurs within a transaction from a network error that occurs while -committing a transaction. - -Transactions in GridFS -~~~~~~~~~~~~~~~~~~~~~~ - -The GridFS spec has not been updated to support sessions, however some -drivers have already implemented support for it on their own. When the -GridFS spec has been updated to support sessions, then drivers that -implement that spec MUST also support transactions in GridFS because all -APIs that allow sessions MUST support transactions. - -Drivers that have already implemented session support in GridFS MUST -also support transactions in GridFS. Drivers that have not implemented -ClientSession support in GridFS are not required to support -transactions (or sessions) in GridFS. - -This spec does not require all drivers to implement transaction -support in GridFS because transactions in GridFS are not very useful: -transactions in 4.0 are too limited in time and space to operate on -large GridFS files. Additionally, GridFS as specified already has some -basic guarantees that make transactions less necessary: files are -immutable and they are created "atomically", from the primary's -perspective, because the file entry is only saved after all chunks are -uploaded. - -Causal Consistency with RunCommand helper -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Causal Consistency alone only applies to commands that read, and we -don't want to parse the document passed to runCommand to see if it's a -command that reads. In a transaction, however, any command at all that -starts a transaction must include ``afterClusterTime``, so we can add -``afterClusterTime`` to the document passed to runCommand without adding -per-command special logic to runCommand. - -Calling commitTransaction with the generic runCommand helper is undefined behavior -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Applications should only use the ClientSession API to manage -transactions. Applications should not use a generic runCommand helper to -run the commitTransaction or abortTransaction commands directly. This -spec does not define the behavior of calling such commands, consistent -with other drivers specifications that do not define the behavior of -calling directly commands for which helper methods are available The -purpose of the generic runCommand method is to execute a command -directly with minimum additional client-side logic. - -**Dependencies** ----------------- - -This specification depends on: - -1. `Driver Sessions Specification`_ -2. `Retryable Writes Specification`_ - -.. _Driver Sessions Specification: ../sessions/driver-sessions.rst - -.. _Retryable Writes Specification: ../retryable-writes/retryable-writes.rst - -**Backwards Compatibility** ---------------------------- - -The API changes to support transactions extend the existing API but do -not introduce any backward breaking changes. Existing programs that do -not make use of transactions will continue to compile and run correctly. - -**Reference Implementation** ----------------------------- - -The `Python driver `_ serves -as a reference implementation. - -**Future work** ---------------- - -- Support retryable writes within a transaction. - -- Support transactions on secondaries. In this case, drivers would be - required to pin a transaction to the server selected for the - initial operation. All subsequent operations in the transaction - would go to the pinned server. - -- Support for transactions that read from multiple nodes in a replica - set. One interesting use case would be to run a single transaction - that performs low-latency reads with readPreference "nearest" - followed by some writes. - -- Support for unacknowledged transaction commits. This might be useful - when data consistency is paramount but durability is optional. - Imagine a system that increments two counters in two different - collections. The system may want to use transactions to guarantee - that both counters are always incremented together or not at all. - -**Justifications** ------------------- - -Why is there no Transaction object? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In order to use transactions an application already has to create and -manage a ClientSession object. Introducing a Transaction object would -result in another object that the application needs to manage. Moreover, -a server session can only have a single transaction in progress at a -time. We chose not to introduce a public Transaction object so that -applications only need to manage a single object and to more closely -mirror how transactions work on the server. - -Some drivers' startTransaction methods will return an object as part of -the language's resource management block protocol. The object returned -by startTransaction MUST NOT be named Transaction, in order to reserve -that name for some future API extension. Additionally, by avoiding the -name Transaction, we prevent users from thinking they can run multiple -transactions in a session. Finally, we avoid the temptation to diverge -from this spec's API by adding a commit() or abort() method to the -object returned by startTransaction. Committing and aborting a -transaction is the responsibility of the ClientSession object in all -drivers. - -Why is readPreference part of TransactionOptions? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Providing a read preference for the entire transaction makes it easier -for applications that use one or more non-primary read preferences for -non-transactional reads to run transactions under a single, primary -read-preference. Applications only need to set primary read preference -on the transaction instead of changing the read preference of all -operations. - -Because primary is the only read preference allowed with transactions in -MongoDB 4.0, this specification could have omitted -TransactionOptions.readPreference, or at least defaulted the read -preference to primary instead of inheriting the client's read -preference. However, this would have required a breaking change circa -MongoDB 4.2 when we introduce secondary reads in transactions: -TransactionOptions will inherit the client's read preference in 4.2, so -for the sake of future-compatibility, TransactionOptions inherits the -client's read preference now. - -We considered defaulting TransactionOptions.readPreference to primary in -4.0, overriding the client's read preference by default for convenience. -However, for consistency with other options-inheritance rules in our -specifications, transactions MUST inherit the client's read preference. - -In MongoDB 4.0, the error "read preference in a transaction must be -primary" is thrown whenever the application attempts a read operation in -a transaction with a non-primary read preference. We considered throwing -this error from startTransaction instead, to make the error more -deterministic and reduce the performance burden of re-checking the -TransactionOptions on each operation. However, this behavior will have -to change when we introduce secondary reads in transactions. There will -then be new error scenarios, such as a transaction with secondary reads -followed by a write. It won't be possible in the future for -startTransaction to check that the read preference is correct for all -operations the application will perform in the transaction. Therefore, -we specify now that the readPreference must be checked per-operation. -(However, we have not completely planned how read preference validation -will behave in MongoDB 4.2.) - -Users cannot pass readConcern or writeConcern to operations in transactions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For drivers that allow readConcern and/or writeConcern to be passed to a -particular operation, -If the driver did not prohibit the readConcern parameter to methods in a -transaction, the following code would be ambiguous: - -.. code:: python - - client = MongoClient("mongodb://localhost/?readConcernLevel=majority") - with client.start_session() as s: - # Transaction uses readConcern majority. - with s.start_transaction(): - # The first command in a transaction. Which readConcern? - client.db.collection.distinct( - readConcern={'level': 'snapshot'}, - session=s) - -In this scenario, the driver must choose which of the two possible readConcerns -to use for the *first* command in the transaction. The server will accept either -without error, so the ambiguity MUST be resolved by raising a client-side error. - -We *could* specify that if a user passes an explicit writeConcern to an -operation in a transaction, that the driver passes this writeConcern to the -server. The server correctly returns an error in this scenario; there is not the -same ambiguity with an explicit writeConcern as there is with an explicit -readConcern passed to the first operation in a transaction. For consistency, -however, we specify that an explicit writeConcern passed to an operation in a -transaction provokes a client-side error, the same as for readConcern. - -Another alternative is to silently ignore the readConcern and/or writeConcern -that the user has explicitly provided to a particular operation in a -transaction. This would be a surprising and undetectable deviation from the -user's explicit intent. - -On the other hand, if a user configures the write concern of a client, database, -or collection, and then configures the same option on a transaction, the -transaction's configuration overrides the inherited configuration: - -.. code:: python - - client = MongoClient("mongodb://localhost/?w=majority") - with client.start_session() as s: - with s.start_transaction(writeConcern={'w': 1}): - # Uses w: 1. - client.db.collection.insert_one( - {'_id': 1}, - session=s) - -In this case the transaction options express a more immediate user -intent than the client options, so it is not surprising to override the -client options. - -Aggregate with $out is a read operation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We intend to migrate away from designs that require drivers to inspect -the contents of the aggregation pipeline and override user read -preferences for aggregate with $out. In general, our specifications -should stop defining different behaviors based on the contents of -commands. - -A server selection error is labeled UnknownTransactionCommitResult -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Drivers add the "UnknownTransactionCommitResult" to a server selection -error from commitTransaction, even if this is the first attempt to send -commitTransaction. It is true in this case that the driver knows the -result: the transaction is definitely not committed. However, the -"UnknownTransactionCommitResult" label properly communicates to the -application that calling commitTransaction again may succeed. - -FAQ ---- - -What commands can be run in a transaction? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following commands are allowed inside transactions: - -1. find - -2. getMore - - - Note that it is not possible to start a transaction with a - getMore command, the cursor must have been created within the - transaction in order for the getMore to succeed. - -3. killCursors - -4. insert - -5. update - -6. delete - -7. findAndModify - -8. aggregate (including $lookup) - - - The $out stage is prohibited because it uses collection create - and rename operations. - -9. distinct - -10. geoSearch - -Why don’t drivers retry after write concern timeout errors? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A write concern timeout error indicates that the command succeeded but -failed to meet the specified writeConcern within the given time limit. -Attempting to retry would implicitly double the application’s wtimeout -value so drivers do not retry. - -What happens when a command object passed to RunCommand already contains a transaction field (eg. lsid, txnNumber, etc...)? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The behavior of running such commands in a transaction are undefined. -Applications should not run such commands inside a transaction. - -**Changelog** -------------- - -:2018-06-18: Explicit readConcern and/or writeConcern are prohibited within - transactions, with a client-side error. -:2018-06-07: The count command is not supported within transactions. -:2018-06-14: Any retryable writes error raised by commitTransaction must be - labelled "UnknownTransactionCommitResult". +.. note:: + This specification has been converted to Markdown and renamed to + `transactions.md `_. diff --git a/source/unified-test-format/schema-1.0.json b/source/unified-test-format/schema-1.0.json new file mode 100644 index 0000000000..54e3c3e5a9 --- /dev/null +++ b/source/unified-test-format/schema-1.0.json @@ -0,0 +1,275 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset"] + } + } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["commandStartedEvent", "commandSucceededEvent", "commandFailedEvent"] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedEvent" } + } + } + }, + + "expectedEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" } + } + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.1.json b/source/unified-test-format/schema-1.1.json new file mode 100644 index 0000000000..847db9085f --- /dev/null +++ b/source/unified-test-format/schema-1.1.json @@ -0,0 +1,295 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset"] + } + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["commandStartedEvent", "commandSucceededEvent", "commandFailedEvent"] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedEvent" } + } + } + }, + + "expectedEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" } + } + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.10.json b/source/unified-test-format/schema-1.10.json new file mode 100644 index 0000000000..5d85ea73e0 --- /dev/null +++ b/source/unified-test-format/schema-1.10.json @@ -0,0 +1,622 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.11.json b/source/unified-test-format/schema-1.11.json new file mode 100644 index 0000000000..824f7b77e8 --- /dev/null +++ b/source/unified-test-format/schema-1.11.json @@ -0,0 +1,623 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.12.json b/source/unified-test-format/schema-1.12.json new file mode 100644 index 0000000000..91acf4fb69 --- /dev/null +++ b/source/unified-test-format/schema-1.12.json @@ -0,0 +1,624 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { "type": "object" }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.13.json b/source/unified-test-format/schema-1.13.json new file mode 100644 index 0000000000..9bcce8253f --- /dev/null +++ b/source/unified-test-format/schema-1.13.json @@ -0,0 +1,677 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { "$ref": "#/definitions/logSeverityLevel" }, + "topology": { "$ref": "#/definitions/logSeverityLevel" }, + "serverSelection": { "$ref": "#/definitions/logSeverityLevel" }, + "connection": { "$ref": "#/definitions/logSeverityLevel" } + } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "logComponent": { + "type": "string", + "enum": ["command", "topology", "serverSelection", "connection"] + }, + + "logSeverityLevel": { + "type": "string", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug", "trace"] + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "messages"], + "properties": { + "client": { "type": "string" }, + "messages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + } + } + }, + + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": ["level", "component", "data"], + "properties": { + "level": { "$ref": "#/definitions/logSeverityLevel" }, + "component": { "$ref": "#/definitions/logComponent" }, + "data": { "type": "object" }, + "failureIsRedacted": { "type": "boolean" } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedLogMessagesForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } + } diff --git a/source/unified-test-format/schema-1.14.json b/source/unified-test-format/schema-1.14.json new file mode 100644 index 0000000000..27372fe4bf --- /dev/null +++ b/source/unified-test-format/schema-1.14.json @@ -0,0 +1,684 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { "$ref": "#/definitions/logSeverityLevel" }, + "topology": { "$ref": "#/definitions/logSeverityLevel" }, + "serverSelection": { "$ref": "#/definitions/logSeverityLevel" }, + "connection": { "$ref": "#/definitions/logSeverityLevel" } + } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "logComponent": { + "type": "string", + "enum": ["command", "topology", "serverSelection", "connection"] + }, + + "logSeverityLevel": { + "type": "string", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug", "trace"] + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "messages"], + "properties": { + "client": { "type": "string" }, + "messages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + } + } + }, + + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": ["level", "component", "data"], + "properties": { + "level": { "$ref": "#/definitions/logSeverityLevel" }, + "component": { "$ref": "#/definitions/logComponent" }, + "data": { "type": "object" }, + "failureIsRedacted": { "type": "boolean" } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedLogMessagesForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } + } diff --git a/source/unified-test-format/schema-1.15.json b/source/unified-test-format/schema-1.15.json new file mode 100644 index 0000000000..65c5a383ea --- /dev/null +++ b/source/unified-test-format/schema-1.15.json @@ -0,0 +1,686 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { "$ref": "#/definitions/logSeverityLevel" }, + "topology": { "$ref": "#/definitions/logSeverityLevel" }, + "serverSelection": { "$ref": "#/definitions/logSeverityLevel" }, + "connection": { "$ref": "#/definitions/logSeverityLevel" } + } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "logComponent": { + "type": "string", + "enum": ["command", "topology", "serverSelection", "connection"] + }, + + "logSeverityLevel": { + "type": "string", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug", "trace"] + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "messages"], + "properties": { + "client": { "type": "string" }, + "messages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + } + } + }, + + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": ["level", "component", "data"], + "properties": { + "level": { "$ref": "#/definitions/logSeverityLevel" }, + "component": { "$ref": "#/definitions/logComponent" }, + "data": { "type": "object" }, + "failureIsRedacted": { "type": "boolean" } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedLogMessagesForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } + } diff --git a/source/unified-test-format/schema-1.16.json b/source/unified-test-format/schema-1.16.json new file mode 100644 index 0000000000..477ca0e345 --- /dev/null +++ b/source/unified-test-format/schema-1.16.json @@ -0,0 +1,691 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { "$ref": "#/definitions/logSeverityLevel" }, + "topology": { "$ref": "#/definitions/logSeverityLevel" }, + "serverSelection": { "$ref": "#/definitions/logSeverityLevel" }, + "connection": { "$ref": "#/definitions/logSeverityLevel" } + } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "logComponent": { + "type": "string", + "enum": ["command", "topology", "serverSelection", "connection"] + }, + + "logSeverityLevel": { + "type": "string", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug", "trace"] + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "messages"], + "properties": { + "client": { "type": "string" }, + "messages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + }, + "ignoreExtraMessages": { "type": "boolean" }, + "ignoreMessages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + } + } + }, + + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": ["level", "component", "data"], + "properties": { + "level": { "$ref": "#/definitions/logSeverityLevel" }, + "component": { "$ref": "#/definitions/logComponent" }, + "data": { "type": "object" }, + "failureIsRedacted": { "type": "boolean" } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedLogMessagesForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } + } diff --git a/source/unified-test-format/schema-1.17.json b/source/unified-test-format/schema-1.17.json new file mode 100644 index 0000000000..7d321889a4 --- /dev/null +++ b/source/unified-test-format/schema-1.17.json @@ -0,0 +1,712 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { "$ref": "#/definitions/logSeverityLevel" }, + "topology": { "$ref": "#/definitions/logSeverityLevel" }, + "serverSelection": { "$ref": "#/definitions/logSeverityLevel" }, + "connection": { "$ref": "#/definitions/logSeverityLevel" } + } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" } + } + } + } + }, + + "logComponent": { + "type": "string", + "enum": ["command", "topology", "serverSelection", "connection"] + }, + + "logSeverityLevel": { + "type": "string", + "enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug", "trace"] + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap", "sdam"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "sdam" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedSdamEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" }, + "interruptInUseConnections": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { "$ref": "#/definitions/serverDescription" }, + "newDescription": { "$ref": "#/definitions/serverDescription" } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { "type": "boolean" } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { "type": "boolean" } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { "type": "boolean" } + } + } + } + }, + + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "messages"], + "properties": { + "client": { "type": "string" }, + "messages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + }, + "ignoreExtraMessages": { "type": "boolean" }, + "ignoreMessages": { + "type": "array", + "items": { "$ref": "#/definitions/expectedLogMessage" } + } + } + }, + + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": ["level", "component", "data"], + "properties": { + "level": { "$ref": "#/definitions/logSeverityLevel" }, + "component": { "$ref": "#/definitions/logComponent" }, + "data": { "type": "object" }, + "failureIsRedacted": { "type": "boolean" } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedLogMessagesForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } + } diff --git a/source/unified-test-format/schema-1.18.json b/source/unified-test-format/schema-1.18.json new file mode 100644 index 0000000000..c10df8e679 --- /dev/null +++ b/source/unified-test-format/schema-1.18.json @@ -0,0 +1,1070 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.19.json b/source/unified-test-format/schema-1.19.json new file mode 100644 index 0000000000..79609fe539 --- /dev/null +++ b/source/unified-test-format/schema-1.19.json @@ -0,0 +1,1073 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.2.json b/source/unified-test-format/schema-1.2.json new file mode 100644 index 0000000000..c3cf983ca8 --- /dev/null +++ b/source/unified-test-format/schema-1.2.json @@ -0,0 +1,332 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset"] + } + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["commandStartedEvent", "commandSucceededEvent", "commandFailedEvent"] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedEvent" } + } + } + }, + + "expectedEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" } + } + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.20.json b/source/unified-test-format/schema-1.20.json new file mode 100644 index 0000000000..de1d5b75b2 --- /dev/null +++ b/source/unified-test-format/schema-1.20.json @@ -0,0 +1,1109 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.21.json b/source/unified-test-format/schema-1.21.json new file mode 100644 index 0000000000..09c3a6882f --- /dev/null +++ b/source/unified-test-format/schema-1.21.json @@ -0,0 +1,1118 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.22.json b/source/unified-test-format/schema-1.22.json new file mode 100644 index 0000000000..64d6d48422 --- /dev/null +++ b/source/unified-test-format/schema-1.22.json @@ -0,0 +1,1121 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.23.json b/source/unified-test-format/schema-1.23.json new file mode 100644 index 0000000000..c40ddaf405 --- /dev/null +++ b/source/unified-test-format/schema-1.23.json @@ -0,0 +1,1161 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.24.json b/source/unified-test-format/schema-1.24.json new file mode 100644 index 0000000000..77d4aaed14 --- /dev/null +++ b/source/unified-test-format/schema-1.24.json @@ -0,0 +1,1163 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.25.json b/source/unified-test-format/schema-1.25.json new file mode 100644 index 0000000000..3be461221e --- /dev/null +++ b/source/unified-test-format/schema-1.25.json @@ -0,0 +1,1177 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.26.json b/source/unified-test-format/schema-1.26.json new file mode 100644 index 0000000000..c4e2b539ef --- /dev/null +++ b/source/unified-test-format/schema-1.26.json @@ -0,0 +1,1180 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "awaitMinPoolSizeMS": { + "type": "integer" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.27.json b/source/unified-test-format/schema-1.27.json new file mode 100644 index 0000000000..d195ba5af0 --- /dev/null +++ b/source/unified-test-format/schema-1.27.json @@ -0,0 +1,1242 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "observeTracingMessages": { + "type": "object", + "additionalProperties": false, + "properties": { + "enableCommandPayload": { + "type": "boolean" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "awaitMinPoolSizeMS": { + "type": "integer" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "spanComponent": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "attributes" + ], + "properties": { + "name": { + "type": "string" + }, + "attributes": { + "type": "object" + }, + "nested": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectTracingMessagesForClient": { + "additionalProperties": false, + "type": "object", + "required": [ + "client", + "spans" + ], + "properties": { + "client": { + "type": "string" + }, + "ignoreExtraSpans": { + "type": "boolean" + }, + "spans": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "expectTracingMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectTracingMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.28.json b/source/unified-test-format/schema-1.28.json new file mode 100644 index 0000000000..1801f4cff1 --- /dev/null +++ b/source/unified-test-format/schema-1.28.json @@ -0,0 +1,1274 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "observeTracingMessages": { + "type": "object", + "additionalProperties": false, + "properties": { + "enableCommandPayload": { + "type": "boolean" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "awaitMinPoolSizeMS": { + "type": "integer" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "spanComponent": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "attributes" + ], + "properties": { + "name": { + "type": "string" + }, + "attributes": { + "type": "object" + }, + "nested": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "accessToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + }, + "required": [ + "accessToken" + ] + } + ] + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "accessToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + }, + "required": [ + "accessToken" + ] + } + ] + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectTracingMessagesForClient": { + "additionalProperties": false, + "type": "object", + "required": [ + "client", + "spans" + ], + "properties": { + "client": { + "type": "string" + }, + "ignoreExtraSpans": { + "type": "boolean" + }, + "spans": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "expectTracingMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectTracingMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.3.json b/source/unified-test-format/schema-1.3.json new file mode 100644 index 0000000000..3de6c7f8d1 --- /dev/null +++ b/source/unified-test-format/schema-1.3.json @@ -0,0 +1,458 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.4.json b/source/unified-test-format/schema-1.4.json new file mode 100644 index 0000000000..f259b629d1 --- /dev/null +++ b/source/unified-test-format/schema-1.4.json @@ -0,0 +1,462 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.5.json b/source/unified-test-format/schema-1.5.json new file mode 100644 index 0000000000..3661f78a22 --- /dev/null +++ b/source/unified-test-format/schema-1.5.json @@ -0,0 +1,463 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.6.json b/source/unified-test-format/schema-1.6.json new file mode 100644 index 0000000000..bab0fd4a82 --- /dev/null +++ b/source/unified-test-format/schema-1.6.json @@ -0,0 +1,466 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.7.json b/source/unified-test-format/schema-1.7.json new file mode 100644 index 0000000000..654e376d63 --- /dev/null +++ b/source/unified-test-format/schema-1.7.json @@ -0,0 +1,468 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.8.json b/source/unified-test-format/schema-1.8.json new file mode 100644 index 0000000000..e69374878b --- /dev/null +++ b/source/unified-test-format/schema-1.8.json @@ -0,0 +1,555 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-1.9.json b/source/unified-test-format/schema-1.9.json new file mode 100644 index 0000000000..3850647fb8 --- /dev/null +++ b/source/unified-test-format/schema-1.9.json @@ -0,0 +1,563 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": ["description", "schemaVersion", "tests"], + "properties": { + "description": { "type": "string" }, + "schemaVersion": { "$ref": "#/definitions/version" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/entity" } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/test" } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { "$ref": "#/definitions/version" }, + "minServerVersion": { "$ref": "#/definitions/version" }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["single", "replicaset", "sharded", "sharded-replicaset", "load-balanced"] + } + }, + "serverless": { + "type": "string", + "enum": ["require", "forbid", "allow"] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { "type": "boolean" }, + "csfle": { "type": "boolean" } + } + }, + + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": ["id"], + "properties": { + "id": { "type": "string" }, + "uriOptions": { "type": "object" }, + "useMultipleMongoses": { "type": "boolean" }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "storeEventsAsEntities": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/storeEventsAsEntity" } + }, + "serverApi": { "$ref": "#/definitions/serverApi" }, + "observeSensitiveCommands": { "type": "boolean" } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": ["id", "clientEncryptionOpts"], + "properties": { + "id": { "type": "string" }, + "clientEncryptionOpts": { "$ref": "#/definitions/clientEncryptionOpts" } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client", "databaseName"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "databaseName": { "type": "string" }, + "databaseOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database", "collectionName"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "collectionName": { "type": "string" }, + "collectionOptions": { "$ref": "#/definitions/collectionOrDatabaseOptions" } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": ["id", "client"], + "properties": { + "id": { "type": "string" }, + "client": { "type": "string" }, + "sessionOptions": { "type": "object" } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": ["id", "database"], + "properties": { + "id": { "type": "string" }, + "database": { "type": "string" }, + "bucketOptions": { "type": "object" } + } + } + } + }, + + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": ["keyVaultClient", "keyVaultNamespace", "kmsProviders"], + "properties": { + "keyVaultClient": { "type": "string" }, + "keyVaultNamespace": { "type": "string" }, + "kmsProviders": { "$ref": "#/definitions/kmsProviders" } + } + }, + + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["$$placeholder"], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "aws": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "secretAccessKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "sessionToken": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "azure": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientId": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "clientSecret": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "identityPlatformEndpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "gcp": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "privateKey": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" }, + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "kmip": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + }, + "local": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" } + } + } + } + }, + + "storeEventsAsEntity": { + "type": "object", + "additionalProperties": false, + "required": ["id", "events"], + "properties": { + "id": { "type": "string" }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + } + } + }, + + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": ["collectionName", "databaseName", "documents"], + "properties": { + "collectionName": { "type": "string" }, + "databaseName": { "type": "string" }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { "type": "object" } + } + } + }, + + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": ["client", "events"], + "properties": { + "client": { "type": "string" }, + "eventType": { + "type": "string", + "enum": ["command", "cmap"] + }, + "events": { "type": "array" }, + "ignoreExtraEvents": { "type": "boolean" } + }, + "oneOf": [ + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "command" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + } + } + }, + { + "required": ["eventType"], + "properties": { + "eventType": { "const": "cmap" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCmapEvent" } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { "type": "string" }, + "events": { + "type": "array", + "items": { "$ref": "#/definitions/expectedCommandEvent" } + }, + "ignoreExtraEvents": { "type": "boolean" } + } + } + ] + }, + + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { "type": "object" }, + "commandName": { "type": "string" }, + "databaseName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { "type": "object" }, + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { "type": "string" }, + "hasServiceId": { "type": "boolean" }, + "hasServerConnectionId": { "type": "boolean" } + } + } + } + }, + + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { "type": "boolean" } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { "type": "string" } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { "type": "object" }, + "readPreference": { "type": "object" }, + "writeConcern": { "type": "object" }, + "timeoutMS": { "type": "integer" } + } + }, + + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": ["version"], + "properties": { + "version": { "type": "string" }, + "strict": { "type": "boolean" }, + "deprecationErrors": { "type": "boolean" } + } + }, + + "operation": { + "type": "object", + "additionalProperties": false, + "required": ["name", "object"], + "properties": { + "name": { "type": "string" }, + "object": { "type": "string" }, + "arguments": { "type": "object" }, + "ignoreResultAndError": { "type": "boolean" }, + "expectError": { "$ref": "#/definitions/expectedError" }, + "expectResult": {}, + "saveResultAsEntity": { "type": "string" } + }, + "allOf": [ + { "not": { "required": ["expectError", "expectResult"] } }, + { "not": { "required": ["expectError", "saveResultAsEntity"] } }, + { "not": { "required": ["ignoreResultAndError", "expectResult"] } }, + { "not": { "required": ["ignoreResultAndError", "expectError"] } }, + { "not": { "required": ["ignoreResultAndError", "saveResultAsEntity"] } } + ] + }, + + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { "type": "boolean" }, + "isTimeoutError": { "type": "boolean" }, + "errorContains": { "type": "string" }, + "errorCode": { "type": "integer" }, + "errorCodeName": { "type": "string" }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + "expectResult": {} + } + }, + + "test": { + "type": "object", + "additionalProperties": false, + "required": ["description", "operations"], + "properties": { + "description": { "type": "string" }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/runOnRequirement" } + }, + "skipReason": { "type": "string" }, + "operations": { + "type": "array", + "items": { "$ref": "#/definitions/operation" } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/expectedEventsForClient" } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/collectionData" } + } + } + } + } +} diff --git a/source/unified-test-format/schema-latest.json b/source/unified-test-format/schema-latest.json new file mode 100644 index 0000000000..1801f4cff1 --- /dev/null +++ b/source/unified-test-format/schema-latest.json @@ -0,0 +1,1274 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "observeTracingMessages": { + "type": "object", + "additionalProperties": false, + "properties": { + "enableCommandPayload": { + "type": "boolean" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "awaitMinPoolSizeMS": { + "type": "integer" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "spanComponent": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "attributes" + ], + "properties": { + "name": { + "type": "string" + }, + "attributes": { + "type": "object" + }, + "nested": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "accessToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + }, + "required": [ + "accessToken" + ] + } + ] + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "accessToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + }, + "required": [ + "accessToken" + ] + } + ] + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectTracingMessagesForClient": { + "additionalProperties": false, + "type": "object", + "required": [ + "client", + "spans" + ], + "properties": { + "client": { + "type": "string" + }, + "ignoreExtraSpans": { + "type": "boolean" + }, + "spans": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "expectTracingMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectTracingMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/tests/Makefile b/source/unified-test-format/tests/Makefile new file mode 100644 index 0000000000..dda5a8ec3e --- /dev/null +++ b/source/unified-test-format/tests/Makefile @@ -0,0 +1,155 @@ +SCHEMA=../schema-latest.json + +.PHONY: all \ + auth \ + causal-consistency \ + change-streams \ + client-side-encryption \ + client-side-operations-timeout \ + collection-management \ + command-logging-and-monitoring \ + command-logging-and-monitoring/logging \ + command-logging-and-monitoring/monitoring \ + connection-monitoring-and-pooling \ + connection-monitoring-and-pooling/logging \ + crud \ + gridfs \ + index-management \ + load-balancers \ + read-write-concern \ + retryable-reads \ + retryable-writes \ + run-command \ + server-discovery-and-monitoring \ + server-selection \ + server-selection/logging \ + sessions \ + transactions-convenient-api \ + transactions \ + unified-test-format \ + unified-test-format/invalid \ + unified-test-format/valid-fail \ + unified-test-format/valid-pass \ + versioned-api \ + HAS_AJV + +all: auth \ + causal-consistency \ + change-streams \ + client-side-encryption \ + client-side-operations-timeout \ + collection-management \ + command-logging-and-monitoring \ + connection-monitoring-and-pooling \ + crud \ + gridfs \ + index-management \ + load-balancers \ + read-write-concern \ + retryable-reads \ + retryable-writes \ + run-command \ + server-discovery-and-monitoring \ + server-selection \ + sessions \ + transactions-convenient-api \ + transactions \ + unified-test-format \ + versioned-api + +# Keep specifications sorted alphabetically +# When adding a new specification, remember to add it to the all and .PHONY targets above +# For specifications that contain multiple test folders, create a target for each folder +# in addition to a target for the specification itself +auth: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../auth/tests/unified/*.yml" --valid + +causal-consistency: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../causal-consistency/tests/*.yml" --valid + +change-streams: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../change-streams/tests/unified/*.yml" --valid + +client-side-encryption: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../client-side-encryption/tests/unified/*.yml" --valid + +client-side-operations-timeout: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../client-side-operations-timeout/tests/*.yml" --valid + +collection-management: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../collection-management/tests/*.yml" --valid + +command-logging-and-monitoring: command-logging-and-monitoring/logging command-logging-and-monitoring/monitoring + +command-logging-and-monitoring/logging: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../command-logging-and-monitoring/tests/logging/*.yml" --valid + +command-logging-and-monitoring/monitoring: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../command-logging-and-monitoring/tests/monitoring/*.yml" --valid + +connection-monitoring-and-pooling: connection-monitoring-and-pooling/logging + +connection-monitoring-and-pooling/logging: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../connection-monitoring-and-pooling/tests/logging/*.yml" --valid + +crud: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../crud/tests/unified/*.yml" --valid + +gridfs: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../gridfs/tests/*.yml" --valid + +index-management: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../index-management/tests/*.yml" --valid + +load-balancers: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../load-balancers/tests/*.yml" --valid + +read-write-concern: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../read-write-concern/tests/operation/*.yml" --valid + +retryable-reads: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../retryable-reads/tests/unified/*.yml" --valid + +retryable-writes: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../retryable-writes/tests/unified/*.yml" --valid + +run-command: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../run-command/tests/unified/*.yml" --valid + +server-discovery-and-monitoring: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../server-discovery-and-monitoring/tests/unified/*.yml" --valid + +server-selection: server-selection/logging + +server-selection/logging: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../server-selection/tests/logging/*.yml" --valid + +sessions: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../sessions/tests/*.yml" --valid + +transactions-convenient-api: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../transactions-convenient-api/tests/unified/*.yml" --valid + +transactions: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../transactions/tests/unified/*.yml" --valid + +unified-test-format: unified-test-format/invalid unified-test-format/valid-fail unified-test-format/valid-pass + +unified-test-format/invalid: HAS_AJV + @# Redirect stdout to hide expected validation errors + @ajv --spec=draft2019 test -s $(SCHEMA) -d "invalid/*.yml" --invalid > /dev/null && echo "invalid/*.yml passed test" + +unified-test-format/valid-fail: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "valid-fail/*.yml" --valid + +unified-test-format/valid-pass: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "valid-pass/*.yml" --valid + +versioned-api: HAS_AJV + @ajv --spec=draft2019 test -s $(SCHEMA) -d "../../versioned-api/tests/*.yml" --valid + +HAS_AJV: + @if ! command -v ajv > /dev/null; then \ + echo 'Error: need "npm install -g ajv-cli"' 1>&2; \ + exit 1; \ + fi diff --git a/source/unified-test-format/tests/README.md b/source/unified-test-format/tests/README.md new file mode 100644 index 0000000000..53e8180e4d --- /dev/null +++ b/source/unified-test-format/tests/README.md @@ -0,0 +1,32 @@ +# Unified Test Format Tests + +______________________________________________________________________ + +## Introduction + +This directory contains tests for the Unified Test Format's schema and test runner implementation(s). Tests are +organized in the following directories: + +- `invalid`: These files do not validate against the schema and are used to test the schema itself. +- `valid-pass`: These files validate against the schema and should pass when executed with a test runner. +- `valid-fail`: These files validate against the schema but should produce runtime errors or failures when executed with + a test runner. Some do so by violating the "SHOULD" and "SHOULD NOT" guidance in the spec (e.g. referencing an + undefined entity). + +## Validating Test Files + +JSON and YAML test files can be validated using [Ajv](https://ajv.js.org/) and a schema from the parent directory (e.g. +`schema-1.0.json`). + +Test files can be validated individually like so: + +```bash +ajv -s ../schema-1.0.json -d path/to/test.yml +``` + +Ajv can also be used to assert the validity of test files: + +```bash +ajv test -s ../schema-1.0.json -d "invalid/*.yml" --invalid +ajv test -s ../schema-1.0.json -d "valid-*/*.yml" --valid +``` diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.json new file mode 100644 index 0000000000..26d14051a7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.json @@ -0,0 +1,30 @@ +{ + "description": "clientEncryptionOpts-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + }, + "invalid": {} + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.yml new file mode 100644 index 0000000000..e4a06698eb --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-additionalProperties.yml @@ -0,0 +1,19 @@ +description: clientEncryptionOpts-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + invalid: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.json new file mode 100644 index 0000000000..c43a2a9125 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.json @@ -0,0 +1,23 @@ +{ + "description": "clientEncryptionOpts-keyVaultClient-required", + "schemaVersion": "1.8", + "createEntities": [ + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.yml new file mode 100644 index 0000000000..38b8b0b739 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-required.yml @@ -0,0 +1,15 @@ +description: clientEncryptionOpts-keyVaultClient-required + +schemaVersion: "1.8" + +createEntities: + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.json new file mode 100644 index 0000000000..1be9167a40 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-keyVaultClient-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": 0, + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.yml new file mode 100644 index 0000000000..244fef6a95 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultClient-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-keyVaultClient-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: 0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.json new file mode 100644 index 0000000000..3f54d89aa7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.json @@ -0,0 +1,28 @@ +{ + "description": "clientEncryptionOpts-keyVaultNamespace-required", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.yml new file mode 100644 index 0000000000..9fc217a800 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-required.yml @@ -0,0 +1,17 @@ +description: clientEncryptionOpts-keyVaultNamespace-required + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.json new file mode 100644 index 0000000000..53f2f5f086 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-keyVaultNamespace-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": 0, + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.yml new file mode 100644 index 0000000000..a8575840d0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-keyVaultNamespace-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-keyVaultNamespace-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: 0 + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.json new file mode 100644 index 0000000000..cfd979e2b2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "invalid": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.yml new file mode 100644 index 0000000000..619a7e1ddf --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + invalid: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.json new file mode 100644 index 0000000000..59b273487d --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-aws-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": { + "invalid": {} + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.yml new file mode 100644 index 0000000000..e2e0b45604 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-aws-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { invalid: {} } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.json new file mode 100644 index 0000000000..ffcc85bfcf --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-aws-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": 0 + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.yml new file mode 100644 index 0000000000..93992b8647 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-aws-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-aws-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.json new file mode 100644 index 0000000000..8fe5c150a1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-azure-accessToken-type", + "schemaVersion": "1.28", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "accessToken": 0 + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.yml new file mode 100644 index 0000000000..750396e2c5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-accessToken-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-azure-accessToken-type + +schemaVersion: "1.28" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { accessToken: 0 } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.json new file mode 100644 index 0000000000..1664b79097 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-azure-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": { + "invalid": {} + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.yml new file mode 100644 index 0000000000..b29d032969 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-azure-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { invalid: {} } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.json new file mode 100644 index 0000000000..5bd50c8078 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-azure-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "azure": 0 + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.yml new file mode 100644 index 0000000000..cab0661eb9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-azure-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-azure-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.json new file mode 100644 index 0000000000..2284e26cb6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-gcp-accessToken-type", + "schemaVersion": "1.28", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "accessToken": 0 + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.yml new file mode 100644 index 0000000000..601741a68c --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-accessToken-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-gcp-accessToken-type + +schemaVersion: "1.28" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { accessToken: 0 } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.json new file mode 100644 index 0000000000..120c088b00 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-gcp-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": { + "invalid": {} + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.yml new file mode 100644 index 0000000000..f95db9fef7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-gcp-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { invalid: {} } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.json new file mode 100644 index 0000000000..1dd1c8a2a3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-gcp-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "gcp": 0 + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.yml new file mode 100644 index 0000000000..ef707d4df2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-gcp-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-gcp-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.json new file mode 100644 index 0000000000..9c659c8f76 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-invalidName", + "schemaVersion": "1.18", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name_with_invalid_character*": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.yml new file mode 100644 index 0000000000..a8bda7ff8c --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-invalidName.yml @@ -0,0 +1,19 @@ +description: clientEncryptionOpts-kmsProviders-invalidName + +schemaVersion: "1.18" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + # The `*` is an invalid character. + "aws:name_with_invalid_character*": {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.json new file mode 100644 index 0000000000..22ded20440 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-kmip-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "kmip": { + "invalid": {} + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.yml new file mode 100644 index 0000000000..fb4a79aba8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-kmip-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + kmip: { invalid: {} } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.json new file mode 100644 index 0000000000..9b9e74be37 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-kmip-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "kmip": 0 + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.yml new file mode 100644 index 0000000000..f89e08ce9b --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-kmip-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-kmip-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + kmip: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.json new file mode 100644 index 0000000000..b93cfe00d1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-local-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "invalid": {} + } + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.yml new file mode 100644 index 0000000000..9ee3a0eb02 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-additionalProperties.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-local-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: { invalid: {} } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.json new file mode 100644 index 0000000000..526ea24831 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.json @@ -0,0 +1,29 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-local-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": 0 + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.yml new file mode 100644 index 0000000000..a1b51dd632 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-local-type.yml @@ -0,0 +1,18 @@ +description: clientEncryptionOpts-kmsProviders-local-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.json new file mode 100644 index 0000000000..b823a67baf --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.json @@ -0,0 +1,26 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-required", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys" + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.yml new file mode 100644 index 0000000000..4575483a50 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-required.yml @@ -0,0 +1,16 @@ +description: clientEncryptionOpts-kmsProviders-required + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.json new file mode 100644 index 0000000000..e7a6190b68 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.json @@ -0,0 +1,27 @@ +{ + "description": "clientEncryptionOpts-kmsProviders-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": 0 + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.yml new file mode 100644 index 0000000000..17a51d88c1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-kmsProviders-type.yml @@ -0,0 +1,17 @@ +description: clientEncryptionOpts-kmsProviders-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.json b/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.json new file mode 100644 index 0000000000..3b4972f23d --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.json @@ -0,0 +1,30 @@ +{ + "description": "clientEncryptionOpts-tlsOptions_not_supported", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + }, + "tlsOptions": {} + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.yml b/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.yml new file mode 100644 index 0000000000..cd04141f24 --- /dev/null +++ b/source/unified-test-format/tests/invalid/clientEncryptionOpts-tlsOptions_not_supported.yml @@ -0,0 +1,21 @@ +# Note: tests the same rule as clientEncryptionOpts-additionalProperties, but +# exists as an independent test because tlsOptions is defined in the CSLE spec. +description: clientEncryptionOpts-tlsOptions_not_supported + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + tlsOptions: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-additionalProperties.json b/source/unified-test-format/tests/invalid/collectionData-additionalProperties.json new file mode 100644 index 0000000000..1f4ed4c154 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-additionalProperties.json @@ -0,0 +1,39 @@ +{ + "description": "collectionData-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": "foo", + "documents": [], + "foo": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-additionalProperties.yml b/source/unified-test-format/tests/invalid/collectionData-additionalProperties.yml new file mode 100644 index 0000000000..62a4a81b6a --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-additionalProperties.yml @@ -0,0 +1,25 @@ +description: "collectionData-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-collectionName-required.json b/source/unified-test-format/tests/invalid/collectionData-collectionName-required.json new file mode 100644 index 0000000000..5426418c88 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-collectionName-required.json @@ -0,0 +1,37 @@ +{ + "description": "collectionData-collectionName-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "databaseName": "foo", + "documents": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-collectionName-required.yml b/source/unified-test-format/tests/invalid/collectionData-collectionName-required.yml new file mode 100644 index 0000000000..f8da422eab --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-collectionName-required.yml @@ -0,0 +1,23 @@ +description: "collectionData-collectionName-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - databaseName: *database0Name + documents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-collectionName-type.json b/source/unified-test-format/tests/invalid/collectionData-collectionName-type.json new file mode 100644 index 0000000000..2a922de13e --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-collectionName-type.json @@ -0,0 +1,38 @@ +{ + "description": "collectionData-collectionName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": 0, + "databaseName": "foo", + "documents": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-collectionName-type.yml b/source/unified-test-format/tests/invalid/collectionData-collectionName-type.yml new file mode 100644 index 0000000000..d14ecb5fab --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-collectionName-type.yml @@ -0,0 +1,24 @@ +description: "collectionData-collectionName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: 0 + databaseName: *database0Name + documents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-createOptions-type.json b/source/unified-test-format/tests/invalid/collectionData-createOptions-type.json new file mode 100644 index 0000000000..5b78bbcbb6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-createOptions-type.json @@ -0,0 +1,39 @@ +{ + "description": "collectionData-createOptions-type", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": "foo", + "createOptions": 0, + "documents": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-createOptions-type.yml b/source/unified-test-format/tests/invalid/collectionData-createOptions-type.yml new file mode 100644 index 0000000000..5bc17a15f9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-createOptions-type.yml @@ -0,0 +1,25 @@ +description: "collectionData-createOptions-type" + +schemaVersion: "1.9" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + createOptions: 0 + documents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-databaseName-required.json b/source/unified-test-format/tests/invalid/collectionData-databaseName-required.json new file mode 100644 index 0000000000..8417801390 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-databaseName-required.json @@ -0,0 +1,37 @@ +{ + "description": "collectionData-databaseName-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "documents": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-databaseName-required.yml b/source/unified-test-format/tests/invalid/collectionData-databaseName-required.yml new file mode 100644 index 0000000000..595ae798e8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-databaseName-required.yml @@ -0,0 +1,23 @@ +description: "collectionData-databaseName-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + documents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-databaseName-type.json b/source/unified-test-format/tests/invalid/collectionData-databaseName-type.json new file mode 100644 index 0000000000..d3480e8034 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-databaseName-type.json @@ -0,0 +1,38 @@ +{ + "description": "collectionData-databaseName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": 0, + "documents": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-databaseName-type.yml b/source/unified-test-format/tests/invalid/collectionData-databaseName-type.yml new file mode 100644 index 0000000000..bc5b47fe1b --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-databaseName-type.yml @@ -0,0 +1,24 @@ +description: "collectionData-databaseName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: 0 + documents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-items.json b/source/unified-test-format/tests/invalid/collectionData-documents-items.json new file mode 100644 index 0000000000..beb5af61c4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-items.json @@ -0,0 +1,40 @@ +{ + "description": "collectionData-documents-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": "foo", + "documents": [ + 0 + ] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-items.yml b/source/unified-test-format/tests/invalid/collectionData-documents-items.yml new file mode 100644 index 0000000000..f129ee371a --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-items.yml @@ -0,0 +1,24 @@ +description: "collectionData-documents-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-required.json b/source/unified-test-format/tests/invalid/collectionData-documents-required.json new file mode 100644 index 0000000000..4aadf9b159 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-required.json @@ -0,0 +1,37 @@ +{ + "description": "collectionData-documents-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": "foo" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-required.yml b/source/unified-test-format/tests/invalid/collectionData-documents-required.yml new file mode 100644 index 0000000000..b275e7610c --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-required.yml @@ -0,0 +1,23 @@ +description: "collectionData-documents-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-type.json b/source/unified-test-format/tests/invalid/collectionData-documents-type.json new file mode 100644 index 0000000000..9cbd3c164c --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-type.json @@ -0,0 +1,38 @@ +{ + "description": "collectionData-documents-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo" + } + } + ], + "initialData": [ + { + "collectionName": "foo", + "databaseName": "foo", + "documents": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionData-documents-type.yml b/source/unified-test-format/tests/invalid/collectionData-documents-type.yml new file mode 100644 index 0000000000..a51a506508 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionData-documents-type.yml @@ -0,0 +1,24 @@ +description: "collectionData-documents-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: &database0Name "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: &collection0Name "foo" + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.json b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.json new file mode 100644 index 0000000000..beef260eed --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.json @@ -0,0 +1,27 @@ +{ + "description": "collectionOrDatabaseOptions-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": { + "foo": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.yml b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.yml new file mode 100644 index 0000000000..e5b92562a2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-additionalProperties.yml @@ -0,0 +1,17 @@ +description: "collectionOrDatabaseOptions-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.json b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.json new file mode 100644 index 0000000000..1b9f4bcbea --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.json @@ -0,0 +1,27 @@ +{ + "description": "collectionOrDatabaseOptions-readConcern-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": { + "readConcern": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.yml b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.yml new file mode 100644 index 0000000000..671a5b0241 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readConcern-type.yml @@ -0,0 +1,17 @@ +description: "collectionOrDatabaseOptions-readConcern-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: + readConcern: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.json b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.json new file mode 100644 index 0000000000..988b594d13 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.json @@ -0,0 +1,27 @@ +{ + "description": "collectionOrDatabaseOptions-readPreference-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": { + "readPreference": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.yml b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.yml new file mode 100644 index 0000000000..84b328fc46 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-readPreference-type.yml @@ -0,0 +1,17 @@ +description: "collectionOrDatabaseOptions-readPreference-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: + readPreference: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.json b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.json new file mode 100644 index 0000000000..088e9d1eb2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.json @@ -0,0 +1,27 @@ +{ + "description": "collectionOrDatabaseOptions-timeoutMS-type", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": { + "timeoutMS": 4.5 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.yml b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.yml new file mode 100644 index 0000000000..6a24053583 --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-timeoutMS-type.yml @@ -0,0 +1,17 @@ +description: "collectionOrDatabaseOptions-timeoutMS-type" + +schemaVersion: "1.9" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: + timeoutMS: 4.5 # Must be an integer, not a float. + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.json b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.json new file mode 100644 index 0000000000..bd2157c5cb --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.json @@ -0,0 +1,27 @@ +{ + "description": "collectionOrDatabaseOptions-writeConcern-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": { + "writeConcern": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.yml b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.yml new file mode 100644 index 0000000000..5dc3f8b63c --- /dev/null +++ b/source/unified-test-format/tests/invalid/collectionOrDatabaseOptions-writeConcern-type.yml @@ -0,0 +1,17 @@ +description: "collectionOrDatabaseOptions-writeConcern-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: + writeConcern: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/createEntities-items.json b/source/unified-test-format/tests/invalid/createEntities-items.json new file mode 100644 index 0000000000..8e9d6ff702 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-items.json @@ -0,0 +1,13 @@ +{ + "description": "createEntities-items", + "schemaVersion": "1.0", + "createEntities": [ + 0 + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/createEntities-items.yml b/source/unified-test-format/tests/invalid/createEntities-items.yml new file mode 100644 index 0000000000..f0d00e33b2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-items.yml @@ -0,0 +1,9 @@ +description: "createEntities-items" + +schemaVersion: "1.0" + +createEntities: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/createEntities-minItems.json b/source/unified-test-format/tests/invalid/createEntities-minItems.json new file mode 100644 index 0000000000..3654923d28 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "createEntities-minItems", + "schemaVersion": "1.0", + "createEntities": [], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/createEntities-minItems.yml b/source/unified-test-format/tests/invalid/createEntities-minItems.yml new file mode 100644 index 0000000000..11641ea7d8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-minItems.yml @@ -0,0 +1,9 @@ +description: "createEntities-minItems" + +schemaVersion: "1.0" + +createEntities: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/createEntities-type.json b/source/unified-test-format/tests/invalid/createEntities-type.json new file mode 100644 index 0000000000..ce3c382c93 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-type.json @@ -0,0 +1,11 @@ +{ + "description": "createEntities-type", + "schemaVersion": "1.0", + "createEntities": 0, + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/createEntities-type.yml b/source/unified-test-format/tests/invalid/createEntities-type.yml new file mode 100644 index 0000000000..c181f31922 --- /dev/null +++ b/source/unified-test-format/tests/invalid/createEntities-type.yml @@ -0,0 +1,9 @@ +description: "createEntities-type" + +schemaVersion: "1.0" + +createEntities: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/description-required.json b/source/unified-test-format/tests/invalid/description-required.json new file mode 100644 index 0000000000..e4e0d0efdf --- /dev/null +++ b/source/unified-test-format/tests/invalid/description-required.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/description-required.yml b/source/unified-test-format/tests/invalid/description-required.yml new file mode 100644 index 0000000000..5bc6ecff5e --- /dev/null +++ b/source/unified-test-format/tests/invalid/description-required.yml @@ -0,0 +1,7 @@ +# description: "description-required" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-additionalProperties.json new file mode 100644 index 0000000000..38b8898787 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-additionalProperties.json @@ -0,0 +1,15 @@ +{ + "description": "entity-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "foo": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-additionalProperties.yml new file mode 100644 index 0000000000..4978518c5e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-additionalProperties.yml @@ -0,0 +1,10 @@ +description: "entity-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.json new file mode 100644 index 0000000000..46f9b4038e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.json @@ -0,0 +1,31 @@ +{ + "description": "entity-bucket-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.yml new file mode 100644 index 0000000000..e864aab955 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-additionalProperties.yml @@ -0,0 +1,19 @@ +description: "entity-bucket-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + id: &bucket0 "bucket0" + database: *database0 + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.json b/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.json new file mode 100644 index 0000000000..c3d7423e65 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.json @@ -0,0 +1,31 @@ +{ + "description": "entity-bucket-bucketOptions-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0", + "bucketOptions": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.yml b/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.yml new file mode 100644 index 0000000000..2d80f5ca3f --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-bucketOptions-type.yml @@ -0,0 +1,19 @@ +description: "entity-bucket-bucketOptions-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + id: &bucket0 "bucket0" + database: *database0 + bucketOptions: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-database-required.json b/source/unified-test-format/tests/invalid/entity-bucket-database-required.json new file mode 100644 index 0000000000..1fde5a96c9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-database-required.json @@ -0,0 +1,29 @@ +{ + "description": "entity-bucket-database-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "id": "bucket0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-database-required.yml b/source/unified-test-format/tests/invalid/entity-bucket-database-required.yml new file mode 100644 index 0000000000..df7f8d9457 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-database-required.yml @@ -0,0 +1,17 @@ +description: "entity-bucket-database-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + id: &bucket0 "bucket0" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-database-type.json b/source/unified-test-format/tests/invalid/entity-bucket-database-type.json new file mode 100644 index 0000000000..798d273fb0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-database-type.json @@ -0,0 +1,30 @@ +{ + "description": "entity-bucket-database-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "id": "bucket0", + "database": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-database-type.yml b/source/unified-test-format/tests/invalid/entity-bucket-database-type.yml new file mode 100644 index 0000000000..e735d16e16 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-database-type.yml @@ -0,0 +1,18 @@ +description: "entity-bucket-database-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + id: &bucket0 "bucket0" + database: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-id-required.json b/source/unified-test-format/tests/invalid/entity-bucket-id-required.json new file mode 100644 index 0000000000..c547d8ea3c --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-id-required.json @@ -0,0 +1,29 @@ +{ + "description": "entity-bucket-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "database": "database0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-id-required.yml b/source/unified-test-format/tests/invalid/entity-bucket-id-required.yml new file mode 100644 index 0000000000..b47435870f --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-id-required.yml @@ -0,0 +1,17 @@ +description: "entity-bucket-id-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + database: *database0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-bucket-id-type.json b/source/unified-test-format/tests/invalid/entity-bucket-id-type.json new file mode 100644 index 0000000000..f4e10ee630 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-id-type.json @@ -0,0 +1,30 @@ +{ + "description": "entity-bucket-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "bucket": { + "id": 0, + "database": "database0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-bucket-id-type.yml b/source/unified-test-format/tests/invalid/entity-bucket-id-type.yml new file mode 100644 index 0000000000..33522fea32 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-bucket-id-type.yml @@ -0,0 +1,18 @@ +description: "entity-bucket-id-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - bucket: + id: 0 + database: *database0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-client-additionalProperties.json new file mode 100644 index 0000000000..467e1d6ae1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-additionalProperties.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-client-additionalProperties.yml new file mode 100644 index 0000000000..b93e51d08a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-additionalProperties.yml @@ -0,0 +1,12 @@ +description: "entity-client-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-id-required.json b/source/unified-test-format/tests/invalid/entity-client-id-required.json new file mode 100644 index 0000000000..4be2fbf8e8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-id-required.json @@ -0,0 +1,15 @@ +{ + "description": "entity-client-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": {} + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-id-required.yml b/source/unified-test-format/tests/invalid/entity-client-id-required.yml new file mode 100644 index 0000000000..794a025f23 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-id-required.yml @@ -0,0 +1,10 @@ +description: "entity-client-id-required" + +schemaVersion: "1.0" + +createEntities: + - client: {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-id-type.json b/source/unified-test-format/tests/invalid/entity-client-id-type.json new file mode 100644 index 0000000000..cdc7cbc0e7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-id-type.json @@ -0,0 +1,17 @@ +{ + "description": "entity-client-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-id-type.yml b/source/unified-test-format/tests/invalid/entity-client-id-type.yml new file mode 100644 index 0000000000..fc872b2b22 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-id-type.yml @@ -0,0 +1,11 @@ +description: "entity-client-id-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.json b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.json new file mode 100644 index 0000000000..1252ac82d7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-ignoreCommandMonitoringEvents-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "ignoreCommandMonitoringEvents": [ + 0 + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.yml b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.yml new file mode 100644 index 0000000000..8f63e4d7e6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-items.yml @@ -0,0 +1,12 @@ +description: "entity-client-ignoreCommandMonitoringEvents-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + ignoreCommandMonitoringEvents: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.json b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.json new file mode 100644 index 0000000000..e78068a442 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-ignoreCommandMonitoringEvents-minItems", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "ignoreCommandMonitoringEvents": [] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.yml b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.yml new file mode 100644 index 0000000000..128282c6f3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-minItems.yml @@ -0,0 +1,12 @@ +description: "entity-client-ignoreCommandMonitoringEvents-minItems" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + ignoreCommandMonitoringEvents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.json b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.json new file mode 100644 index 0000000000..5ac2b340c5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-ignoreCommandMonitoringEvents-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "ignoreCommandMonitoringEvents": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.yml b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.yml new file mode 100644 index 0000000000..9fa8bfa323 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-ignoreCommandMonitoringEvents-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-ignoreCommandMonitoringEvents-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + ignoreCommandMonitoringEvents: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.json b/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.json new file mode 100644 index 0000000000..c39c94eee2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeEvents-enum", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "foo" + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.yml b/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.yml new file mode 100644 index 0000000000..5deaeb6212 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-enum.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeEvents-enum" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + observeEvents: ["foo"] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.json b/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.json new file mode 100644 index 0000000000..3aee11e3d5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeEvents-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + 0 + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.yml b/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.yml new file mode 100644 index 0000000000..ad73f22699 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-items.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeEvents-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + observeEvents: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.json b/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.json new file mode 100644 index 0000000000..e70d90c0a7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeEvents-minItems", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.yml b/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.yml new file mode 100644 index 0000000000..bd16e78c94 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-minItems.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeEvents-minItems" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + observeEvents: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.json b/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.json new file mode 100644 index 0000000000..c144e32369 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeEvents-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.yml b/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.yml new file mode 100644 index 0000000000..b75e97faea --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeEvents-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeEvents-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + observeEvents: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.json b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.json new file mode 100644 index 0000000000..87cbd21125 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeLogMessages-minProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": {} + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.yml b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.yml new file mode 100644 index 0000000000..b65f9731a8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-minProperties.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeLogMessages-minProperties" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + observeLogMessages: {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.json b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.json new file mode 100644 index 0000000000..fed0accd6e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeLogMessages-property-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": { + "command": {} + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.yml b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.yml new file mode 100644 index 0000000000..4e48c097a4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-type.yml @@ -0,0 +1,13 @@ +description: "entity-client-observeLogMessages-property-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + observeLogMessages: + command: {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.json b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.json new file mode 100644 index 0000000000..f14b18d6de --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeLogMessages-property-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": { + "command": "notALogLevel" + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.yml b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.yml new file mode 100644 index 0000000000..dee7de6840 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-property-value.yml @@ -0,0 +1,13 @@ +description: "entity-client-observeLogMessages-property-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + observeLogMessages: + command: notALogLevel + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.json b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.json new file mode 100644 index 0000000000..8a277034e2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeLogMessages-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.yml b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.yml new file mode 100644 index 0000000000..260bd45178 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeLogMessages-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeLogMessages-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + observeLogMessages: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.json b/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.json new file mode 100644 index 0000000000..c5572f1fbe --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeSensitiveCommands-type", + "schemaVersion": "1.5", + "createEntities": [ + { + "client": { + "id": "client0", + "observeSensitiveCommands": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.yml b/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.yml new file mode 100644 index 0000000000..b4ac28279e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeSensitiveCommands-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeSensitiveCommands-type" + +schemaVersion: "1.5" + +createEntities: + - client: + id: &client0 "client0" + observeSensitiveCommands: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.json new file mode 100644 index 0000000000..aa8046d28e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeTracingMessages-additionalProperties", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0", + "observeTracingMessages": { + "foo": "bar" + } + } + } + ], + "tests": [ + { + "description": "observeTracingMessages must not have additional properties'", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.yml new file mode 100644 index 0000000000..3ec4cd1691 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalProperties.yml @@ -0,0 +1,13 @@ +description: "entity-client-observeTracingMessages-additionalProperties" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + observeTracingMessages: + foo: "bar" + +tests: + - description: "observeTracingMessages must not have additional properties'" + operations: [ ] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.json b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.json new file mode 100644 index 0000000000..0b3a65f587 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeTracingMessages-additionalPropertyType", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0", + "observeTracingMessages": { + "enableCommandPayload": 0 + } + } + } + ], + "tests": [ + { + "description": "observeTracingMessages enableCommandPayload must be boolean", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.yml b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.yml new file mode 100644 index 0000000000..8327458aee --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-additionalPropertyType.yml @@ -0,0 +1,13 @@ +description: "entity-client-observeTracingMessages-additionalPropertyType" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + observeTracingMessages: + enableCommandPayload: 0 + +tests: + - description: "observeTracingMessages enableCommandPayload must be boolean" + operations: [ ] diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.json b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.json new file mode 100644 index 0000000000..de3ef39ab1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeTracingMessages-type", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0", + "observeTracingMessages": "foo" + } + } + ], + "tests": [ + { + "description": "observeTracingMessages must be an object", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.yml b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.yml new file mode 100644 index 0000000000..e4dbeae64e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-observeTracingMessages-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-observeTracingMessages-type" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + observeTracingMessages: "foo" + +tests: + - description: "observeTracingMessages must be an object" + operations: [ ] diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.json b/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.json new file mode 100644 index 0000000000..b688dae631 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.json @@ -0,0 +1,21 @@ +{ + "description": "entity-client-serverApi-version-type", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": { + "version": "1", + "deprecationErrors": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.yml b/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.yml new file mode 100644 index 0000000000..9e17cca0b7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-deprecationErrors-type.yml @@ -0,0 +1,14 @@ +description: "entity-client-serverApi-version-type" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 "client0" + serverApi: + version: "1" + deprecationErrors: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.json b/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.json new file mode 100644 index 0000000000..0b2fdc4849 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.json @@ -0,0 +1,21 @@ +{ + "description": "entity-client-serverApi-version-type", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": { + "version": "1", + "strict": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.yml b/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.yml new file mode 100644 index 0000000000..7aba8a30ea --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-strict-type.yml @@ -0,0 +1,14 @@ +description: "entity-client-serverApi-version-type" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 "client0" + serverApi: + version: "1" + strict: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-type.json b/source/unified-test-format/tests/invalid/entity-client-serverApi-type.json new file mode 100644 index 0000000000..20c9d1dce3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-serverApi-type", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-type.yml b/source/unified-test-format/tests/invalid/entity-client-serverApi-type.yml new file mode 100644 index 0000000000..1fc641f9f5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-serverApi-type" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 "client0" + serverApi: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.json b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.json new file mode 100644 index 0000000000..8bef92b06f --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-serverApi-version-required", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": {} + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.yml b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.yml new file mode 100644 index 0000000000..218c7f76b0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-required.yml @@ -0,0 +1,12 @@ +description: "entity-client-serverApi-version-required" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 "client0" + serverApi: {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.json b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.json new file mode 100644 index 0000000000..2c36ff57ed --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-serverApi-version-type", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": { + "version": 0 + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.yml b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.yml new file mode 100644 index 0000000000..27530b8517 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-serverApi-version-type.yml @@ -0,0 +1,13 @@ +description: "entity-client-serverApi-version-type" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 "client0" + serverApi: + version: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.json b/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.json new file mode 100644 index 0000000000..4252480e98 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-uriOptions-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "uriOptions": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.yml b/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.yml new file mode 100644 index 0000000000..09bc9f25d3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-uriOptions-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-uriOptions-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + uriOptions: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.json b/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.json new file mode 100644 index 0000000000..e429cd71f8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-useMultipleMongoses-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.yml b/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.yml new file mode 100644 index 0000000000..ef5e4baae9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-client-useMultipleMongoses-type.yml @@ -0,0 +1,12 @@ +description: "entity-client-useMultipleMongoses-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + useMultipleMongoses: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.json new file mode 100644 index 0000000000..77c0a91434 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.json @@ -0,0 +1,30 @@ +{ + "description": "entity-clientEncryption-additionalProperties", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + } + }, + "invalid": {} + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.yml new file mode 100644 index 0000000000..636896e39e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-additionalProperties.yml @@ -0,0 +1,19 @@ +description: entity-clientEncryption-additionalProperties + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + invalid: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.json b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.json new file mode 100644 index 0000000000..88e852342a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.json @@ -0,0 +1,17 @@ +{ + "description": "entity-clientEncryption-clientEncryptionOpts-required", + "schemaVersion": "1.8", + "createEntities": [ + { + "clientEncryption": { + "id": "clientEncryption0" + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.yml b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.yml new file mode 100644 index 0000000000..b2af763f0a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-required.yml @@ -0,0 +1,11 @@ +description: entity-clientEncryption-clientEncryptionOpts-required + +schemaVersion: "1.8" + +createEntities: + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.json b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.json new file mode 100644 index 0000000000..77fb6a362a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-clientEncryption-clientEncryptionOpts-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": 0 + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.yml b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.yml new file mode 100644 index 0000000000..354a14afdb --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-clientEncryptionOpts-type.yml @@ -0,0 +1,12 @@ +description: entity-clientEncryption-clientEncryptionOpts-type + +schemaVersion: "1.8" + +createEntities: + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: 0 + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.json b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.json new file mode 100644 index 0000000000..464ba7159a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.json @@ -0,0 +1,28 @@ +{ + "description": "entity-clientEncryption-id-required", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.yml b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.yml new file mode 100644 index 0000000000..1cda4eff50 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-required.yml @@ -0,0 +1,17 @@ +description: entity-clientEncryption-id-required + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.json b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.json new file mode 100644 index 0000000000..a7746657fc --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.json @@ -0,0 +1,29 @@ +{ + "description": "entity-clientEncryption-id-type", + "schemaVersion": "1.8", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "clientEncryption": { + "id": 0, + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws": {} + } + } + } + } + ], + "tests": [ + { + "description": "", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.yml b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.yml new file mode 100644 index 0000000000..df3095fd1d --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-clientEncryption-id-type.yml @@ -0,0 +1,18 @@ +description: entity-clientEncryption-id-type + +schemaVersion: "1.8" + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: 0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.json new file mode 100644 index 0000000000..90ee2b1ca0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.json @@ -0,0 +1,32 @@ +{ + "description": "entity-collection-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.yml new file mode 100644 index 0000000000..2010de017a --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-additionalProperties.yml @@ -0,0 +1,20 @@ +description: "entity-collection-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: "foo" + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.json b/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.json new file mode 100644 index 0000000000..2446722e5e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.json @@ -0,0 +1,30 @@ +{ + "description": "entity-collection-collectionName-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.yml b/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.yml new file mode 100644 index 0000000000..97e1616207 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionName-required.yml @@ -0,0 +1,18 @@ +description: "entity-collection-collectionName-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.json b/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.json new file mode 100644 index 0000000000..ccad66aac9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.json @@ -0,0 +1,31 @@ +{ + "description": "entity-collection-collectionName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.yml b/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.yml new file mode 100644 index 0000000000..46bcb473f8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionName-type.yml @@ -0,0 +1,19 @@ +description: "entity-collection-collectionName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.json b/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.json new file mode 100644 index 0000000000..52220c1cd1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.json @@ -0,0 +1,32 @@ +{ + "description": "entity-collection-collectionOptions-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "foo", + "collectionOptions": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.yml b/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.yml new file mode 100644 index 0000000000..58d101981d --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-collectionOptions-type.yml @@ -0,0 +1,20 @@ +description: "entity-collection-collectionOptions-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + database: *database0 + collectionName: "foo" + collectionOptions: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-database-required.json b/source/unified-test-format/tests/invalid/entity-collection-database-required.json new file mode 100644 index 0000000000..ba96b43f76 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-database-required.json @@ -0,0 +1,30 @@ +{ + "description": "entity-collection-database-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-database-required.yml b/source/unified-test-format/tests/invalid/entity-collection-database-required.yml new file mode 100644 index 0000000000..6a584cd9b6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-database-required.yml @@ -0,0 +1,18 @@ +description: "entity-collection-database-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-database-type.json b/source/unified-test-format/tests/invalid/entity-collection-database-type.json new file mode 100644 index 0000000000..b87134498d --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-database-type.json @@ -0,0 +1,31 @@ +{ + "description": "entity-collection-database-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": "collection0", + "database": 0, + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-database-type.yml b/source/unified-test-format/tests/invalid/entity-collection-database-type.yml new file mode 100644 index 0000000000..8ed3c743e9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-database-type.yml @@ -0,0 +1,19 @@ +description: "entity-collection-database-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: &collection0 "collection0" + database: 0 + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-id-required.json b/source/unified-test-format/tests/invalid/entity-collection-id-required.json new file mode 100644 index 0000000000..84e5352ead --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-id-required.json @@ -0,0 +1,30 @@ +{ + "description": "entity-collection-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "database": "database0", + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-id-required.yml b/source/unified-test-format/tests/invalid/entity-collection-id-required.yml new file mode 100644 index 0000000000..83de062263 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-id-required.yml @@ -0,0 +1,18 @@ +description: "entity-collection-id-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + database: *database0 + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-collection-id-type.json b/source/unified-test-format/tests/invalid/entity-collection-id-type.json new file mode 100644 index 0000000000..f0821e5250 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-id-type.json @@ -0,0 +1,31 @@ +{ + "description": "entity-collection-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + }, + { + "collection": { + "id": 0, + "database": "database0", + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-collection-id-type.yml b/source/unified-test-format/tests/invalid/entity-collection-id-type.yml new file mode 100644 index 0000000000..c7bfa502ee --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-collection-id-type.yml @@ -0,0 +1,19 @@ +description: "entity-collection-id-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + - collection: + id: 0 + database: *database0 + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-database-additionalProperties.json new file mode 100644 index 0000000000..964cd27966 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-additionalProperties.json @@ -0,0 +1,25 @@ +{ + "description": "entity-database-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-database-additionalProperties.yml new file mode 100644 index 0000000000..dcfdc048dc --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-additionalProperties.yml @@ -0,0 +1,16 @@ +description: "entity-database-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-client-required.json b/source/unified-test-format/tests/invalid/entity-database-client-required.json new file mode 100644 index 0000000000..54f99cf13e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-client-required.json @@ -0,0 +1,23 @@ +{ + "description": "entity-database-client-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-client-required.yml b/source/unified-test-format/tests/invalid/entity-database-client-required.yml new file mode 100644 index 0000000000..6e271b1859 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-client-required.yml @@ -0,0 +1,14 @@ +description: "entity-database-client-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-client-type.json b/source/unified-test-format/tests/invalid/entity-database-client-type.json new file mode 100644 index 0000000000..ff4584c405 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-client-type.json @@ -0,0 +1,24 @@ +{ + "description": "entity-database-client-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": 0, + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-client-type.yml b/source/unified-test-format/tests/invalid/entity-database-client-type.yml new file mode 100644 index 0000000000..fe04610863 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-client-type.yml @@ -0,0 +1,15 @@ +description: "entity-database-client-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: 0 + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseName-required.json b/source/unified-test-format/tests/invalid/entity-database-databaseName-required.json new file mode 100644 index 0000000000..64cca95c49 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseName-required.json @@ -0,0 +1,23 @@ +{ + "description": "entity-database-databaseName-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseName-required.yml b/source/unified-test-format/tests/invalid/entity-database-databaseName-required.yml new file mode 100644 index 0000000000..cd2c6e77a0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseName-required.yml @@ -0,0 +1,14 @@ +description: "entity-database-databaseName-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseName-type.json b/source/unified-test-format/tests/invalid/entity-database-databaseName-type.json new file mode 100644 index 0000000000..bd01aef781 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseName-type.json @@ -0,0 +1,24 @@ +{ + "description": "entity-database-databaseName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseName-type.yml b/source/unified-test-format/tests/invalid/entity-database-databaseName-type.yml new file mode 100644 index 0000000000..34ec075b64 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseName-type.yml @@ -0,0 +1,15 @@ +description: "entity-database-databaseName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.json b/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.json new file mode 100644 index 0000000000..bc22ad3129 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.json @@ -0,0 +1,25 @@ +{ + "description": "entity-database-databaseOptions-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo", + "databaseOptions": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.yml b/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.yml new file mode 100644 index 0000000000..77828e5390 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-databaseOptions-type.yml @@ -0,0 +1,16 @@ +description: "entity-database-databaseOptions-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + databaseOptions: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-id-required.json b/source/unified-test-format/tests/invalid/entity-database-id-required.json new file mode 100644 index 0000000000..0b65cf1159 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-id-required.json @@ -0,0 +1,23 @@ +{ + "description": "entity-database-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "client": "client0", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-id-required.yml b/source/unified-test-format/tests/invalid/entity-database-id-required.yml new file mode 100644 index 0000000000..51e63b4ab1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-id-required.yml @@ -0,0 +1,14 @@ +description: "entity-database-id-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + client: *client0 + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-database-id-type.json b/source/unified-test-format/tests/invalid/entity-database-id-type.json new file mode 100644 index 0000000000..98b5789d04 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-id-type.json @@ -0,0 +1,24 @@ +{ + "description": "entity-database-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": 0, + "client": "client0", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-database-id-type.yml b/source/unified-test-format/tests/invalid/entity-database-id-type.yml new file mode 100644 index 0000000000..1174c36ca4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-database-id-type.yml @@ -0,0 +1,15 @@ +description: "entity-database-id-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - database: + id: 0 + client: *client0 + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-maxProperties.json b/source/unified-test-format/tests/invalid/entity-maxProperties.json new file mode 100644 index 0000000000..f4a6b7c914 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-maxProperties.json @@ -0,0 +1,22 @@ +{ + "description": "entity-maxProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + }, + "database": { + "id": "database0", + "client": "client0", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-maxProperties.yml b/source/unified-test-format/tests/invalid/entity-maxProperties.yml new file mode 100644 index 0000000000..c6f8bcffc5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-maxProperties.yml @@ -0,0 +1,15 @@ +description: "entity-maxProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + database: + id: &database0 "database0" + client: *client0 + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-minProperties.json b/source/unified-test-format/tests/invalid/entity-minProperties.json new file mode 100644 index 0000000000..d89949ce30 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-minProperties.json @@ -0,0 +1,13 @@ +{ + "description": "entity-minProperties", + "schemaVersion": "1.0", + "createEntities": [ + {} + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-minProperties.yml b/source/unified-test-format/tests/invalid/entity-minProperties.yml new file mode 100644 index 0000000000..d758bd5745 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-minProperties.yml @@ -0,0 +1,10 @@ +description: "entity-minProperties" + +schemaVersion: "1.0" + +createEntities: + - {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-session-additionalProperties.json new file mode 100644 index 0000000000..ab4cd2014f --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "entity-session-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-session-additionalProperties.yml new file mode 100644 index 0000000000..1bb2233043 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "entity-session-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + id: &session0 "session0" + client: *client0 + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-client-required.json b/source/unified-test-format/tests/invalid/entity-session-client-required.json new file mode 100644 index 0000000000..8c9ed72e99 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-client-required.json @@ -0,0 +1,22 @@ +{ + "description": "entity-session-client-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "id": "session0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-client-required.yml b/source/unified-test-format/tests/invalid/entity-session-client-required.yml new file mode 100644 index 0000000000..3c1fe4e3c0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-client-required.yml @@ -0,0 +1,13 @@ +description: "entity-session-client-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + id: &session0 "session0" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-client-type.json b/source/unified-test-format/tests/invalid/entity-session-client-type.json new file mode 100644 index 0000000000..b5ccc3f60f --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-client-type.json @@ -0,0 +1,23 @@ +{ + "description": "entity-session-client-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "id": "session0", + "client": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-client-type.yml b/source/unified-test-format/tests/invalid/entity-session-client-type.yml new file mode 100644 index 0000000000..60353c08db --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-client-type.yml @@ -0,0 +1,14 @@ +description: "entity-session-client-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + id: &session0 "session0" + client: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-id-required.json b/source/unified-test-format/tests/invalid/entity-session-id-required.json new file mode 100644 index 0000000000..3e5d5c5439 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-id-required.json @@ -0,0 +1,22 @@ +{ + "description": "entity-session-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "client": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-id-required.yml b/source/unified-test-format/tests/invalid/entity-session-id-required.yml new file mode 100644 index 0000000000..2aa4269d16 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-id-required.yml @@ -0,0 +1,13 @@ +description: "entity-session-id-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + client: *client0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-id-type.json b/source/unified-test-format/tests/invalid/entity-session-id-type.json new file mode 100644 index 0000000000..dcd46e5be7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-id-type.json @@ -0,0 +1,23 @@ +{ + "description": "entity-session-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "id": 0, + "client": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-id-type.yml b/source/unified-test-format/tests/invalid/entity-session-id-type.yml new file mode 100644 index 0000000000..058dcdb1b7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-id-type.yml @@ -0,0 +1,14 @@ +description: "entity-session-id-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + id: 0 + client: *client0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.json b/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.json new file mode 100644 index 0000000000..0ee15891eb --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.json @@ -0,0 +1,24 @@ +{ + "description": "entity-session-sessionOptions-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "session": { + "id": "session0", + "client": "client0", + "sessionOptions": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.yml b/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.yml new file mode 100644 index 0000000000..2d651f690b --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-session-sessionOptions-type.yml @@ -0,0 +1,15 @@ +description: "entity-session-sessionOptions-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + - session: + id: &session0 "session0" + client: *client0 + sessionOptions: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.json new file mode 100644 index 0000000000..c8e76e9985 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.json @@ -0,0 +1,19 @@ +{ + "description": "entity-stream-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "id": "stream0", + "hexBytes": "FF", + "foo": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.yml new file mode 100644 index 0000000000..e2f4f8e93d --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-additionalProperties.yml @@ -0,0 +1,13 @@ +description: "entity-stream-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - stream: + id: &stream0 "stream0" + hexBytes: "FF" + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.json b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.json new file mode 100644 index 0000000000..7381893b55 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.json @@ -0,0 +1,18 @@ +{ + "description": "entity-stream-hexBytes-pattern", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "id": "stream0", + "hexBytes": "FFF" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.yml b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.yml new file mode 100644 index 0000000000..3e1aa8e45d --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-pattern.yml @@ -0,0 +1,12 @@ +description: "entity-stream-hexBytes-pattern" + +schemaVersion: "1.0" + +createEntities: + - stream: + id: &stream0 "stream0" + hexBytes: "FFF" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.json b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.json new file mode 100644 index 0000000000..cc3bf09b20 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.json @@ -0,0 +1,17 @@ +{ + "description": "entity-stream-hexBytes-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "id": "stream0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.yml b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.yml new file mode 100644 index 0000000000..b1a4b98561 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-required.yml @@ -0,0 +1,11 @@ +description: "entity-stream-hexBytes-required" + +schemaVersion: "1.0" + +createEntities: + - stream: + id: &stream0 "stream0" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.json b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.json new file mode 100644 index 0000000000..e6e2299eac --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-stream-hexBytes-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "id": "stream0", + "hexBytes": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.yml b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.yml new file mode 100644 index 0000000000..79b8188c5b --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-hexBytes-type.yml @@ -0,0 +1,12 @@ +description: "entity-stream-hexBytes-type" + +schemaVersion: "1.0" + +createEntities: + - stream: + id: &stream0 "stream0" + hexBytes: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-id-required.json b/source/unified-test-format/tests/invalid/entity-stream-id-required.json new file mode 100644 index 0000000000..ff814d4e9c --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-id-required.json @@ -0,0 +1,17 @@ +{ + "description": "entity-stream-id-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "hexBytes": "FF" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-id-required.yml b/source/unified-test-format/tests/invalid/entity-stream-id-required.yml new file mode 100644 index 0000000000..0ca388aade --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-id-required.yml @@ -0,0 +1,11 @@ +description: "entity-stream-id-required" + +schemaVersion: "1.0" + +createEntities: + - stream: + hexBytes: "FF" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-stream-id-type.json b/source/unified-test-format/tests/invalid/entity-stream-id-type.json new file mode 100644 index 0000000000..5fc654d97e --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-id-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-stream-id-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "stream": { + "id": 0, + "hexBytes": "FF" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-stream-id-type.yml b/source/unified-test-format/tests/invalid/entity-stream-id-type.yml new file mode 100644 index 0000000000..d7b17952cb --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-stream-id-type.yml @@ -0,0 +1,12 @@ +description: "entity-stream-id-type" + +schemaVersion: "1.0" + +createEntities: + - stream: + id: 0 + hexBytes: "FF" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.json b/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.json new file mode 100644 index 0000000000..b296719f13 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.json @@ -0,0 +1,18 @@ +{ + "description": "entity-thread-additionalProperties", + "schemaVersion": "1.10", + "createEntities": [ + { + "thread": { + "id": "thread0", + "foo": "bar" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.yml b/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.yml new file mode 100644 index 0000000000..b3fb1dc514 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-additionalProperties.yml @@ -0,0 +1,12 @@ +description: "entity-thread-additionalProperties" + +schemaVersion: "1.10" + +createEntities: + - thread: + id: &thread0 "thread0" + foo: "bar" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-thread-id-required.json b/source/unified-test-format/tests/invalid/entity-thread-id-required.json new file mode 100644 index 0000000000..3b197e3d6b --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-id-required.json @@ -0,0 +1,15 @@ +{ + "description": "entity-thread-id-required", + "schemaVersion": "1.10", + "createEntities": [ + { + "thread": {} + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-thread-id-required.yml b/source/unified-test-format/tests/invalid/entity-thread-id-required.yml new file mode 100644 index 0000000000..b940d4d5c6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-id-required.yml @@ -0,0 +1,10 @@ +description: "entity-thread-id-required" + +schemaVersion: "1.10" + +createEntities: + - thread: {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/entity-thread-id-type.json b/source/unified-test-format/tests/invalid/entity-thread-id-type.json new file mode 100644 index 0000000000..8f281ef6f4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-id-type.json @@ -0,0 +1,17 @@ +{ + "description": "entity-thread-id-type", + "schemaVersion": "1.10", + "createEntities": [ + { + "thread": { + "id": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/entity-thread-id-type.yml b/source/unified-test-format/tests/invalid/entity-thread-id-type.yml new file mode 100644 index 0000000000..85646ce9c0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/entity-thread-id-type.yml @@ -0,0 +1,11 @@ +description: "entity-thread-id-type" + +schemaVersion: "1.10" + +createEntities: + - thread: + id: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.json new file mode 100644 index 0000000000..110ce7869e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionCheckOutFailedEvent-reason-type", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutFailedEvent": { + "reason": 10 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.yml new file mode 100644 index 0000000000..f86972968d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutFailedEvent-reason-type.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionCheckOutFailedEvent-reason-type + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionCheckOutFailedEvent: + reason: 10 diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.json new file mode 100644 index 0000000000..f84e208d6a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.yml new file mode 100644 index 0000000000..e3aa7e6bce --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionCheckOutStartedEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionCheckOutStartedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.json new file mode 100644 index 0000000000..56ffcdee72 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionCheckedInEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckedInEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.yml new file mode 100644 index 0000000000..d610c94019 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedInEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionCheckedInEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionCheckedInEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.json new file mode 100644 index 0000000000..9b804aad0a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionCheckedOutEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCheckedOutEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.yml new file mode 100644 index 0000000000..560b8569e3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCheckedOutEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionCheckedOutEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionCheckedOutEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.json new file mode 100644 index 0000000000..053cd0b413 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionClosedEvent-reason-type", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionClosedEvent": { + "reason": 10 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.yml new file mode 100644 index 0000000000..06a1d39266 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionClosedEvent-reason-type.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionClosedEvent-reason-type + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionClosedEvent: + reason: 10 diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.json new file mode 100644 index 0000000000..c2edc3f6aa --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionCreatedEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.yml new file mode 100644 index 0000000000..1ff92d5f62 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionCreatedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionCreatedEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionCreatedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.json new file mode 100644 index 0000000000..994fb63314 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-connectionReadyEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "connectionReadyEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.yml new file mode 100644 index 0000000000..2835190c06 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-connectionReadyEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-connectionReadyEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - connectionReadyEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.json new file mode 100644 index 0000000000..5a1a25d463 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolClearedEvent-hasServiceId-type", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolClearedEvent": { + "hasServiceId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.yml new file mode 100644 index 0000000000..156581d53a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-hasServiceId-type.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolClearedEvent-hasServiceId-type + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolClearedEvent: + hasServiceId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json new file mode 100644 index 0000000000..de59318822 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type", + "schemaVersion": "1.11", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolClearedEvent": { + "interruptInUseConnections": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.yml new file mode 100644 index 0000000000..06bef37d78 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type + +schemaVersion: '1.11' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolClearedEvent: + interruptInUseConnections: foo diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.json new file mode 100644 index 0000000000..c181707f4a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolClosedEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolClosedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.yml new file mode 100644 index 0000000000..1ff0220e59 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolClosedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolClosedEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolClosedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.json new file mode 100644 index 0000000000..6aaa59a600 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolCreatedEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolCreatedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.yml new file mode 100644 index 0000000000..fd9c8c0046 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolCreatedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolCreatedEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolCreatedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.json new file mode 100644 index 0000000000..66c803a5d8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolReadyEvent-additionalProperties", + "schemaVersion": "1.3", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolReadyEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.yml new file mode 100644 index 0000000000..dd2ad8a5de --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolReadyEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolReadyEvent-additionalProperties + +schemaVersion: '1.3' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolReadyEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.json new file mode 100644 index 0000000000..9e45cbadda --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.json @@ -0,0 +1,27 @@ +{ + "description": "expectedCommandEvent-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "foo": 0 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.yml new file mode 100644 index 0000000000..f68da7c710 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "expectedCommandEvent-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.json new file mode 100644 index 0000000000..a571d8e0c0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandFailedEvent-commandName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandFailedEvent": { + "commandName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.yml new file mode 100644 index 0000000000..41fc57d3a1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-commandName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandFailedEvent-commandName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandFailedEvent: + commandName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json new file mode 100644 index 0000000000..f6a305b89a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandFailedEvent-databaseName-type", + "schemaVersion": "1.15", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandFailedEvent": { + "databaseName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.yml new file mode 100644 index 0000000000..9ab33ad59e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandFailedEvent-databaseName-type" + +schemaVersion: "1.15" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandFailedEvent: + databaseName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.json new file mode 100644 index 0000000000..7787ea6516 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type", + "schemaVersion": "1.6", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandFailedEvent": { + "hasServerConnectionId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.yml new file mode 100644 index 0000000000..3940147192 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandFailedEvent-hasServerConnectionId-type + +schemaVersion: '1.6' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandFailedEvent: + hasServerConnectionId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.json new file mode 100644 index 0000000000..5314dc9f80 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandFailedEvent-hasServiceId-type", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandFailedEvent": { + "hasServiceId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.yml new file mode 100644 index 0000000000..afb3292e54 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandFailedEvent-hasServiceId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandFailedEvent-hasServiceId-type + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandFailedEvent: + hasServiceId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.json new file mode 100644 index 0000000000..996332d27d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "foo": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.yml new file mode 100644 index 0000000000..d668092f6e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-additionalProperties.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandStartedEvent-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.json new file mode 100644 index 0000000000..8f89460617 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-command-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.yml new file mode 100644 index 0000000000..ffedc4ac2d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-command-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandStartedEvent-command-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.json new file mode 100644 index 0000000000..121947b06f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-commandName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.yml new file mode 100644 index 0000000000..97331a0ce1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-commandName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandStartedEvent-commandName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + commandName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.json new file mode 100644 index 0000000000..97d2b84f68 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-databaseName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.yml new file mode 100644 index 0000000000..9a5786bb79 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-databaseName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandStartedEvent-databaseName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.json new file mode 100644 index 0000000000..a913f00ab7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type", + "schemaVersion": "1.6", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "hasServerConnectionId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.yml new file mode 100644 index 0000000000..a78e6aac45 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandStartedEvent-hasServerConnectionId-type + +schemaVersion: '1.6' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + hasServerConnectionId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.json new file mode 100644 index 0000000000..39ab925efb --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandStartedEvent-hasServiceId-type", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "hasServiceId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.yml new file mode 100644 index 0000000000..83d0c7a483 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandStartedEvent-hasServiceId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandStartedEvent-hasServiceId-type + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + hasServiceId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.json new file mode 100644 index 0000000000..bde2f4817b --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-commandName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "commandName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.yml new file mode 100644 index 0000000000..f4e7dd93c9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-commandName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandSucceededEvent-commandName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandSucceededEvent: + commandName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json new file mode 100644 index 0000000000..47b8c8bb9d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-databaseName-type", + "schemaVersion": "1.15", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "databaseName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.yml new file mode 100644 index 0000000000..94adc2b730 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandSucceededEvent-databaseName-type" + +schemaVersion: "1.15" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandSucceededEvent: + databaseName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.json new file mode 100644 index 0000000000..0712c33694 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type", + "schemaVersion": "1.6", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "hasServerConnectionId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.yml new file mode 100644 index 0000000000..6b2219f6db --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandSucceededEvent-hasServerConnectionId-type + +schemaVersion: '1.6' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandSucceededEvent: + hasServerConnectionId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.json new file mode 100644 index 0000000000..edc9d3cd72 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-hasServiceId-type", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "hasServiceId": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.yml new file mode 100644 index 0000000000..15dd983369 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-hasServiceId-type.yml @@ -0,0 +1,16 @@ +description: expectedCommandEvent-commandSucceededEvent-hasServiceId-type + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: foo + operations: [] + expectEvents: + - client: *client0 + events: + - commandSucceededEvent: + hasServiceId: foo diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.json new file mode 100644 index 0000000000..9df04acd29 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-reply-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "reply": 0 + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.yml new file mode 100644 index 0000000000..477fe7919e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-commandSucceededEvent-reply-type.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-commandSucceededEvent-reply-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandSucceededEvent: + reply: 0 diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.json new file mode 100644 index 0000000000..dd8b0e7e7c --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.json @@ -0,0 +1,28 @@ +{ + "description": "expectedCommandEvent-maxProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": {}, + "commandSucceededEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.yml new file mode 100644 index 0000000000..62d5bb4fec --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-maxProperties.yml @@ -0,0 +1,16 @@ +description: "expectedCommandEvent-maxProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: {} + commandSucceededEvent: {} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.json b/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.json new file mode 100644 index 0000000000..0f3e711a18 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.json @@ -0,0 +1,25 @@ +{ + "description": "expectedCommandEvent-minProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + {} + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.yml b/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.yml new file mode 100644 index 0000000000..3b7d08d412 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCommandEvent-minProperties.yml @@ -0,0 +1,15 @@ +description: "expectedCommandEvent-minProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: + - {} diff --git a/source/unified-test-format/tests/invalid/expectedError-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedError-additionalProperties.json new file mode 100644 index 0000000000..3a79df8e34 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-additionalProperties.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "foo": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedError-additionalProperties.yml new file mode 100644 index 0000000000..6146367962 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "expectedError-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-errorCode-type.json b/source/unified-test-format/tests/invalid/expectedError-errorCode-type.json new file mode 100644 index 0000000000..b6b6f5d05a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorCode-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorCode-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorCode": "foo" + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorCode-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorCode-type.yml new file mode 100644 index 0000000000..2110e9c6ad --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorCode-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorCode-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorCode: "foo" diff --git a/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.json b/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.json new file mode 100644 index 0000000000..3ac5e43045 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorCodeName-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorCodeName": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.yml new file mode 100644 index 0000000000..c35ec3ed4a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorCodeName-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorCodeName-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorCodeName: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-errorContains-type.json b/source/unified-test-format/tests/invalid/expectedError-errorContains-type.json new file mode 100644 index 0000000000..847a987dff --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorContains-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorContains-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorContains": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorContains-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorContains-type.yml new file mode 100644 index 0000000000..f4989b61e3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorContains-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorContains-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorContains: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.json new file mode 100644 index 0000000000..4eab56ad18 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.json @@ -0,0 +1,27 @@ +{ + "description": "expectedError-errorLabelsContain-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsContain": [ + 0 + ] + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.yml new file mode 100644 index 0000000000..4382fa645a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-items.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsContain-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsContain: [0] diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.json new file mode 100644 index 0000000000..48162110aa --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorLabelsContain-minItems", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsContain": [] + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.yml new file mode 100644 index 0000000000..ebd650e11a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-minItems.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsContain-minItems" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsContain: [] diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.json new file mode 100644 index 0000000000..a0aba918b5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorLabelsContain-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsContain": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.yml new file mode 100644 index 0000000000..537cfb9cfe --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsContain-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsContain-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsContain: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.json new file mode 100644 index 0000000000..6c94d07135 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.json @@ -0,0 +1,27 @@ +{ + "description": "expectedError-errorLabelsOmit-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsOmit": [ + 0 + ] + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.yml new file mode 100644 index 0000000000..d80711ef0f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-items.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsOmit-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsOmit: [0] diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.json new file mode 100644 index 0000000000..88c6582028 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorLabelsOmit-minItems", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsOmit": [] + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.yml new file mode 100644 index 0000000000..205162cf67 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-minItems.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsOmit-minItems" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsOmit: [] diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.json b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.json new file mode 100644 index 0000000000..5f57114fea --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorLabelsOmit-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorLabelsOmit": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.yml new file mode 100644 index 0000000000..6c1947b043 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorLabelsOmit-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorLabelsOmit-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorLabelsOmit: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.json b/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.json new file mode 100644 index 0000000000..6eb66d9b0b --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-errorResponse-type", + "schemaVersion": "1.12", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "errorResponse": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.yml b/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.yml new file mode 100644 index 0000000000..e63f6ce892 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-errorResponse-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-errorResponse-type" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + errorResponse: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-isClientError-type.json b/source/unified-test-format/tests/invalid/expectedError-isClientError-type.json new file mode 100644 index 0000000000..bfcc06679b --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isClientError-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-isClientError-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isClientError": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-isClientError-type.yml b/source/unified-test-format/tests/invalid/expectedError-isClientError-type.yml new file mode 100644 index 0000000000..8b8afc1061 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isClientError-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-isClientError-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isClientError: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-isError-const.json b/source/unified-test-format/tests/invalid/expectedError-isError-const.json new file mode 100644 index 0000000000..6a398bbf22 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isError-const.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-isError-const", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isError": false + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-isError-const.yml b/source/unified-test-format/tests/invalid/expectedError-isError-const.yml new file mode 100644 index 0000000000..347ae2965a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isError-const.yml @@ -0,0 +1,15 @@ +description: "expectedError-isError-const" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isError: false diff --git a/source/unified-test-format/tests/invalid/expectedError-isError-type.json b/source/unified-test-format/tests/invalid/expectedError-isError-type.json new file mode 100644 index 0000000000..354aff31f4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isError-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-isError-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isError": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-isError-type.yml b/source/unified-test-format/tests/invalid/expectedError-isError-type.yml new file mode 100644 index 0000000000..3e53f4af90 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isError-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-isError-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isError: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.json b/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.json new file mode 100644 index 0000000000..5683911d0d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedError-isTimeoutError-type", + "schemaVersion": "1.9", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isTimeoutError": 0 + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.yml b/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.yml new file mode 100644 index 0000000000..9d9db95350 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-isTimeoutError-type.yml @@ -0,0 +1,15 @@ +description: "expectedError-isTimeoutError-type" + +schemaVersion: "1.9" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isTimeoutError: 0 diff --git a/source/unified-test-format/tests/invalid/expectedError-minProperties.json b/source/unified-test-format/tests/invalid/expectedError-minProperties.json new file mode 100644 index 0000000000..10e0b89ab7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-minProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedError-minProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": {} + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedError-minProperties.yml b/source/unified-test-format/tests/invalid/expectedError-minProperties.yml new file mode 100644 index 0000000000..5292d85008 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedError-minProperties.yml @@ -0,0 +1,14 @@ +description: "expectedError-minProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: {} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.json new file mode 100644 index 0000000000..90ed9c3273 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "expectedEventsForClient-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [], + "foo": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.yml new file mode 100644 index 0000000000..9784c02ec1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "expectedEventsForClient-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: [] + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.json new file mode 100644 index 0000000000..24b6330de7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedEventsForClient-client-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "events": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.yml new file mode 100644 index 0000000000..beba7080b0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-required.yml @@ -0,0 +1,13 @@ +description: "expectedEventsForClient-client-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - events: [] diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.json new file mode 100644 index 0000000000..6e66857ee6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedEventsForClient-client-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": 0, + "events": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.yml new file mode 100644 index 0000000000..8d572fc451 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-client-type.yml @@ -0,0 +1,14 @@ +description: "expectedEventsForClient-client-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: 0 + events: [] diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.json new file mode 100644 index 0000000000..6e26cfaa7e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.json @@ -0,0 +1,24 @@ +{ + "description": "expectedEventsForClient-eventType-enum", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "invalid eventType value", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "foo", + "events": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.yml new file mode 100644 index 0000000000..792e752130 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-enum.yml @@ -0,0 +1,15 @@ +description: expectedEventsForClient-eventType-enum + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: invalid eventType value + operations: [] + expectEvents: + - client: *client0 + eventType: foo + events: [] diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.json new file mode 100644 index 0000000000..105bb001e5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedEventsForClient-eventType-type", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "invalid eventType type", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": 10, + "events": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.yml new file mode 100644 index 0000000000..ce1b0c9798 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-eventType-type.yml @@ -0,0 +1,15 @@ +description: expectedEventsForClient-eventType-type + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: invalid eventType type + operations: [] + expectEvents: + - client: *client0 + eventType: 10 + events: [] diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.json new file mode 100644 index 0000000000..c1fcd4a6c3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.json @@ -0,0 +1,25 @@ +{ + "description": "expectedEventsForClient-events-items", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + 0 + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.yml new file mode 100644 index 0000000000..1c3b927f9a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-items.yml @@ -0,0 +1,14 @@ +description: "expectedEventsForClient-events-items" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: [0] diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.json new file mode 100644 index 0000000000..39c1e9e12d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedEventsForClient-events-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.yml new file mode 100644 index 0000000000..86d162eba7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-required.yml @@ -0,0 +1,13 @@ +description: "expectedEventsForClient-events-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.json new file mode 100644 index 0000000000..4199d042b0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedEventsForClient-events-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.yml new file mode 100644 index 0000000000..24bd318ccf --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events-type.yml @@ -0,0 +1,14 @@ +description: "expectedEventsForClient-events-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: 0 diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.json new file mode 100644 index 0000000000..b380219912 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.json @@ -0,0 +1,28 @@ +{ + "description": "expectedEventsForClient-events_conflicts_with_cmap_eventType", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "invalid event when eventType is cmap", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "commandStartedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.yml new file mode 100644 index 0000000000..681d23429d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_cmap_eventType.yml @@ -0,0 +1,16 @@ +description: expectedEventsForClient-events_conflicts_with_cmap_eventType + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: invalid event when eventType is cmap + operations: [] + expectEvents: + - client: *client0 + eventType: cmap + events: + - commandStartedEvent: {} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.json new file mode 100644 index 0000000000..08446fe180 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.json @@ -0,0 +1,28 @@ +{ + "description": "expectedEventsForClient-events_conflicts_with_command_eventType", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "invalid event when eventType is command", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "command", + "events": [ + { + "poolCreatedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.yml new file mode 100644 index 0000000000..1e0788edc0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_command_eventType.yml @@ -0,0 +1,16 @@ +description: expectedEventsForClient-events_conflicts_with_command_eventType + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: invalid event when eventType is command + operations: [] + expectEvents: + - client: *client0 + eventType: command + events: + - poolCreatedEvent: {} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.json new file mode 100644 index 0000000000..c31efbb8b7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.json @@ -0,0 +1,27 @@ +{ + "description": "expectedEventsForClient-events_conflicts_with_default_eventType", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "invalid event when eventType is unset", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "poolCreatedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.yml new file mode 100644 index 0000000000..49048bf2d2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-events_conflicts_with_default_eventType.yml @@ -0,0 +1,15 @@ +description: expectedEventsForClient-events_conflicts_with_default_eventType + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: invalid event when eventType is unset + operations: [] + expectEvents: + - client: *client0 + events: + - poolCreatedEvent: {} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.json b/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.json new file mode 100644 index 0000000000..965190664e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedEventsForClient-ignoreExtraEvents-type", + "schemaVersion": "1.7", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [], + "ignoreExtraEvents": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.yml b/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.yml new file mode 100644 index 0000000000..c42886899b --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedEventsForClient-ignoreExtraEvents-type.yml @@ -0,0 +1,15 @@ +description: "expectedEventsForClient-ignoreExtraEvents-type" + +schemaVersion: "1.7" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectEvents: + - client: *client0 + events: [] + ignoreExtraEvents: 0 diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.json new file mode 100644 index 0000000000..cd7cf8726c --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessage-additionalProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "foo": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.yml new file mode 100644 index 0000000000..83d8297ab6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "expectedLogMessage-additionalProperties" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: [] + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.json b/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.json new file mode 100644 index 0000000000..2283e9b243 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-component-enum", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "foo", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.yml new file mode 100644 index 0000000000..bde3791c56 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-enum.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-component-enum" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + component: foo + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.json b/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.json new file mode 100644 index 0000000000..f3a157787f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-component-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.yml new file mode 100644 index 0000000000..88f4734068 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-required.yml @@ -0,0 +1,16 @@ +description: "expectedLogMessage-component-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.json b/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.json new file mode 100644 index 0000000000..af8f711573 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-component-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": 0, + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.yml new file mode 100644 index 0000000000..57c1dbbba5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-component-type.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-component-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + component: 0 + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.json b/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.json new file mode 100644 index 0000000000..7e8152dddd --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-data-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command" + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.yml new file mode 100644 index 0000000000..a379ef60a2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-data-required.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-data-required" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + component: command + diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.json b/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.json new file mode 100644 index 0000000000..4f81fb6272 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-data-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command", + "data": 0 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.yml new file mode 100644 index 0000000000..7f4cad0aa6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-data-type.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-data-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + component: command + data: 0 diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.json b/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.json new file mode 100644 index 0000000000..190748a185 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.json @@ -0,0 +1,30 @@ +{ + "description": "expectedLogMessage-failureIsRedacted-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command", + "failureIsRedacted": 0, + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.yml new file mode 100644 index 0000000000..916ca8f6e4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-failureIsRedacted-type.yml @@ -0,0 +1,18 @@ +description: "expectedLogMessage-failureIsRedacted-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: debug + component: command + failureIsRedacted: 0 + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.json b/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.json new file mode 100644 index 0000000000..f4c886bb68 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-level-enum", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "foo", + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.yml new file mode 100644 index 0000000000..3c46048da1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-enum.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-level-enum" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: foo + component: command + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.json b/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.json new file mode 100644 index 0000000000..27c9c7a6cd --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-level-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.yml new file mode 100644 index 0000000000..1f2a89c3f9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-required.yml @@ -0,0 +1,16 @@ +description: "expectedLogMessage-level-required" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - component: command + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.json b/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.json new file mode 100644 index 0000000000..180d7afcd6 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-level-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": 0, + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.yml new file mode 100644 index 0000000000..9f304a1969 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessage-level-type.yml @@ -0,0 +1,17 @@ +description: "expectedLogMessage-level-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: + - level: 0 + component: command + data: {} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.json new file mode 100644 index 0000000000..306b78b446 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-additionalProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "foo": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.yml new file mode 100644 index 0000000000..d43ef98239 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-additionalProperties.yml @@ -0,0 +1,15 @@ +description: "expectedLogMessagesForClient-additionalProperties" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: [] + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.json new file mode 100644 index 0000000000..d8e1100bea --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedLogMessagesForClient-client-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "messages": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.yml new file mode 100644 index 0000000000..a311bf0bc3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-required.yml @@ -0,0 +1,13 @@ +description: "expectedLogMessagesForClient-client-required" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - messages: [] diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.json new file mode 100644 index 0000000000..5399cac029 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedEventsForClient-client-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": 0, + "messages": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.yml new file mode 100644 index 0000000000..2e268fd095 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-client-type.yml @@ -0,0 +1,14 @@ +description: "expectedEventsForClient-client-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: 0 + messages: [] diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json new file mode 100644 index 0000000000..a9f2da9bce --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-ignoreExtraMessages-type", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "ignoreExtraMessages": "true", + "messages": [] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.yml new file mode 100644 index 0000000000..3e11d35d82 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.yml @@ -0,0 +1,15 @@ +description: "expectedLogMessagesForClient-ignoreExtraMessages-type" + +schemaVersion: "1.16" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + ignoreExtraMessages: "true" + messages: [] diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.json new file mode 100644 index 0000000000..345faf41f5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.json @@ -0,0 +1,26 @@ +{ + "description": "expectedLogMessagesForClient-ignoreMessages-items", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "ignoreMessages": [ + 0 + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.yml new file mode 100644 index 0000000000..f429c3e5bf --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-items.yml @@ -0,0 +1,15 @@ +description: "expectedLogMessagesForClient-ignoreMessages-items" + +schemaVersion: "1.16" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: [] + ignoreMessages: [0] diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.json new file mode 100644 index 0000000000..4bc2d41dbf --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-ignoreMessages-type", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "ignoreMessages": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.yml new file mode 100644 index 0000000000..9512f3358e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-ignoreMessages-type.yml @@ -0,0 +1,15 @@ +description: "expectedLogMessagesForClient-ignoreMessages-type" + +schemaVersion: "1.16" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: [] + ignoreMessages: 0 diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.json new file mode 100644 index 0000000000..9788d8fe5c --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.json @@ -0,0 +1,25 @@ +{ + "description": "expectedLogMessagesForClient-messages-items", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + 0 + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.yml new file mode 100644 index 0000000000..e1f7e873cf --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-items.yml @@ -0,0 +1,14 @@ +description: "expectedLogMessagesForClient-messages-items" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: [0] diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.json new file mode 100644 index 0000000000..85d070672f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedLogMessagesForClient-messages-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.yml new file mode 100644 index 0000000000..7aaa90092c --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-required.yml @@ -0,0 +1,14 @@ +description: "expectedLogMessagesForClient-messages-required" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.json b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.json new file mode 100644 index 0000000000..27531667c5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedLogMessagesForClient-messages-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.yml b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.yml new file mode 100644 index 0000000000..f6c3d4b3f9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedLogMessagesForClient-messages-type.yml @@ -0,0 +1,14 @@ +description: "expectedLogMessagesForClient-messages-type" + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: [] + expectLogMessages: + - client: *client0 + messages: 0 diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.json new file mode 100644 index 0000000000..1c6ec460b7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties", + "schemaVersion": "1.10", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "serverDescriptionChangedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.yml new file mode 100644 index 0000000000..7d9580fe71 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedSdamEvent-serverDescriptionChangedEvent-additionalProperties + +schemaVersion: '1.10' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: sdam + events: + - serverDescriptionChangedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.json new file mode 100644 index 0000000000..58f686739a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.json @@ -0,0 +1,25 @@ +{ + "description": "expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties", + "schemaVersion": "1.10", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "serverDescriptionChangedEvent": { + "previousDescription": { + "foo": "bar" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.yml new file mode 100644 index 0000000000..4f5d744220 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties.yml @@ -0,0 +1,14 @@ +description: expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-additionalProperties + +schemaVersion: '1.10' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: sdam + events: + - serverDescriptionChangedEvent: + previousDescription: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.json b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.json new file mode 100644 index 0000000000..1b4a7e2e70 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.json @@ -0,0 +1,25 @@ +{ + "description": "expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum", + "schemaVersion": "1.10", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "serverDescriptionChangedEvent": { + "previousDescription": { + "type": "not a server type" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.yml b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.yml new file mode 100644 index 0000000000..5211cde784 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum.yml @@ -0,0 +1,14 @@ +description: expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-enum + +schemaVersion: '1.10' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: sdam + events: + - serverDescriptionChangedEvent: + previousDescription: + type: "not a server type" diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.json b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.json new file mode 100644 index 0000000000..c7ea9cc9be --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.json @@ -0,0 +1,25 @@ +{ + "description": "expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type", + "schemaVersion": "1.10", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "serverDescriptionChangedEvent": { + "previousDescription": { + "type": 12 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.yml b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.yml new file mode 100644 index 0000000000..3f856bbdae --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type.yml @@ -0,0 +1,14 @@ +description: expectedSdamEvent-serverDescriptionChangedEvent-serverDescription-type-type + +schemaVersion: '1.10' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: sdam + events: + - serverDescriptionChangedEvent: + previousDescription: + type: 12 diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json new file mode 100644 index 0000000000..ef2686e93f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties", + "schemaVersion": "1.14", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "topologyDescriptionChangedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.yml new file mode 100644 index 0000000000..9599d8c85e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.yml @@ -0,0 +1,13 @@ +description: expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties + +schemaVersion: '1.14' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: sdam + events: + - topologyDescriptionChangedEvent: + foo: bar diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.json new file mode 100644 index 0000000000..5947a286b2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.json @@ -0,0 +1,30 @@ +{ + "description": "expectedTracingSpans-additionalProperties", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "additional property foo not allowed in expectTracingMessages", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "ignoreExtraSpans": false, + "spans": [ + { + "name": "command", + "tags": { + "db.system": "mongodb" + } + } + ], + "foo": 0 + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.yml new file mode 100644 index 0000000000..a00a79f257 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-additionalProperties.yml @@ -0,0 +1,16 @@ +description: "expectedTracingSpans-additionalProperties" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "additional property foo not allowed in expectTracingMessages" + operations: [] + expectTracingMessages: + client: *client0 + ignoreExtraSpans: false + spans: [ { name: "command", tags: { db.system: mongodb } } ] + foo: 0 diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.json new file mode 100644 index 0000000000..2fe7faea34 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.json @@ -0,0 +1,28 @@ +{ + "description": "expectedTracingSpans-clientType", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "client type must be string", + "operations": [], + "expectTracingMessages": { + "client": 0, + "spans": [ + { + "name": "command", + "tags": { + "db.system": "mongodb" + } + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.yml new file mode 100644 index 0000000000..41af5de8bc --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-clientType.yml @@ -0,0 +1,14 @@ +description: "expectedTracingSpans-clientType" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "client type must be string" + operations: [] + expectTracingMessages: + client: 0 + spans: [ { name: "command", tags: { db.system: mongodb } } ] diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.json new file mode 100644 index 0000000000..8a98d5ba81 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.json @@ -0,0 +1,29 @@ +{ + "description": "expectedTracingSpans-emptyNestedSpan", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "nested spans must not have fewer than 1 items'", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "command", + "tags": { + "db.system": "mongodb" + }, + "nested": [] + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.yml new file mode 100644 index 0000000000..abc9f8b3b8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-emptyNestedSpan.yml @@ -0,0 +1,14 @@ +description: "expectedTracingSpans-emptyNestedSpan" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "nested spans must not have fewer than 1 items'" + operations: [] + expectTracingMessages: + client: *client0 + spans: [ { name: "command", tags: { db.system: mongodb }, nested:[] } ] diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.json new file mode 100644 index 0000000000..79a86744fb --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.json @@ -0,0 +1,31 @@ +{ + "description": "expectedTracingSpans-invalidNestedSpan", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "nested span must have required property name", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "command", + "tags": { + "db.system": "mongodb" + }, + "nested": [ + {} + ] + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.yml new file mode 100644 index 0000000000..6490c30502 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-invalidNestedSpan.yml @@ -0,0 +1,14 @@ +description: "expectedTracingSpans-invalidNestedSpan" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "nested span must have required property name" + operations: [] + expectTracingMessages: + client: *client0 + spans: [ { name: "command", tags: { db.system: mongodb }, nested: [ { } ] } ] diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.json new file mode 100644 index 0000000000..2fb1cd5bbc --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.json @@ -0,0 +1,27 @@ +{ + "description": "expectedTracingSpans-missingPropertyClient", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "missing required property client", + "operations": [], + "expectTracingMessages": { + "spans": [ + { + "name": "command", + "tags": { + "db.system": "mongodb" + } + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.yml new file mode 100644 index 0000000000..cb0a46f1da --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertyClient.yml @@ -0,0 +1,13 @@ +description: "expectedTracingSpans-missingPropertyClient" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "missing required property client" + operations: [] + expectTracingMessages: + spans: [ { name: "command", tags: { db.system: mongodb } } ] diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.json new file mode 100644 index 0000000000..acd1030736 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.json @@ -0,0 +1,20 @@ +{ + "description": "expectedTracingSpans-missingPropertySpans", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "missing required property spans", + "operations": [], + "expectTracingMessages": { + "client": "client0" + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.yml new file mode 100644 index 0000000000..b58f471189 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-missingPropertySpans.yml @@ -0,0 +1,13 @@ +description: "expectedTracingSpans-missingPropertySpans" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "missing required property spans" + operations: [] + expectTracingMessages: + client: *client0 diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.json new file mode 100644 index 0000000000..17299f8622 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.json @@ -0,0 +1,28 @@ +{ + "description": "expectedTracingSpans-spanMalformedAdditionalProperties", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "Span must not have additional properties", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "foo", + "tags": {}, + "nested": [], + "foo": "bar" + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.yml new file mode 100644 index 0000000000..fe209cc82f --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedAdditionalProperties.yml @@ -0,0 +1,19 @@ +description: "expectedTracingSpans-spanMalformedAdditionalProperties" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "Span must not have additional properties" + operations: [] + expectTracingMessages: + client: *client0 + spans: + - name: "foo" + tags: {} + nested: [] + foo: "bar" + diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.json new file mode 100644 index 0000000000..0257cd9b3d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.json @@ -0,0 +1,27 @@ +{ + "description": "expectedTracingSpans-spanMalformedMissingName", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "missing required span name", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "tags": { + "db.system": "mongodb" + } + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.yml new file mode 100644 index 0000000000..e983a2e074 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingName.yml @@ -0,0 +1,16 @@ +description: "expectedTracingSpans-spanMalformedMissingName" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "missing required span name" + operations: [] + expectTracingMessages: + client: *client0 + spans: + - tags: { db.system: mongodb } + diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.json new file mode 100644 index 0000000000..a09ca31c95 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.json @@ -0,0 +1,25 @@ +{ + "description": "expectedTracingSpans-spanMalformedMissingTags", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "missing required span tags", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "foo" + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.yml new file mode 100644 index 0000000000..5d82e5b360 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedMissingTags.yml @@ -0,0 +1,15 @@ +description: "expectedTracingSpans-spanMalformedMissingTags" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "missing required span tags" + operations: [] + expectTracingMessages: + client: *client0 + spans: + - name: "foo" diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.json new file mode 100644 index 0000000000..ccff04108d --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.json @@ -0,0 +1,27 @@ +{ + "description": "expectedTracingSpans-spanMalformedNestedMustBeArray", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "nested spans must be an array", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "foo", + "tags": {}, + "nested": {} + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.yml new file mode 100644 index 0000000000..c5eed9a491 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedNestedMustBeArray.yml @@ -0,0 +1,18 @@ +description: "expectedTracingSpans-spanMalformedNestedMustBeArray" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "nested spans must be an array" + operations: [] + expectTracingMessages: + client: *client0 + spans: + - name: "foo" + tags: {} + nested: {} + diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.json b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.json new file mode 100644 index 0000000000..72af1c29b1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.json @@ -0,0 +1,26 @@ +{ + "description": "expectedTracingSpans-spanMalformedNestedMustBeObject", + "schemaVersion": "1.26", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "span tags must be an object", + "operations": [], + "expectTracingMessages": { + "client": "client0", + "spans": [ + { + "name": "foo", + "tags": [] + } + ] + } + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.yml b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.yml new file mode 100644 index 0000000000..b34bdc2b82 --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedTracingSpans-spanMalformedTagsMustBeObject.yml @@ -0,0 +1,17 @@ +description: "expectedTracingSpans-spanMalformedNestedMustBeObject" + +schemaVersion: "1.26" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "span tags must be an object" + operations: [] + expectTracingMessages: + client: *client0 + spans: + - name: "foo" + tags: [] + diff --git a/source/unified-test-format/tests/invalid/initialData-items.json b/source/unified-test-format/tests/invalid/initialData-items.json new file mode 100644 index 0000000000..9c27d554f9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-items.json @@ -0,0 +1,13 @@ +{ + "description": "initialData-items", + "schemaVersion": "1.0", + "initialData": [ + 0 + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/initialData-items.yml b/source/unified-test-format/tests/invalid/initialData-items.yml new file mode 100644 index 0000000000..87fa3e562b --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-items.yml @@ -0,0 +1,9 @@ +description: "initialData-items" + +schemaVersion: "1.0" + +initialData: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/initialData-minItems.json b/source/unified-test-format/tests/invalid/initialData-minItems.json new file mode 100644 index 0000000000..984100a2be --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "initialData-minItems", + "schemaVersion": "1.0", + "initialData": [], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/initialData-minItems.yml b/source/unified-test-format/tests/invalid/initialData-minItems.yml new file mode 100644 index 0000000000..dc1afde97f --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-minItems.yml @@ -0,0 +1,9 @@ +description: "initialData-minItems" + +schemaVersion: "1.0" + +initialData: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/initialData-type.json b/source/unified-test-format/tests/invalid/initialData-type.json new file mode 100644 index 0000000000..c33585e03a --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-type.json @@ -0,0 +1,11 @@ +{ + "description": "initialData-type", + "schemaVersion": "1.0", + "initialData": 0, + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/initialData-type.yml b/source/unified-test-format/tests/invalid/initialData-type.yml new file mode 100644 index 0000000000..946c0e4364 --- /dev/null +++ b/source/unified-test-format/tests/invalid/initialData-type.yml @@ -0,0 +1,9 @@ +description: "initialData-type" + +schemaVersion: "1.0" + +initialData: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/operation-additionalProperties.json b/source/unified-test-format/tests/invalid/operation-additionalProperties.json new file mode 100644 index 0000000000..8f2f1434ec --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "operation-additionalProperties", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "foo": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-additionalProperties.yml b/source/unified-test-format/tests/invalid/operation-additionalProperties.yml new file mode 100644 index 0000000000..d5dbef8e8f --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-additionalProperties.yml @@ -0,0 +1,14 @@ +description: "operation-additionalProperties" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + foo: 0 diff --git a/source/unified-test-format/tests/invalid/operation-arguments-type.json b/source/unified-test-format/tests/invalid/operation-arguments-type.json new file mode 100644 index 0000000000..a22f3921c3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-arguments-type.json @@ -0,0 +1,23 @@ +{ + "description": "operation-arguments-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "arguments": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-arguments-type.yml b/source/unified-test-format/tests/invalid/operation-arguments-type.yml new file mode 100644 index 0000000000..1564f21601 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-arguments-type.yml @@ -0,0 +1,14 @@ +description: "operation-arguments-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + arguments: 0 diff --git a/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.json b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.json new file mode 100644 index 0000000000..bc15fbac76 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.json @@ -0,0 +1,26 @@ +{ + "description": "operation-expectError-conflicts_with_expectResult", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isError": true + }, + "expectResult": {} + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.yml b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.yml new file mode 100644 index 0000000000..aa9430fdb1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_expectResult.yml @@ -0,0 +1,16 @@ +description: "operation-expectError-conflicts_with_expectResult" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isError: true + expectResult: {} diff --git a/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.json b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.json new file mode 100644 index 0000000000..dead4a3b9d --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.json @@ -0,0 +1,26 @@ +{ + "description": "operation-expectError-conflicts_with_saveResultAsEntity", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": { + "isError": true + }, + "saveResultAsEntity": "foo" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.yml b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.yml new file mode 100644 index 0000000000..6c369598fe --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-conflicts_with_saveResultAsEntity.yml @@ -0,0 +1,16 @@ +description: "operation-expectError-conflicts_with_saveResultAsEntity" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: + isError: true + saveResultAsEntity: "foo" diff --git a/source/unified-test-format/tests/invalid/operation-expectError-type.json b/source/unified-test-format/tests/invalid/operation-expectError-type.json new file mode 100644 index 0000000000..b224ba3535 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-type.json @@ -0,0 +1,23 @@ +{ + "description": "operation-expectError-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectError": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-expectError-type.yml b/source/unified-test-format/tests/invalid/operation-expectError-type.yml new file mode 100644 index 0000000000..55588d4359 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectError-type.yml @@ -0,0 +1,14 @@ +description: "operation-expectError-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectError: 0 diff --git a/source/unified-test-format/tests/invalid/operation-expectEvents-type.json b/source/unified-test-format/tests/invalid/operation-expectEvents-type.json new file mode 100644 index 0000000000..ecd4c011a9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectEvents-type.json @@ -0,0 +1,23 @@ +{ + "description": "operation-expectEvents-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "expectEvents": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-expectEvents-type.yml b/source/unified-test-format/tests/invalid/operation-expectEvents-type.yml new file mode 100644 index 0000000000..2c9936ed28 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-expectEvents-type.yml @@ -0,0 +1,14 @@ +description: "operation-expectEvents-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + expectEvents: 0 diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.json b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.json new file mode 100644 index 0000000000..b47e6be2a1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.json @@ -0,0 +1,19 @@ +{ + "description": "operation-ignoreResultAndError-conflicts_with_expectError", + "schemaVersion": "1.3", + "tests": [ + { + "description": "ignoreResultAndError used with expectError", + "operations": [ + { + "name": "foo", + "object": "bar", + "ignoreResultAndError": true, + "expectError": { + "isError": true + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.yml b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.yml new file mode 100644 index 0000000000..75dd8340a0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectError.yml @@ -0,0 +1,12 @@ +description: operation-ignoreResultAndError-conflicts_with_expectError + +schemaVersion: '1.3' + +tests: + - description: ignoreResultAndError used with expectError + operations: + - name: foo + object: bar + ignoreResultAndError: true + expectError: + isError: true diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.json b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.json new file mode 100644 index 0000000000..03c5a1dbbc --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.json @@ -0,0 +1,17 @@ +{ + "description": "operation-ignoreResultAndError-conflicts_with_expectResult", + "schemaVersion": "1.3", + "tests": [ + { + "description": "ignoreResultAndError used with expectResult", + "operations": [ + { + "name": "foo", + "object": "bar", + "ignoreResultAndError": true, + "expectResult": 1 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.yml b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.yml new file mode 100644 index 0000000000..6c51c07b8e --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_expectResult.yml @@ -0,0 +1,11 @@ +description: operation-ignoreResultAndError-conflicts_with_expectResult + +schemaVersion: '1.3' + +tests: + - description: ignoreResultAndError used with expectResult + operations: + - name: foo + object: bar + ignoreResultAndError: true + expectResult: 1 diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.json b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.json new file mode 100644 index 0000000000..6745dff2eb --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.json @@ -0,0 +1,17 @@ +{ + "description": "operation-ignoreResultAndError-conflicts_with_saveResultAsEntity", + "schemaVersion": "1.3", + "tests": [ + { + "description": "ignoreResultAndError used with saveResultAsEntity", + "operations": [ + { + "name": "foo", + "object": "bar", + "ignoreResultAndError": true, + "saveResultAsEntity": "entity0" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.yml b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.yml new file mode 100644 index 0000000000..4f514352d0 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-ignoreResultAndError-conflicts_with_saveResultAsEntity.yml @@ -0,0 +1,11 @@ +description: operation-ignoreResultAndError-conflicts_with_saveResultAsEntity + +schemaVersion: '1.3' + +tests: + - description: ignoreResultAndError used with saveResultAsEntity + operations: + - name: foo + object: bar + ignoreResultAndError: true + saveResultAsEntity: entity0 diff --git a/source/unified-test-format/tests/invalid/operation-name-required.json b/source/unified-test-format/tests/invalid/operation-name-required.json new file mode 100644 index 0000000000..42fcb3a308 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-name-required.json @@ -0,0 +1,21 @@ +{ + "description": "operation-name-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "object": "client0" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-name-required.yml b/source/unified-test-format/tests/invalid/operation-name-required.yml new file mode 100644 index 0000000000..253cb579b9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-name-required.yml @@ -0,0 +1,12 @@ +description: "operation-name-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - object: *client0 diff --git a/source/unified-test-format/tests/invalid/operation-name-type.json b/source/unified-test-format/tests/invalid/operation-name-type.json new file mode 100644 index 0000000000..2f91da078a --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-name-type.json @@ -0,0 +1,22 @@ +{ + "description": "operation-name-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": 0, + "object": "client0" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-name-type.yml b/source/unified-test-format/tests/invalid/operation-name-type.yml new file mode 100644 index 0000000000..f9323050b5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-name-type.yml @@ -0,0 +1,13 @@ +description: "operation-name-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: 0 + object: *client0 diff --git a/source/unified-test-format/tests/invalid/operation-object-required.json b/source/unified-test-format/tests/invalid/operation-object-required.json new file mode 100644 index 0000000000..c0410ce3fd --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-object-required.json @@ -0,0 +1,21 @@ +{ + "description": "operation-object-required", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo" + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-object-required.yml b/source/unified-test-format/tests/invalid/operation-object-required.yml new file mode 100644 index 0000000000..eec6101bbe --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-object-required.yml @@ -0,0 +1,12 @@ +description: "operation-object-required" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" diff --git a/source/unified-test-format/tests/invalid/operation-object-type.json b/source/unified-test-format/tests/invalid/operation-object-type.json new file mode 100644 index 0000000000..edb0a0b51a --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-object-type.json @@ -0,0 +1,22 @@ +{ + "description": "operation-object-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-object-type.yml b/source/unified-test-format/tests/invalid/operation-object-type.yml new file mode 100644 index 0000000000..fb8e10267b --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-object-type.yml @@ -0,0 +1,13 @@ +description: "operation-object-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: 0 diff --git a/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.json b/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.json new file mode 100644 index 0000000000..65ead94c7a --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.json @@ -0,0 +1,23 @@ +{ + "description": "operation-saveResultAsEntity-type", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + { + "name": "foo", + "object": "client0", + "saveResultAsEntity": 0 + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.yml b/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.yml new file mode 100644 index 0000000000..7d20938f32 --- /dev/null +++ b/source/unified-test-format/tests/invalid/operation-saveResultAsEntity-type.yml @@ -0,0 +1,14 @@ +description: "operation-saveResultAsEntity-type" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 "client0" + +tests: + - description: "foo" + operations: + - name: "foo" + object: *client0 + saveResultAsEntity: 0 diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.json b/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.json new file mode 100644 index 0000000000..79fa687e45 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.json @@ -0,0 +1,16 @@ +{ + "description": "runOnRequirement-additionalProperties", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "foo": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.yml b/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.yml new file mode 100644 index 0000000000..044b803d29 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-additionalProperties.yml @@ -0,0 +1,11 @@ +description: "runOnRequirement-additionalProperties" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.0" + foo: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.json new file mode 100644 index 0000000000..e5475d079d --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-auth-type", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "auth": "foo" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.yml new file mode 100644 index 0000000000..1a8a547fbc --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-auth-type.yml @@ -0,0 +1,10 @@ +description: runOnRequirement-auth-type + +schemaVersion: '1.3' + +runOnRequirements: + - auth: foo + +tests: + - description: foo + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.json new file mode 100644 index 0000000000..007f3f304c --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-authMechanism-type", + "schemaVersion": "1.19", + "runOnRequirements": [ + { + "authMechanism": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.yml new file mode 100644 index 0000000000..a0e835612b --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-authMechanism-type.yml @@ -0,0 +1,10 @@ +description: runOnRequirement-authMechanism-type + +schemaVersion: '1.19' + +runOnRequirements: + - authMechanism: 0 + +tests: + - description: foo + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.json b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.json new file mode 100644 index 0000000000..1db023bf68 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.json @@ -0,0 +1,17 @@ +{ + "description": "runOnRequirement-csfle-minLibmongocryptVersion-pattern", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "csfle": { + "minLibmongocryptVersion": "1.2.3.4" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.yml b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.yml new file mode 100644 index 0000000000..a95e2c6ee7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-pattern.yml @@ -0,0 +1,11 @@ +description: "runOnRequirement-csfle-minLibmongocryptVersion-pattern" + +schemaVersion: "1.25" + +runOnRequirements: + - csfle: + minLibmongocryptVersion: "1.2.3.4" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.json new file mode 100644 index 0000000000..8de7b293f1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.json @@ -0,0 +1,17 @@ +{ + "description": "runOnRequirement-csfle-minLibmongocryptVersion-type", + "schemaVersion": "1.25", + "runOnRequirements": [ + { + "csfle": { + "minLibmongocryptVersion": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.yml new file mode 100644 index 0000000000..0230982ebe --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-minLibmongocryptVersion-type.yml @@ -0,0 +1,11 @@ +description: "runOnRequirement-csfle-minLibmongocryptVersion-type" + +schemaVersion: "1.25" + +runOnRequirements: + - csfle: + minLibmongocryptVersion: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.json new file mode 100644 index 0000000000..b48c850d14 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-csfle-type", + "schemaVersion": "1.8", + "runOnRequirements": [ + { + "csfle": "foo" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.yml new file mode 100644 index 0000000000..c82bba410d --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-csfle-type.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-csfle-type" + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: foo + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.json b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.json new file mode 100644 index 0000000000..78766eb925 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-maxServerVersion-pattern", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": "1.2.3.4" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.yml b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.yml new file mode 100644 index 0000000000..ce5af27df5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-pattern.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-maxServerVersion-pattern" + +schemaVersion: "1.0" + +runOnRequirements: + - maxServerVersion: "1.2.3.4" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.json new file mode 100644 index 0000000000..ffc9118ba2 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-maxServerVersion-type", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "maxServerVersion": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.yml new file mode 100644 index 0000000000..a63df28232 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-maxServerVersion-type.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-maxServerVersion-type" + +schemaVersion: "1.0" + +runOnRequirements: + - maxServerVersion: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.json b/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.json new file mode 100644 index 0000000000..c2bfed3be7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.json @@ -0,0 +1,13 @@ +{ + "description": "runOnRequirement-minProperties", + "schemaVersion": "1.0", + "runOnRequirements": [ + {} + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.yml b/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.yml new file mode 100644 index 0000000000..e8b91fd4f5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minProperties.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-minProperties" + +schemaVersion: "1.0" + +runOnRequirements: + - {} + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.json b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.json new file mode 100644 index 0000000000..19abc1755f --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-minServerVersion-pattern", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "1.2.3.4" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.yml b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.yml new file mode 100644 index 0000000000..69d4986665 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-pattern.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-minServerVersion-pattern" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "1.2.3.4" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.json new file mode 100644 index 0000000000..688d1c67ee --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-minServerVersion-type", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.yml new file mode 100644 index 0000000000..e2727c5800 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-minServerVersion-type.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-minServerVersion-type" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.json b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.json new file mode 100644 index 0000000000..031fa539df --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-serverless-enum", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "serverless": "foo" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.yml b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.yml new file mode 100644 index 0000000000..6db134500b --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-enum.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-serverless-enum" + +schemaVersion: "1.4" + +runOnRequirements: + - serverless: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.json new file mode 100644 index 0000000000..1aa41712f9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-serverless-type", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "serverless": 1234 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.yml new file mode 100644 index 0000000000..82a7ed31d5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-serverless-type.yml @@ -0,0 +1,10 @@ +description: runOnRequirement-serverless-type + +schemaVersion: '1.4' + +runOnRequirements: + - serverless: 1234 + +tests: + - description: foo + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.json b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.json new file mode 100644 index 0000000000..f62e5040d4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.json @@ -0,0 +1,17 @@ +{ + "description": "runOnRequirement-topologies-enum", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": [ + "foo" + ] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.yml b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.yml new file mode 100644 index 0000000000..924a67515d --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-enum.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-topologies-enum" + +schemaVersion: "1.0" + +runOnRequirements: + - topologies: ["foo"] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.json b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.json new file mode 100644 index 0000000000..a205b3293d --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.json @@ -0,0 +1,17 @@ +{ + "description": "runOnRequirement-topologies-items", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": [ + 0 + ] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.yml b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.yml new file mode 100644 index 0000000000..7b2265fb26 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-items.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-topologies-items" + +schemaVersion: "1.0" + +runOnRequirements: + - topologies: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.json b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.json new file mode 100644 index 0000000000..16f29b3f4b --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-topologies-minItems", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": [] + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.yml b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.yml new file mode 100644 index 0000000000..3e6d11ab8f --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-minItems.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-topologies-minItems" + +schemaVersion: "1.0" + +runOnRequirements: + - topologies: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.json new file mode 100644 index 0000000000..f6d147cd6f --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-topologies-type", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.yml new file mode 100644 index 0000000000..5a1652d8d9 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-topologies-type.yml @@ -0,0 +1,10 @@ +description: "runOnRequirement-topologies-type" + +schemaVersion: "1.0" + +runOnRequirements: + - topologies: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-items.json b/source/unified-test-format/tests/invalid/runOnRequirements-items.json new file mode 100644 index 0000000000..40ec84a3f3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-items.json @@ -0,0 +1,13 @@ +{ + "description": "runOnRequirements-items", + "schemaVersion": "1.0", + "runOnRequirements": [ + 0 + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-items.yml b/source/unified-test-format/tests/invalid/runOnRequirements-items.yml new file mode 100644 index 0000000000..a86bbb1318 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-items.yml @@ -0,0 +1,9 @@ +description: "runOnRequirements-items" + +schemaVersion: "1.0" + +runOnRequirements: [0] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-minItems.json b/source/unified-test-format/tests/invalid/runOnRequirements-minItems.json new file mode 100644 index 0000000000..4ca9f99b5d --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "runOnRequirements-minItems", + "schemaVersion": "1.0", + "runOnRequirements": [], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-minItems.yml b/source/unified-test-format/tests/invalid/runOnRequirements-minItems.yml new file mode 100644 index 0000000000..034786a220 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-minItems.yml @@ -0,0 +1,9 @@ +description: "runOnRequirements-minItems" + +schemaVersion: "1.0" + +runOnRequirements: [] + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-type.json b/source/unified-test-format/tests/invalid/runOnRequirements-type.json new file mode 100644 index 0000000000..98b859f3ea --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-type.json @@ -0,0 +1,11 @@ +{ + "description": "runOnRequirements-type", + "schemaVersion": "1.0", + "runOnRequirements": 0, + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirements-type.yml b/source/unified-test-format/tests/invalid/runOnRequirements-type.yml new file mode 100644 index 0000000000..42afc21ced --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirements-type.yml @@ -0,0 +1,9 @@ +description: "runOnRequirements-type" + +schemaVersion: "1.0" + +runOnRequirements: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/schemaVersion-pattern.json b/source/unified-test-format/tests/invalid/schemaVersion-pattern.json new file mode 100644 index 0000000000..bcb8980516 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-pattern.json @@ -0,0 +1,10 @@ +{ + "description": "schemaVersion-pattern", + "schemaVersion": "1.2.3.4", + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/schemaVersion-pattern.yml b/source/unified-test-format/tests/invalid/schemaVersion-pattern.yml new file mode 100644 index 0000000000..3d36ee64b5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-pattern.yml @@ -0,0 +1,7 @@ +description: "schemaVersion-pattern" + +schemaVersion: "1.2.3.4" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/schemaVersion-required.json b/source/unified-test-format/tests/invalid/schemaVersion-required.json new file mode 100644 index 0000000000..7388ff0bf1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-required.json @@ -0,0 +1,9 @@ +{ + "description": "schemaVersion-required", + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/schemaVersion-required.yml b/source/unified-test-format/tests/invalid/schemaVersion-required.yml new file mode 100644 index 0000000000..14a13872d8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-required.yml @@ -0,0 +1,5 @@ +description: "schemaVersion-required" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/schemaVersion-type.json b/source/unified-test-format/tests/invalid/schemaVersion-type.json new file mode 100644 index 0000000000..646473a209 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-type.json @@ -0,0 +1,10 @@ +{ + "description": "schemaVersion-type", + "schemaVersion": 0, + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/schemaVersion-type.yml b/source/unified-test-format/tests/invalid/schemaVersion-type.yml new file mode 100644 index 0000000000..9dfeed1e78 --- /dev/null +++ b/source/unified-test-format/tests/invalid/schemaVersion-type.yml @@ -0,0 +1,7 @@ +description: "schemaVersion-type" + +schemaVersion: 0 + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/invalid/test-additionalProperties.json b/source/unified-test-format/tests/invalid/test-additionalProperties.json new file mode 100644 index 0000000000..a699319c30 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-additionalProperties.json @@ -0,0 +1,11 @@ +{ + "description": "test-additionalProperties", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "foo": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-additionalProperties.yml b/source/unified-test-format/tests/invalid/test-additionalProperties.yml new file mode 100644 index 0000000000..65a2e2aa64 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-additionalProperties.yml @@ -0,0 +1,8 @@ +description: "test-additionalProperties" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + foo: 0 diff --git a/source/unified-test-format/tests/invalid/test-description-required.json b/source/unified-test-format/tests/invalid/test-description-required.json new file mode 100644 index 0000000000..8bf23014d4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-description-required.json @@ -0,0 +1,9 @@ +{ + "description": "test-description-required", + "schemaVersion": "1.0", + "tests": [ + { + "operation": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-description-required.yml b/source/unified-test-format/tests/invalid/test-description-required.yml new file mode 100644 index 0000000000..d1c725b69a --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-description-required.yml @@ -0,0 +1,6 @@ +description: "test-description-required" + +schemaVersion: "1.0" + +tests: + - operation: [] diff --git a/source/unified-test-format/tests/invalid/test-description-type.json b/source/unified-test-format/tests/invalid/test-description-type.json new file mode 100644 index 0000000000..bba3690449 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-description-type.json @@ -0,0 +1,10 @@ +{ + "description": "test-description-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": 0, + "operation": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-description-type.yml b/source/unified-test-format/tests/invalid/test-description-type.yml new file mode 100644 index 0000000000..891db9bbc1 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-description-type.yml @@ -0,0 +1,7 @@ +description: "test-description-type" + +schemaVersion: "1.0" + +tests: + - description: 0 + operation: [] diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-items.json b/source/unified-test-format/tests/invalid/test-expectEvents-items.json new file mode 100644 index 0000000000..394f74746c --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-items.json @@ -0,0 +1,13 @@ +{ + "description": "test-expectEvents-items", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + 0 + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-items.yml b/source/unified-test-format/tests/invalid/test-expectEvents-items.yml new file mode 100644 index 0000000000..1a889b1b4f --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-items.yml @@ -0,0 +1,8 @@ +description: "test-expectEvents-items" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + expectEvents: [0] diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-minItems.json b/source/unified-test-format/tests/invalid/test-expectEvents-minItems.json new file mode 100644 index 0000000000..0da3a56f79 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectEvents-minItems", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-minItems.yml b/source/unified-test-format/tests/invalid/test-expectEvents-minItems.yml new file mode 100644 index 0000000000..75a4297479 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-minItems.yml @@ -0,0 +1,8 @@ +description: "test-expectEvents-minItems" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + expectEvents: [] diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-type.json b/source/unified-test-format/tests/invalid/test-expectEvents-type.json new file mode 100644 index 0000000000..1569f0a0d7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectEvents-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectEvents-type.yml b/source/unified-test-format/tests/invalid/test-expectEvents-type.yml new file mode 100644 index 0000000000..0a1e9d97d5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectEvents-type.yml @@ -0,0 +1,8 @@ +description: "test-expectEvents-type" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + expectEvents: 0 diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-items.json b/source/unified-test-format/tests/invalid/test-expectLogMessages-items.json new file mode 100644 index 0000000000..be4a609c56 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-items.json @@ -0,0 +1,13 @@ +{ + "description": "test-expectLogMessages-items", + "schemaVersion": "1.13", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + 0 + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-items.yml b/source/unified-test-format/tests/invalid/test-expectLogMessages-items.yml new file mode 100644 index 0000000000..4555a3ecdc --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-items.yml @@ -0,0 +1,8 @@ +description: "test-expectLogMessages-items" + +schemaVersion: "1.13" + +tests: + - description: "foo" + operations: [] + expectLogMessages: [0] diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.json b/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.json new file mode 100644 index 0000000000..d7a07c2e77 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectLogMessages-minItems", + "schemaVersion": "1.11", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.yml b/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.yml new file mode 100644 index 0000000000..581957be06 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-minItems.yml @@ -0,0 +1,8 @@ +description: "test-expectLogMessages-minItems" + +schemaVersion: "1.11" + +tests: + - description: "foo" + operations: [] + expectLogMessages: [] diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-type.json b/source/unified-test-format/tests/invalid/test-expectLogMessages-type.json new file mode 100644 index 0000000000..9a8d6fcdfb --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectLogMessages-type", + "schemaVersion": "1.13", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-expectLogMessages-type.yml b/source/unified-test-format/tests/invalid/test-expectLogMessages-type.yml new file mode 100644 index 0000000000..190dd97bad --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-expectLogMessages-type.yml @@ -0,0 +1,8 @@ +description: "test-expectLogMessages-type" + +schemaVersion: "1.13" + +tests: + - description: "foo" + operations: [] + expectLogMessages: 0 diff --git a/source/unified-test-format/tests/invalid/test-operations-items.json b/source/unified-test-format/tests/invalid/test-operations-items.json new file mode 100644 index 0000000000..00af8e7453 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-items.json @@ -0,0 +1,12 @@ +{ + "description": "test-operations-items", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [ + 0 + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-operations-items.yml b/source/unified-test-format/tests/invalid/test-operations-items.yml new file mode 100644 index 0000000000..6bb097fa3c --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-items.yml @@ -0,0 +1,7 @@ +description: "test-operations-items" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [0] diff --git a/source/unified-test-format/tests/invalid/test-operations-required.json b/source/unified-test-format/tests/invalid/test-operations-required.json new file mode 100644 index 0000000000..67c6f83044 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-required.json @@ -0,0 +1,9 @@ +{ + "description": "test-operations-required", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo" + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-operations-required.yml b/source/unified-test-format/tests/invalid/test-operations-required.yml new file mode 100644 index 0000000000..80af1a900a --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-required.yml @@ -0,0 +1,6 @@ +description: "test-operations-required" + +schemaVersion: "1.0" + +tests: + - description: "foo" diff --git a/source/unified-test-format/tests/invalid/test-operations-type.json b/source/unified-test-format/tests/invalid/test-operations-type.json new file mode 100644 index 0000000000..1e8b5b2496 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-type.json @@ -0,0 +1,10 @@ +{ + "description": "test-operations-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-operations-type.yml b/source/unified-test-format/tests/invalid/test-operations-type.yml new file mode 100644 index 0000000000..713c7ff0ec --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-operations-type.yml @@ -0,0 +1,7 @@ +description: "test-operations-type" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: 0 diff --git a/source/unified-test-format/tests/invalid/test-outcome-items.json b/source/unified-test-format/tests/invalid/test-outcome-items.json new file mode 100644 index 0000000000..cf6bb54f87 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-items.json @@ -0,0 +1,13 @@ +{ + "description": "test-outcome-items", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "outcome": [ + 0 + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-outcome-items.yml b/source/unified-test-format/tests/invalid/test-outcome-items.yml new file mode 100644 index 0000000000..bd5565e4fb --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-items.yml @@ -0,0 +1,8 @@ +description: "test-outcome-items" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + outcome: [0] diff --git a/source/unified-test-format/tests/invalid/test-outcome-minItems.json b/source/unified-test-format/tests/invalid/test-outcome-minItems.json new file mode 100644 index 0000000000..aadf8e514a --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "test-outcome-minItems", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "outcome": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-outcome-minItems.yml b/source/unified-test-format/tests/invalid/test-outcome-minItems.yml new file mode 100644 index 0000000000..f8d87d1f56 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-minItems.yml @@ -0,0 +1,8 @@ +description: "test-outcome-minItems" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + outcome: [] diff --git a/source/unified-test-format/tests/invalid/test-outcome-type.json b/source/unified-test-format/tests/invalid/test-outcome-type.json new file mode 100644 index 0000000000..e60c119d7e --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-outcome-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "outcome": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-outcome-type.yml b/source/unified-test-format/tests/invalid/test-outcome-type.yml new file mode 100644 index 0000000000..cfc44183a5 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-outcome-type.yml @@ -0,0 +1,8 @@ +description: "test-outcome-type" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + outcome: 0 diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-items.json b/source/unified-test-format/tests/invalid/test-runOnRequirements-items.json new file mode 100644 index 0000000000..866bebb51f --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-items.json @@ -0,0 +1,13 @@ +{ + "description": "test-runOnRequirements-items", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "runOnRequirements": [ + 0 + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-items.yml b/source/unified-test-format/tests/invalid/test-runOnRequirements-items.yml new file mode 100644 index 0000000000..2324fa8045 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-items.yml @@ -0,0 +1,8 @@ +description: "test-runOnRequirements-items" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + runOnRequirements: [0] diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.json b/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.json new file mode 100644 index 0000000000..d61f063849 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "test-runOnRequirements-minItems", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "runOnRequirements": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.yml b/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.yml new file mode 100644 index 0000000000..2f3e0391ad --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-minItems.yml @@ -0,0 +1,8 @@ +description: "test-runOnRequirements-minItems" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + runOnRequirements: [] diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-type.json b/source/unified-test-format/tests/invalid/test-runOnRequirements-type.json new file mode 100644 index 0000000000..5b25b1005d --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-runOnRequirements-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "runOnRequirements": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-runOnRequirements-type.yml b/source/unified-test-format/tests/invalid/test-runOnRequirements-type.yml new file mode 100644 index 0000000000..12bd90e859 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-runOnRequirements-type.yml @@ -0,0 +1,8 @@ +description: "test-runOnRequirements-type" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + runOnRequirements: 0 diff --git a/source/unified-test-format/tests/invalid/test-skipReason-type.json b/source/unified-test-format/tests/invalid/test-skipReason-type.json new file mode 100644 index 0000000000..0408e76834 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-skipReason-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-skipReason-type", + "schemaVersion": "1.0", + "tests": [ + { + "description": "foo", + "operations": [], + "skipReason": 0 + } + ] +} diff --git a/source/unified-test-format/tests/invalid/test-skipReason-type.yml b/source/unified-test-format/tests/invalid/test-skipReason-type.yml new file mode 100644 index 0000000000..7921aefc79 --- /dev/null +++ b/source/unified-test-format/tests/invalid/test-skipReason-type.yml @@ -0,0 +1,8 @@ +description: "test-skipReason-type" + +schemaVersion: "1.0" + +tests: + - description: "foo" + operations: [] + skipReason: 0 diff --git a/source/unified-test-format/tests/invalid/tests-items.json b/source/unified-test-format/tests/invalid/tests-items.json new file mode 100644 index 0000000000..11f37469e4 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-items.json @@ -0,0 +1,7 @@ +{ + "description": "tests-items", + "schemaVersion": "1.0", + "tests": [ + 0 + ] +} diff --git a/source/unified-test-format/tests/invalid/tests-items.yml b/source/unified-test-format/tests/invalid/tests-items.yml new file mode 100644 index 0000000000..8c9829634c --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-items.yml @@ -0,0 +1,5 @@ +description: "tests-items" + +schemaVersion: "1.0" + +tests: [0] diff --git a/source/unified-test-format/tests/invalid/tests-minItems.json b/source/unified-test-format/tests/invalid/tests-minItems.json new file mode 100644 index 0000000000..3f74f94af7 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-minItems.json @@ -0,0 +1,5 @@ +{ + "description": "tests-minItems", + "schemaVersion": "1.0", + "tests": [] +} diff --git a/source/unified-test-format/tests/invalid/tests-minItems.yml b/source/unified-test-format/tests/invalid/tests-minItems.yml new file mode 100644 index 0000000000..834120f835 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-minItems.yml @@ -0,0 +1,5 @@ +description: "tests-minItems" + +schemaVersion: "1.0" + +tests: [] diff --git a/source/unified-test-format/tests/invalid/tests-required.json b/source/unified-test-format/tests/invalid/tests-required.json new file mode 100644 index 0000000000..de4b2fd063 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-required.json @@ -0,0 +1,4 @@ +{ + "description": "tests-required", + "schemaVersion": "1.0" +} diff --git a/source/unified-test-format/tests/invalid/tests-required.yml b/source/unified-test-format/tests/invalid/tests-required.yml new file mode 100644 index 0000000000..41c5072acf --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-required.yml @@ -0,0 +1,3 @@ +description: "tests-required" + +schemaVersion: "1.0" diff --git a/source/unified-test-format/tests/invalid/tests-type.json b/source/unified-test-format/tests/invalid/tests-type.json new file mode 100644 index 0000000000..62d8194a41 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-type.json @@ -0,0 +1,5 @@ +{ + "description": "tests-type", + "schemaVersion": "1.0", + "tests": 0 +} diff --git a/source/unified-test-format/tests/invalid/tests-type.yml b/source/unified-test-format/tests/invalid/tests-type.yml new file mode 100644 index 0000000000..c16781a727 --- /dev/null +++ b/source/unified-test-format/tests/invalid/tests-type.yml @@ -0,0 +1,5 @@ +description: "tests-type" + +schemaVersion: "1.0" + +tests: 0 diff --git a/source/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json b/source/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.json new file mode 100644 index 0000000000..9799bb2f65 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml b/source/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml new file mode 100644 index 0000000000..8a8aa63667 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/assertNumberConnectionsCheckedOut.yml @@ -0,0 +1,38 @@ +description: assertNumberConnectionsCheckedOut + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 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/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json b/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json new file mode 100644 index 0000000000..7f7f1978c3 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.json @@ -0,0 +1,18 @@ +{ + "description": "entity-bucket-database-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "bucket": { + "id": "bucket0", + "database": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml b/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml new file mode 100644 index 0000000000..7aeda8e1ac --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-bucket-database-undefined.yml @@ -0,0 +1,12 @@ +description: "entity-bucket-database-undefined" + +schemaVersion: "1.0" + +createEntities: + - bucket: + id: &bucket0 "bucket0" + database: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json b/source/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.json new file mode 100644 index 0000000000..d92d23dcaf --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml b/source/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml new file mode 100644 index 0000000000..7c626a308b --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-client-apiVersion-unsupported.yml @@ -0,0 +1,13 @@ +description: "entity-client-apiVersion-unsupported" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 client0 + serverApi: + version: "server_will_never_support_this_api_version" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json b/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json new file mode 100644 index 0000000000..20b0733e34 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.json @@ -0,0 +1,19 @@ +{ + "description": "entity-collection-database-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "collection": { + "id": "collection0", + "database": "foo", + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml b/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml new file mode 100644 index 0000000000..a66b6ed292 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-collection-database-undefined.yml @@ -0,0 +1,13 @@ +description: "entity-collection-database-undefined" + +schemaVersion: "1.0" + +createEntities: + - collection: + id: &collection0 "collection0" + database: "foo" + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.json b/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.json new file mode 100644 index 0000000000..0f8110e6d3 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.json @@ -0,0 +1,19 @@ +{ + "description": "entity-database-client-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "database": { + "id": "database0", + "client": "foo", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml b/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml new file mode 100644 index 0000000000..34b58cece5 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-database-client-undefined.yml @@ -0,0 +1,13 @@ +description: "entity-database-client-undefined" + +schemaVersion: "1.0" + +createEntities: + - database: + id: &database0 "database0" + client: "foo" + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json b/source/unified-test-format/tests/valid-fail/entity-findCursor-malformed.json new file mode 100644 index 0000000000..0956efa4c8 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml b/source/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml new file mode 100644 index 0000000000..d7cf4856de --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-findCursor-malformed.yml @@ -0,0 +1,31 @@ +# This test is split out into a separate file to accommodate drivers that validate operation structure while decoding +# from JSON/YML. Such drivers fail to decode any files containing invalid operations. Combining this test in a file +# with other entity-findCursor valid-fail tests, which test failures that occur during test execution, would prevent +# such drivers from decoding the file and running any of the tests. +description: entity-findCursor-malformed + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: createFindCursor fails if filter is not specified + operations: + - name: createFindCursor + object: *collection0 + saveResultAsEntity: &cursor0 cursor0 diff --git a/source/unified-test-format/tests/valid-fail/entity-findCursor.json b/source/unified-test-format/tests/valid-fail/entity-findCursor.json new file mode 100644 index 0000000000..389e448c06 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/entity-findCursor.yml b/source/unified-test-format/tests/valid-fail/entity-findCursor.yml new file mode 100644 index 0000000000..d796c132d2 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-findCursor.yml @@ -0,0 +1,31 @@ +description: entity-findCursor + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + 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/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.json b/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.json new file mode 100644 index 0000000000..260356436a --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.json @@ -0,0 +1,18 @@ +{ + "description": "entity-session-client-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "session": { + "id": "session0", + "client": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml b/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml new file mode 100644 index 0000000000..16eaea46b7 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/entity-session-client-undefined.yml @@ -0,0 +1,12 @@ +description: "entity-session-client-undefined" + +schemaVersion: "1.0" + +createEntities: + - session: + id: &session0 "session0" + client: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json b/source/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.json new file mode 100644 index 0000000000..b64779c723 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml b/source/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml new file mode 100644 index 0000000000..4822bbe625 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/ignoreResultAndError-malformed.yml @@ -0,0 +1,34 @@ +# This test is split out into a separate file to accommodate drivers that validate operation structure while decoding +# from JSON/YML. Such drivers fail to decode any files containing invalid operations. Combining this test in a file +# with other ignoreResultAndError valid-fail tests, which test failures that occur during test execution, would prevent +# such drivers from decoding the file and running any of the tests. +description: ignoreResultAndError-malformed + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + 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/source/unified-test-format/tests/valid-fail/ignoreResultAndError.json b/source/unified-test-format/tests/valid-fail/ignoreResultAndError.json new file mode 100644 index 0000000000..01b2421a9f --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/ignoreResultAndError.yml b/source/unified-test-format/tests/valid-fail/ignoreResultAndError.yml new file mode 100644 index 0000000000..802c601f92 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/ignoreResultAndError.yml @@ -0,0 +1,35 @@ +description: ignoreResultAndError + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: operation errors are not ignored if ignoreResultAndError is false + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument { _id: 1 } + - name: insertOne + object: *collection0 + arguments: + # Insert the same document to force a DuplicateKey error. + document: *insertDocument + ignoreResultAndError: false diff --git a/source/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.json new file mode 100644 index 0000000000..e62de80033 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml new file mode 100644 index 0000000000..6418679bbf --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_aws_kms_credentials.yml @@ -0,0 +1,22 @@ +# Missing required KMS credentials should lead to clientEncryption initialization failure. +description: kmsProviders-missing_aws_kms_credentials + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + aws: { accessKeyId: "accessKeyId" } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.json new file mode 100644 index 0000000000..8ef805d0fa --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml new file mode 100644 index 0000000000..4a8afaf10a --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_azure_kms_credentials.yml @@ -0,0 +1,22 @@ +# Missing required KMS credentials should lead to clientEncryption initialization failure. +description: kmsProviders-missing_azure_kms_credentials + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + azure: { tenantId: "tenantId" } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json new file mode 100644 index 0000000000..c6da1ce58c --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml new file mode 100644 index 0000000000..f722b6b058 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/kmsProviders-missing_gcp_kms_credentials.yml @@ -0,0 +1,22 @@ +# Missing required KMS credentials should lead to clientEncryption initialization failure. +description: kmsProviders-missing_gcp_kms_credentials + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + gcp: { email: "email" } + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json b/source/unified-test-format/tests/valid-fail/kmsProviders-no_kms.json new file mode 100644 index 0000000000..57499b4eaf --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml b/source/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml new file mode 100644 index 0000000000..d9e196c0c2 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/kmsProviders-no_kms.yml @@ -0,0 +1,21 @@ +# No configured KMS providers should lead to clientEncryption initialization failure. +description: clientEncryptionOpts-no_kms + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: {} + +tests: + - description: "" + operations: [] diff --git a/source/unified-test-format/tests/valid-fail/operation-failure.json b/source/unified-test-format/tests/valid-fail/operation-failure.json new file mode 100644 index 0000000000..8f6cae1521 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/operation-failure.yml b/source/unified-test-format/tests/valid-fail/operation-failure.yml new file mode 100644 index 0000000000..b80f7794df --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operation-failure.yml @@ -0,0 +1,31 @@ +description: "operation-failure" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-failure + - collection: + id: &collection0 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/source/unified-test-format/tests/valid-fail/operation-unsupported.json b/source/unified-test-format/tests/valid-fail/operation-unsupported.json new file mode 100644 index 0000000000..d8ef5ab1c8 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-fail/operation-unsupported.yml b/source/unified-test-format/tests/valid-fail/operation-unsupported.yml new file mode 100644 index 0000000000..ba311637c0 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operation-unsupported.yml @@ -0,0 +1,13 @@ +description: "operation-unsupported" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + +tests: + - description: "Unsupported operation" + operations: + - name: unsupportedOperation + object: *client0 diff --git a/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.json b/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.json new file mode 100644 index 0000000000..24f6be9cb8 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.json @@ -0,0 +1,205 @@ +{ + "description": "operator-matchAsDocument", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "json": "{ \"x\": 1, \"y\": 2 }" + }, + { + "_id": 2, + "json": "1" + }, + { + "_id": 3, + "json": "[ \"foo\" ]" + }, + { + "_id": 4, + "json": "{ \"x\" }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsDocument with non-matching filter", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": "two" + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": { + "$$exists": false + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument does not permit extra fields", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1 + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument expects JSON object but given scalar", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument expects JSON object but given array", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 3 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 3, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument fails to decode Extended JSON", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 4 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 4, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": {} + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.yml b/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.yml new file mode 100644 index 0000000000..ca5de0056f --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operator-matchAsDocument.yml @@ -0,0 +1,88 @@ +description: operator-matchAsDocument + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, json: '{ "x": 1, "y": 2 }' } + # Documents with non-objects or invalid JSON + - { _id: 2, json: '1' } + - { _id: 3, json: '[ "foo" ]' } + - { _id: 4, json: '{ "x" }' } + +tests: + - description: matchAsDocument with non-matching filter + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, json: { $$matchAsDocument: { x: 1, y: "two" } } } + - + description: matchAsDocument evaluates special operators + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, json: { $$matchAsDocument: { x: 1, y: { $$exists: false } } } } + - + description: matchAsDocument does not permit extra fields + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, json: { $$matchAsDocument: { x: 1 } } } + - + description: matchAsDocument expects JSON object but given scalar + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 2 } + limit: 1 + expectResult: + # The following $$matchAsRoot expression would match any document, so + # this ensures the failure is due to the actual value. + - { _id: 2, json: &match_any_document { $$matchAsDocument: { $$matchAsRoot: { } } } } + - + description: matchAsDocument expects JSON object but given array + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 3 } + limit: 1 + expectResult: + - { _id: 3, json: *match_any_document } + - + description: matchAsDocument fails to decode Extended JSON + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 4 } + limit: 1 + expectResult: + - { _id: 4, json: *match_any_document } diff --git a/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.json b/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.json new file mode 100644 index 0000000000..ec6309418c --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.json @@ -0,0 +1,67 @@ +{ + "description": "operator-matchAsRoot", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "x": { + "y": 2, + "z": 3 + } + } + ] + } + ], + "tests": [ + { + "description": "matchAsRoot with nested document does not match", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 3 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.yml b/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.yml new file mode 100644 index 0000000000..77e3ce5790 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/operator-matchAsRoot.yml @@ -0,0 +1,33 @@ +description: operator-matchAsRoot + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: { y: 2, z: 3 } } + +tests: + - + description: matchAsRoot with nested document does not match + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, x: { $$matchAsRoot: { y: 3 } } } diff --git a/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json b/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json new file mode 100644 index 0000000000..ea425fb568 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.json @@ -0,0 +1,66 @@ +{ + "description": "returnDocument-enum-invalid", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "tests": [ + { + "description": "FindOneAndReplace returnDocument invalid enum value", + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "invalid" + } + } + ] + }, + { + "description": "FindOneAndUpdate returnDocument invalid enum value", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "invalid" + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml b/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml new file mode 100644 index 0000000000..b877f5e965 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/returnDocument-enum-invalid.yml @@ -0,0 +1,34 @@ +description: "returnDocument-enum-invalid" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + +tests: + - description: "FindOneAndReplace returnDocument invalid enum value" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: invalid + + - description: "FindOneAndUpdate returnDocument invalid enum value" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x : 1 }} + returnDocument: invalid diff --git a/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json b/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json new file mode 100644 index 0000000000..ceb5532917 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.json @@ -0,0 +1,10 @@ +{ + "description": "schemaVersion-unsupported", + "schemaVersion": "0.1", + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml b/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml new file mode 100644 index 0000000000..0cb6994d24 --- /dev/null +++ b/source/unified-test-format/tests/valid-fail/schemaVersion-unsupported.yml @@ -0,0 +1,7 @@ +description: "schemaVersion-unsupported" + +schemaVersion: "0.1" + +tests: + - description: "foo" + operations: [] diff --git a/source/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json b/source/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.json new file mode 100644 index 0000000000..a9fc063f33 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml b/source/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml new file mode 100644 index 0000000000..603552fc20 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/assertNumberConnectionsCheckedOut.yml @@ -0,0 +1,17 @@ +description: assertNumberConnectionsCheckedOut + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + +tests: + - description: basic assertion succeeds + operations: + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 0 diff --git a/source/unified-test-format/tests/valid-pass/collectionData-createOptions.json b/source/unified-test-format/tests/valid-pass/collectionData-createOptions.json new file mode 100644 index 0000000000..19edc2247b --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/collectionData-createOptions.yml b/source/unified-test-format/tests/valid-pass/collectionData-createOptions.yml new file mode 100644 index 0000000000..c6afedcfa9 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/collectionData-createOptions.yml @@ -0,0 +1,37 @@ +description: collectionData-createOptions +schemaVersion: "1.9" +runOnRequirements: + - minServerVersion: "3.6" + # Capped collections cannot be created on serverless instances. + serverless: forbid +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + createOptions: + capped: true + # With MMAPv1, the size field cannot be less than 4096. + size: &cappedSize 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: *cappedSize } \ No newline at end of file diff --git a/source/unified-test-format/tests/valid-pass/createEntities-operation.json b/source/unified-test-format/tests/valid-pass/createEntities-operation.json new file mode 100644 index 0000000000..3fde42919d --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/createEntities-operation.yml b/source/unified-test-format/tests/valid-pass/createEntities-operation.yml new file mode 100644 index 0000000000..ee8acd7368 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/createEntities-operation.yml @@ -0,0 +1,38 @@ +description: createEntities-operation + +# Note: createEntities is not technically in the 1.9 schema but was introduced at the same time. +schemaVersion: "1.9" + +tests: + - description: createEntities operation + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client1 client1 + observeEvents: [ commandStartedEvent ] + - database: + id: &database1 database1 + client: *client1 + databaseName: &database1Name database1 + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: &collection1Name coll1 + - name: deleteOne + object: *collection1 + arguments: + filter: { _id : 1 } + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + delete: *collection1Name + deletes: + - q: { _id: 1 } + limit: 1 + commandName: delete + databaseName: *database1Name diff --git a/source/unified-test-format/tests/valid-pass/entity-client-cmap-events.json b/source/unified-test-format/tests/valid-pass/entity-client-cmap-events.json new file mode 100644 index 0000000000..3209033def --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml b/source/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml new file mode 100644 index 0000000000..277134551c --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/entity-client-cmap-events.yml @@ -0,0 +1,40 @@ +description: entity-client-cmap-events + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - connectionReadyEvent + - connectionCheckedOutEvent + - connectionCheckedInEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + 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/source/unified-test-format/tests/valid-pass/entity-commandCursor.json b/source/unified-test-format/tests/valid-pass/entity-commandCursor.json new file mode 100644 index 0000000000..72b74b4a9a --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/entity-commandCursor.json @@ -0,0 +1,278 @@ +{ + "description": "entity-commandCursor", + "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" + } + } + ], + "initialData": [ + { + "collectionName": "collection", + "databaseName": "db", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + } + ] + } + ], + "tests": [ + { + "description": "runCursorCommand creates and exhausts cursor by running getMores", + "operations": [ + { + "name": "runCursorCommand", + "object": "db", + "arguments": { + "commandName": "find", + "batchSize": 2, + "command": { + "find": "collection", + "filter": {}, + "batchSize": 2 + } + }, + "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": "collection", + "filter": {}, + "batchSize": 2, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection", + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "getMore" + } + } + ] + } + ] + }, + { + "description": "createCommandCursor creates a cursor and stores it as an entity that can be iterated one document at a time", + "operations": [ + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "find", + "batchSize": 2, + "command": { + "find": "collection", + "filter": {}, + "batchSize": 2 + } + }, + "saveResultAsEntity": "myRunCommandCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 2, + "x": 22 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 3, + "x": 33 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 4, + "x": 44 + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 5, + "x": 55 + } + } + ] + }, + { + "description": "createCommandCursor's cursor can be closed and will perform a killCursors operation", + "operations": [ + { + "name": "createCommandCursor", + "object": "db", + "arguments": { + "commandName": "find", + "batchSize": 2, + "command": { + "find": "collection", + "filter": {}, + "batchSize": 2 + } + }, + "saveResultAsEntity": "myRunCommandCursor" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "myRunCommandCursor", + "expectResult": { + "_id": 1, + "x": 11 + } + }, + { + "name": "close", + "object": "myRunCommandCursor" + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "collection", + "filter": {}, + "batchSize": 2, + "$db": "db", + "lsid": { + "$$exists": true + } + }, + "commandName": "find" + } + }, + { + "commandStartedEvent": { + "command": { + "killCursors": "collection", + "cursors": { + "$$type": "array" + } + }, + "commandName": "killCursors" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/entity-commandCursor.yml b/source/unified-test-format/tests/valid-pass/entity-commandCursor.yml new file mode 100644 index 0000000000..3becf2095a --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/entity-commandCursor.yml @@ -0,0 +1,115 @@ +description: entity-commandCursor +schemaVersion: '1.3' +createEntities: + - client: + id: &client client + useMultipleMongoses: false + observeEvents: [commandStartedEvent] + - database: + id: &db db + client: *client + databaseName: *db + - collection: + id: &collection collection + database: *db + collectionName: *collection +initialData: + - collectionName: collection + databaseName: *db + documents: &documents + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } +tests: + - description: runCursorCommand creates and exhausts cursor by running getMores + operations: + - name: runCursorCommand + object: *db + arguments: + commandName: find + batchSize: 2 + command: { find: *collection, filter: {}, batchSize: 2 } + expectResult: *documents + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + find: *collection + filter: {} + batchSize: 2 + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$exists: true } + commandName: getMore + - commandStartedEvent: + command: + getMore: { $$type: [int, long] } + collection: *collection + $db: *db + lsid: { $$exists: true } + commandName: getMore + + - description: createCommandCursor creates a cursor and stores it as an entity that can be iterated one document at a time + operations: + - name: createCommandCursor + object: *db + arguments: + commandName: find + batchSize: 2 + command: { find: *collection, filter: {}, batchSize: 2 } + saveResultAsEntity: &myRunCommandCursor myRunCommandCursor + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 1, x: 11 } + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 2, x: 22 } + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 3, x: 33 } + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 4, x: 44 } + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 5, x: 55 } + + - description: createCommandCursor's cursor can be closed and will perform a killCursors operation + operations: + - name: createCommandCursor + object: *db + arguments: + commandName: find + batchSize: 2 + command: { find: *collection, filter: {}, batchSize: 2 } + saveResultAsEntity: myRunCommandCursor + - name: iterateUntilDocumentOrError + object: *myRunCommandCursor + expectResult: { _id: 1, x: 11 } + - name: close + object: *myRunCommandCursor + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + find: *collection + filter: {} + batchSize: 2 + $db: *db + lsid: { $$exists: true } + commandName: find + - commandStartedEvent: + command: + killCursors: *collection + cursors: { $$type: array } + commandName: killCursors diff --git a/source/unified-test-format/tests/valid-pass/entity-cursor-iterateOnce.json b/source/unified-test-format/tests/valid-pass/entity-cursor-iterateOnce.json new file mode 100644 index 0000000000..b17ae78b94 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/entity-cursor-iterateOnce.yml b/source/unified-test-format/tests/valid-pass/entity-cursor-iterateOnce.yml new file mode 100644 index 0000000000..508e594a53 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/entity-cursor-iterateOnce.yml @@ -0,0 +1,59 @@ +description: entity-cursor-iterateOnce + +# Note: iterateOnce is not technically in the 1.9 schema but was introduced at the same time. +schemaVersion: "1.9" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0 + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: 1 + - _id: 2 + - _id: 3 + +tests: + - description: iterateOnce + operations: + - name: createFindCursor + object: *collection0 + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: &cursor0 cursor0 + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: { _id: 1 } + - name: iterateUntilDocumentOrError + object: *cursor0 + expectResult: { _id: 2 } + # This operation could be iterateUntilDocumentOrError, but we use iterateOne to ensure that drivers support it. + - name: iterateOnce + object: *cursor0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: {} + batchSize: 2 + commandName: find + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + commandName: getMore diff --git a/source/unified-test-format/tests/valid-pass/entity-find-cursor.json b/source/unified-test-format/tests/valid-pass/entity-find-cursor.json new file mode 100644 index 0000000000..6f955d81f4 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/entity-find-cursor.yml b/source/unified-test-format/tests/valid-pass/entity-find-cursor.yml new file mode 100644 index 0000000000..3ecdf6da1d --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/entity-find-cursor.yml @@ -0,0 +1,89 @@ +description: entity-find-cursor + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + 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 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: *collection0Name + 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: *collection0Name + commandName: getMore + - commandSucceededEvent: + reply: + cursor: + id: { $$type: [ int, long ] } + ns: { $$type: string } + nextBatch: { $$type: array } + commandName: getMore + - commandStartedEvent: + command: + killCursors: *collection0Name + cursors: { $$type: array } + commandName: killCursors + - commandSucceededEvent: + reply: + # No cursorsKilled field expected on pre-3.2 servers + cursorsKilled: { $$unsetOrMatches: { $$type: array } } + commandName: killCursors diff --git a/source/unified-test-format/tests/valid-pass/expectedError-errorResponse.json b/source/unified-test-format/tests/valid-pass/expectedError-errorResponse.json new file mode 100644 index 0000000000..177b1baf56 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/expectedError-errorResponse.yml b/source/unified-test-format/tests/valid-pass/expectedError-errorResponse.yml new file mode 100644 index 0000000000..e10c25a1ed --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedError-errorResponse.yml @@ -0,0 +1,39 @@ +description: "expectedError-errorResponse" + +schemaVersion: "1.12" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +tests: + - description: "Unsupported command" + operations: + - name: runCommand + object: *database0 + arguments: + commandName: unsupportedCommand + command: { unsupportedCommand: 1 } + expectError: + # Avoid asserting the exact error since it may vary by server version + errorResponse: + errmsg: { $$type: "string" } + + - description: "Unsupported query operator" + operations: + - name: find + object: *collection0 + arguments: + filter: { $unsupportedQueryOperator: 1 } + expectError: + # Avoid asserting the exact error since it may vary by server version + errorResponse: + errmsg: { $$type: "string" } diff --git a/source/unified-test-format/tests/valid-pass/expectedError-isClientError.json b/source/unified-test-format/tests/valid-pass/expectedError-isClientError.json new file mode 100644 index 0000000000..9c6beda588 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedError-isClientError.json @@ -0,0 +1,74 @@ +{ + "description": "expectedError-isClientError", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded", + "load-balanced" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + } + ], + "tests": [ + { + "description": "isClientError considers network errors", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "ping" + ], + "closeConnection": true + } + } + } + }, + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/expectedError-isClientError.yml b/source/unified-test-format/tests/valid-pass/expectedError-isClientError.yml new file mode 100644 index 0000000000..3bc12e73f9 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedError-isClientError.yml @@ -0,0 +1,39 @@ +description: "expectedError-isClientError" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [single, replicaset] + - minServerVersion: "4.1.7" + topologies: [sharded, load-balanced] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + +tests: + - description: "isClientError considers network errors" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ ping ] + closeConnection: true + - name: runCommand + object: *database0 + arguments: + commandName: ping + command: { ping: 1 } + expectError: + isClientError: true diff --git a/source/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.json new file mode 100644 index 0000000000..fe308df965 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml new file mode 100644 index 0000000000..e437c80489 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-eventType.yml @@ -0,0 +1,66 @@ +description: expectedEventsForClient-eventType + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - connectionReadyEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: eventType can be set to command and cmap + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument { _id: 1 } + expectEvents: + - client: *client0 + eventType: command + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *insertDocument + commandName: insert + - client: *client0 + eventType: cmap + events: + - connectionReadyEvent: {} + + - description: eventType defaults to command if unset + operations: + - name: insertOne + object: *collection0 + arguments: + document: *insertDocument + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *insertDocument + commandName: insert + - client: *client0 + eventType: cmap + events: + - connectionReadyEvent: {} diff --git a/source/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json new file mode 100644 index 0000000000..178b756c2c --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml new file mode 100644 index 0000000000..d6d87094f6 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-ignoreExtraEvents.yml @@ -0,0 +1,78 @@ +description: expectedEventsForClient-ignoreExtraEvents + +schemaVersion: '1.7' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: ignoreExtraEvents can be set to false + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument1 { _id: 1 } + expectEvents: + - client: *client0 + ignoreExtraEvents: false + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *insertDocument1 + commandName: insert + + - description: ignoreExtraEvents can be set to true + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument2 { _id: 2 } + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3 } + expectEvents: + - client: *client0 + ignoreExtraEvents: true + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *insertDocument2 + commandName: insert + + - description: ignoreExtraEvents defaults to false if unset + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument4 { _id: 4 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *insertDocument4 + commandName: insert \ No newline at end of file diff --git a/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json new file mode 100644 index 0000000000..cf7bd60826 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json @@ -0,0 +1,68 @@ +{ + "description": "expectedEventsForClient-topologyDescriptionChangedEvent", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "can assert on values of newDescription and previousDescription fields", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "directConnection": true + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Single" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.yml b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.yml new file mode 100644 index 0000000000..c8dacc391e --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.yml @@ -0,0 +1,40 @@ +description: "expectedEventsForClient-topologyDescriptionChangedEvent" + +schemaVersion: "1.20" + +runOnRequirements: + - topologies: + - replicaset + minServerVersion: "4.4" # awaitable hello + +tests: + - description: "can assert on values of newDescription and previousDescription fields" + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client client + uriOptions: + directConnection: true + observeEvents: + - topologyDescriptionChangedEvent + - name: waitForEvent + object: testRunner + arguments: + client: *client + event: + topologyDescriptionChangedEvent: {} + count: 1 + expectEvents: + - client: *client + eventType: sdam + ignoreExtraEvents: true + events: + - topologyDescriptionChangedEvent: + previousDescription: + type: "Unknown" + newDescription: + type: "Single" + diff --git a/source/unified-test-format/tests/valid-pass/ignoreResultAndError.json b/source/unified-test-format/tests/valid-pass/ignoreResultAndError.json new file mode 100644 index 0000000000..2e9b1c58ab --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/ignoreResultAndError.yml b/source/unified-test-format/tests/valid-pass/ignoreResultAndError.yml new file mode 100644 index 0000000000..847e296e72 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/ignoreResultAndError.yml @@ -0,0 +1,34 @@ +description: ignoreResultAndError + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: operation errors are ignored if ignoreResultAndError is true + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument { _id: 1 } + - name: insertOne + object: *collection0 + arguments: + document: *insertDocument + ignoreResultAndError: true diff --git a/source/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json b/source/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.json new file mode 100644 index 0000000000..7cc74939eb --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml b/source/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml new file mode 100644 index 0000000000..b5f87b53a0 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/kmsProviders-explicit_kms_credentials.yml @@ -0,0 +1,26 @@ +# kmsProviders should support explicit credentials. +description: kmsProviders-explicit_kms_credentials + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 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/source/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json b/source/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.json new file mode 100644 index 0000000000..363f2a4576 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml b/source/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml new file mode 100644 index 0000000000..cf6753b5b9 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/kmsProviders-mixed_kms_credential_fields.yml @@ -0,0 +1,24 @@ +# kmsProviders should support both explicit and placeholder credential fields. +description: kmsProviders-mixed_kms_credential_fields + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 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/source/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json b/source/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.json new file mode 100644 index 0000000000..3f7721f01d --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml b/source/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml new file mode 100644 index 0000000000..642187971d --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/kmsProviders-placeholder_kms_credentials.yml @@ -0,0 +1,26 @@ +# kmsProviders should support loading credentials at runtime. +description: kmsProviders-placeholder_kms_credentials + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 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/source/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json b/source/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.json new file mode 100644 index 0000000000..12ca580941 --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml b/source/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml new file mode 100644 index 0000000000..3e6c878186 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/kmsProviders-unconfigured_kms.yml @@ -0,0 +1,27 @@ +# kmsProviders should support request for on-demand KMS credentials. +description: kmsProviders-unconfigured_kms + +schemaVersion: "1.8" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + - clientEncryption: + id: &clientEncryption0 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/source/unified-test-format/tests/valid-pass/observeSensitiveCommands.json b/source/unified-test-format/tests/valid-pass/observeSensitiveCommands.json new file mode 100644 index 0000000000..d3ae5665be --- /dev/null +++ b/source/unified-test-format/tests/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/source/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml b/source/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml new file mode 100644 index 0000000000..cd4d27a165 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/observeSensitiveCommands.yml @@ -0,0 +1,255 @@ +description: "observeSensitiveCommands" + +schemaVersion: "1.5" + +runOnRequirements: + - auth: false + +createEntities: + - client: + id: &clientObserveSensitiveCommands client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + observeSensitiveCommands: true + - client: + id: &clientDoNotObserveSensitiveCommands client1 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + observeSensitiveCommands: false + - client: + id: &clientDoNotObserveSensitiveCommandsByDefault client2 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: &databaseObserveSensitiveCommands database0 + client: *clientObserveSensitiveCommands + databaseName: &databaseName observeSensitiveCommands + - database: + id: &databaseDoNotObserveSensitiveCommands database1 + client: *clientDoNotObserveSensitiveCommands + databaseName: *databaseName + - database: + id: &databaseDoNotObserveSensitiveCommandsByDefault database2 + client: *clientDoNotObserveSensitiveCommandsByDefault + databaseName: *databaseName + +tests: + - description: "getnonce is observed with observeSensitiveCommands=true" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: + commandName: getnonce + command: { getnonce: 1 } + expectEvents: + - client: *clientObserveSensitiveCommands + 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 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: + commandName: getnonce + command: { getnonce: 1 } + expectEvents: + - client: *clientDoNotObserveSensitiveCommands + events: [] + + - description: "getnonce is not observed by default" + runOnRequirements: + - maxServerVersion: 6.1.99 # getnonce removed as of 6.2 via SERVER-71007 + operations: + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: + commandName: getnonce + command: { getnonce: 1 } + expectEvents: + - client: *clientDoNotObserveSensitiveCommandsByDefault + events: [] + + - description: "hello with speculativeAuthenticate" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &helloArgs + commandName: hello + command: + hello: 1 + speculativeAuthenticate: { saslStart: 1 } + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *helloArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *helloArgs + expectEvents: + - client: *clientObserveSensitiveCommands + events: + - commandStartedEvent: + commandName: hello + command: + # Assert that all fields in command are redacted + hello: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - commandSucceededEvent: + commandName: hello + reply: + # Assert that all fields in reply are redacted + isWritablePrimary: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - client: *clientDoNotObserveSensitiveCommands + events: [] + - client: *clientDoNotObserveSensitiveCommandsByDefault + events: [] + + - description: "hello without speculativeAuthenticate is always observed" + runOnRequirements: + - minServerVersion: "4.9" + operations: + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &helloArgs + commandName: hello + command: { hello: 1 } + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *helloArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *helloArgs + expectEvents: + - client: *clientObserveSensitiveCommands + events: &helloEvents + - commandStartedEvent: + commandName: hello + command: { hello: 1 } + - commandSucceededEvent: + commandName: hello + reply: { isWritablePrimary: { $$exists: true } } + - client: *clientDoNotObserveSensitiveCommands + events: *helloEvents + - client: *clientDoNotObserveSensitiveCommandsByDefault + events: *helloEvents + + - description: "legacy hello with speculativeAuthenticate" + operations: + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &ismasterArgs + commandName: ismaster + command: + ismaster: 1 + speculativeAuthenticate: { saslStart: 1 } + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &isMasterArgs + commandName: isMaster + command: + isMaster: 1 + speculativeAuthenticate: { saslStart: 1 } + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *ismasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *isMasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *ismasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *isMasterArgs + expectEvents: + - client: *clientObserveSensitiveCommands + events: + - commandStartedEvent: + commandName: ismaster + command: + # Assert that all fields in command are redacted + ismaster: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - commandSucceededEvent: + commandName: ismaster + reply: + # Assert that all fields in reply are redacted + ismaster: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - commandStartedEvent: + commandName: isMaster + command: + # Assert that all fields in command are redacted + isMaster: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - commandSucceededEvent: + commandName: isMaster + reply: + # Assert that all fields in reply are redacted + ismaster: { $$exists: false } + speculativeAuthenticate: { $$exists: false } + - client: *clientDoNotObserveSensitiveCommands + events: [] + - client: *clientDoNotObserveSensitiveCommandsByDefault + events: [] + + - description: "legacy hello without speculativeAuthenticate is always observed" + operations: + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &ismasterArgs + commandName: ismaster + command: { ismaster: 1 } + - name: runCommand + object: *databaseObserveSensitiveCommands + arguments: &isMasterArgs + commandName: isMaster + command: { isMaster: 1 } + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *ismasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommands + arguments: *isMasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *ismasterArgs + - name: runCommand + object: *databaseDoNotObserveSensitiveCommandsByDefault + arguments: *isMasterArgs + expectEvents: + - client: *clientObserveSensitiveCommands + events: &ismasterAndisMasterEvents + - 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: *clientDoNotObserveSensitiveCommands + events: *ismasterAndisMasterEvents + - client: *clientDoNotObserveSensitiveCommandsByDefault + events: *ismasterAndisMasterEvents diff --git a/source/unified-test-format/tests/valid-pass/operation-empty_array.json b/source/unified-test-format/tests/valid-pass/operation-empty_array.json new file mode 100644 index 0000000000..93b25c983c --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operation-empty_array.json @@ -0,0 +1,10 @@ +{ + "description": "operation-empty_array", + "schemaVersion": "1.0", + "tests": [ + { + "description": "Empty operations array", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/operation-empty_array.yml b/source/unified-test-format/tests/valid-pass/operation-empty_array.yml new file mode 100644 index 0000000000..35d5ba3711 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operation-empty_array.yml @@ -0,0 +1,7 @@ +description: "operation-empty_array" + +schemaVersion: "1.0" + +tests: + - description: "Empty operations array" + operations: [] diff --git a/source/unified-test-format/tests/valid-pass/operator-lte.json b/source/unified-test-format/tests/valid-pass/operator-lte.json new file mode 100644 index 0000000000..7a6a8057ad --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-lte.json @@ -0,0 +1,88 @@ +{ + "description": "operator-lte", + "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, + "x": 2, + "y": 3, + "z": 4 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": { + "$$lte": 2 + }, + "x": { + "$$lte": 2.1 + }, + "y": { + "$$lte": { + "$numberLong": "3" + } + }, + "z": { + "$$lte": 4 + } + } + ] + }, + "commandName": "insert", + "databaseName": "database0Name" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/operator-lte.yml b/source/unified-test-format/tests/valid-pass/operator-lte.yml new file mode 100644 index 0000000000..87c4ece928 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-lte.yml @@ -0,0 +1,41 @@ +description: operator-lte + +# Note: $$lte was introduced alongside schema changes for CSOT +schemaVersion: "1.9" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: special lte matching operator + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id : 1, x: 2, y: 3, z: 4 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + # We can make exact assertions here but we use the $$lte operator to ensure drivers support it. + - { _id: { $$lte: 2 }, x: { $$lte: 2.1 }, y: { $$lte: { $numberLong: "3"} }, z: { $$lte: 4 } } + commandName: insert + databaseName: *database0Name diff --git a/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.json b/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.json new file mode 100644 index 0000000000..fd8b514d4a --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.json @@ -0,0 +1,124 @@ +{ + "description": "operator-matchAsDocument", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "json": "{ \"x\": 1, \"y\": 2.0 }" + }, + { + "_id": 2, + "json": "{ \"x\": { \"$oid\": \"57e193d7a9cc81b4027498b5\" } }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsDocument performs flexible numeric comparisons", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "json": { + "$$matchAsDocument": { + "x": 1, + "y": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsDocument decodes Extended JSON", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "x": { + "$$type": "objectId" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.yml b/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.yml new file mode 100644 index 0000000000..9a811faaa9 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-matchAsDocument.yml @@ -0,0 +1,54 @@ +description: operator-matchAsDocument + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, json: '{ "x": 1, "y": 2.0 }' } + - { _id: 2, json: '{ "x": { "$oid": "57e193d7a9cc81b4027498b5" } }' } + +tests: + - + description: matchAsDocument performs flexible numeric comparisons + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, json: { $$matchAsDocument: { x: 1.0, y: 2 } } } + - + description: matchAsDocument evaluates special operators + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, json: { $$matchAsDocument: { x: 1, y: { $$exists: true } } } } + - + description: matchAsDocument decodes Extended JSON + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 2 } + limit: 1 + expectResult: + - { _id: 2, json: { $$matchAsDocument: { x: { $$type: "objectId" } } } } diff --git a/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.json b/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.json new file mode 100644 index 0000000000..1966e3b377 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.json @@ -0,0 +1,151 @@ +{ + "description": "operator-matchAsRoot", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [ + { + "_id": 1, + "x": { + "y": 2, + "z": 3 + } + }, + { + "_id": 2, + "json": "{ \"x\": 1, \"y\": 2 }" + } + ] + } + ], + "tests": [ + { + "description": "matchAsRoot with nested document", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot performs flexible numeric comparisons", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2 + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot evaluates special operators", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$matchAsRoot": { + "y": 2, + "z": { + "$$exists": true + } + } + } + } + ] + } + ] + }, + { + "description": "matchAsRoot with matchAsDocument", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 2 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 2, + "json": { + "$$matchAsDocument": { + "$$matchAsRoot": { + "x": 1 + } + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.yml b/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.yml new file mode 100644 index 0000000000..bbf738f04a --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-matchAsRoot.yml @@ -0,0 +1,64 @@ +description: operator-matchAsRoot + +schemaVersion: "1.13" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: { y: 2, z: 3 } } + - { _id: 2, json: '{ "x": 1, "y": 2 }' } + +tests: + - + description: matchAsRoot with nested document + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, x: { $$matchAsRoot: { y: 2 } } } + - + description: matchAsRoot performs flexible numeric comparisons + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, x: { $$matchAsRoot: { y: 2.0 } } } + - + description: matchAsRoot evaluates special operators + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 1 } + limit: 1 + expectResult: + - { _id: 1, x: { $$matchAsRoot: { y: 2, z: { $$exists: true } } } } + - + description: matchAsRoot with matchAsDocument + operations: + - name: find + object: *collection0 + arguments: + filter: { _id : 2 } + limit: 1 + expectResult: + - { _id: 2, json: { $$matchAsDocument: { $$matchAsRoot: { x: 1 } } } } diff --git a/source/unified-test-format/tests/valid-pass/operator-type-number_alias.json b/source/unified-test-format/tests/valid-pass/operator-type-number_alias.json new file mode 100644 index 0000000000..e628d0d777 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-type-number_alias.json @@ -0,0 +1,174 @@ +{ + "description": "operator-type-number_alias", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "test", + "documents": [] + } + ], + "tests": [ + { + "description": "type number alias matches int32", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": { + "$numberInt": "2147483647" + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$type": "number" + } + } + ] + } + ] + }, + { + "description": "type number alias matches int64", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": { + "$numberLong": "9223372036854775807" + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$type": "number" + } + } + ] + } + ] + }, + { + "description": "type number alias matches double", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": { + "$numberDouble": "2.71828" + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$type": "number" + } + } + ] + } + ] + }, + { + "description": "type number alias matches decimal128", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "x": { + "$numberDecimal": "3.14159" + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": 1, + "x": { + "$$type": "number" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/operator-type-number_alias.yml b/source/unified-test-format/tests/valid-pass/operator-type-number_alias.yml new file mode 100644 index 0000000000..04357a0242 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/operator-type-number_alias.yml @@ -0,0 +1,61 @@ +description: operator-type-number_alias + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - + description: type number alias matches int32 + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, x: { $numberInt: "2147483647" } } + - &find + name: find + object: *collection0 + arguments: + filter: { _id: 1 } + limit: 1 + expectResult: + - { _id: 1, x: { $$type: "number" } } + - + description: type number alias matches int64 + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, x: { $numberLong: "9223372036854775807" } } + - *find + - + description: type number alias matches double + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, x: { $numberDouble: "2.71828" } } + - *find + - + description: type number alias matches decimal128 + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 1, x: { $numberDecimal: "3.14159" } } + - *find diff --git a/source/unified-test-format/tests/valid-pass/poc-change-streams.json b/source/unified-test-format/tests/valid-pass/poc-change-streams.json new file mode 100644 index 0000000000..50f0d06f08 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-change-streams.json @@ -0,0 +1,455 @@ +{ + "description": "poc-change-streams", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ], + "ignoreCommandMonitoringEvents": [ + "getMore", + "killCursors" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "change-stream-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "client": { + "id": "client1", + "useMultipleMongoses": false + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "change-stream-tests" + } + }, + { + "database": { + "id": "database2", + "client": "client1", + "databaseName": "change-stream-tests-2" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "collection": { + "id": "collection2", + "database": "database1", + "collectionName": "test2" + } + }, + { + "collection": { + "id": "collection3", + "database": "database2", + "collectionName": "test" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "change-stream-tests", + "documents": [] + }, + { + "collectionName": "test2", + "databaseName": "change-stream-tests", + "documents": [] + }, + { + "collectionName": "test", + "databaseName": "change-stream-tests-2", + "documents": [] + } + ], + "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": [ + { + "minServerVersion": "3.8.0", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "client0", + "arguments": { + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection2", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection3", + "arguments": { + "document": { + "y": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "z": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests", + "coll": "test2" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests-2", + "coll": "test" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "y": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "z": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": 1, + "cursor": {}, + "pipeline": [ + { + "$changeStream": { + "allChangesForCluster": true, + "fullDocument": { + "$$unsetOrMatches": "default" + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "Test consecutive resume", + "runOnRequirements": [ + { + "minServerVersion": "4.1.7", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "batchSize": 1, + "pipeline": [] + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "x": 2 + } + } + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "x": 3 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "x": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "x": 2 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0", + "expectResult": { + "operationType": "insert", + "ns": { + "db": "change-stream-tests", + "coll": "test" + }, + "fullDocument": { + "_id": { + "$$type": "objectId" + }, + "x": 3 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "cursor": { + "batchSize": 1 + }, + "pipeline": [ + { + "$changeStream": { + "fullDocument": { + "$$unsetOrMatches": "default" + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "change-stream-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "cursor": { + "batchSize": 1 + }, + "pipeline": [ + { + "$changeStream": { + "fullDocument": { + "$$unsetOrMatches": "default" + }, + "resumeAfter": { + "$$exists": true + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "change-stream-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "test", + "cursor": { + "batchSize": 1 + }, + "pipeline": [ + { + "$changeStream": { + "fullDocument": { + "$$unsetOrMatches": "default" + }, + "resumeAfter": { + "$$exists": true + } + } + } + ] + }, + "commandName": "aggregate", + "databaseName": "change-stream-tests" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-change-streams.yml b/source/unified-test-format/tests/valid-pass/poc-change-streams.yml new file mode 100644 index 0000000000..a7daafceb7 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-change-streams.yml @@ -0,0 +1,241 @@ +description: "poc-change-streams" + +schemaVersion: "1.4" + +runOnRequirements: + - serverless: forbid + +createEntities: + # Entities for creating changeStreams + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + # Original tests do not observe getMore commands but only because event + # assertions ignore extra events. killCursors is explicitly ignored. + ignoreCommandMonitoringEvents: [ getMore, killCursors ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name change-stream-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + # Entities for executing insert operations + - client: + id: &client1 client1 + useMultipleMongoses: false + - database: + id: &database1 database1 + client: *client1 + databaseName: &database1Name change-stream-tests + - database: + id: &database2 database2 + client: *client1 + databaseName: &database2Name change-stream-tests-2 + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: &collection1Name test + - collection: + id: &collection2 collection2 + database: *database1 + collectionName: &collection2Name test2 + - collection: + id: &collection3 collection3 + database: *database2 + collectionName: &collection3Name test + +initialData: + - collectionName: *collection1Name + databaseName: *database1Name + documents: [] + - collectionName: *collection2Name + databaseName: *database1Name + documents: [] + - collectionName: *collection3Name + databaseName: *database2Name + documents: [] + +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: + - minServerVersion: "3.8.0" + topologies: [ replicaset ] + operations: + - name: createChangeStream + object: *client0 + arguments: + pipeline: [] + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *collection2 + arguments: + document: { x: 1 } + - name: insertOne + object: *collection3 + arguments: + document: { y: 1 } + - name: insertOne + object: *collection1 + arguments: + document: { z: 1 } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database1Name + coll: *collection2Name + fullDocument: + _id: { $$type: objectId } + x: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database2Name + coll: *collection3Name + fullDocument: + # Original tests did not include _id, but matching now only permits + # extra keys for root-level documents. + _id: { $$type: objectId } + y: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database1Name + coll: *collection1Name + fullDocument: + _id: { $$type: objectId } + z: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: 1 + cursor: {} + pipeline: + - $changeStream: + allChangesForCluster: true + # Some drivers may send a default value for fullDocument + # or omit it entirely (see: SPEC-1350). + fullDocument: { $$unsetOrMatches: default } + commandName: aggregate + databaseName: admin + + - description: "Test consecutive resume" + runOnRequirements: + - minServerVersion: "4.1.7" + topologies: [ replicaset ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ getMore ] + closeConnection: true + - name: createChangeStream + object: *collection0 + arguments: + batchSize: 1 + pipeline: [] + saveResultAsEntity: *changeStream0 + - name: insertOne + object: *collection1 + arguments: + document: { x: 1 } + - name: insertOne + object: *collection1 + arguments: + document: { x: 2 } + - name: insertOne + object: *collection1 + arguments: + document: { x: 3 } + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database1Name + coll: *collection1Name + fullDocument: + _id: { $$type: objectId } + x: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database1Name + coll: *collection1Name + fullDocument: + _id: { $$type: objectId } + x: 2 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectResult: + operationType: insert + ns: + db: *database1Name + coll: *collection1Name + fullDocument: + _id: { $$type: objectId } + x: 3 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection1Name + cursor: { batchSize: 1 } + pipeline: + - $changeStream: + fullDocument: { $$unsetOrMatches: default } + commandName: aggregate + databaseName: *database1Name + # The original test only asserted the first command, since expected + # events were only an ordered subset. This file does ignore getMore + # commands but we must expect the subsequent aggregate commands, since + # each failed getMore will resume. While doing so we can also assert + # that those commands include a resume token. + - &resumingAggregate + commandStartedEvent: + command: + aggregate: *collection1Name + cursor: { batchSize: 1 } + pipeline: + - $changeStream: + fullDocument: { $$unsetOrMatches: default } + resumeAfter: { $$exists: true } + commandName: aggregate + databaseName: *database0Name + - *resumingAggregate diff --git a/source/unified-test-format/tests/valid-pass/poc-command-monitoring.json b/source/unified-test-format/tests/valid-pass/poc-command-monitoring.json new file mode 100644 index 0000000000..fe0a5ae991 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-command-monitoring.json @@ -0,0 +1,223 @@ +{ + "description": "poc-command-monitoring", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "command-monitoring-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "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 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": "collection0", + "arguments": { + "filter": { + "_id": { + "$gte": 1 + } + }, + "sort": { + "_id": 1 + }, + "batchSize": 3, + "limit": 4 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "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": "collection0", + "arguments": { + "filter": { + "$or": true + } + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "test", + "filter": { + "$or": true + } + }, + "commandName": "find", + "databaseName": "command-monitoring-tests" + } + }, + { + "commandFailedEvent": { + "commandName": "find" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-command-monitoring.yml b/source/unified-test-format/tests/valid-pass/poc-command-monitoring.yml new file mode 100644 index 0000000000..6d5e437349 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-command-monitoring.yml @@ -0,0 +1,102 @@ +description: "poc-command-monitoring" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - commandFailedEvent + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name command-monitoring-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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 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: *collection0 + arguments: + filter: { _id: { $gte: 1 }} + sort: { _id: 1 } + batchSize: 3 + limit: 4 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gte : 1 } } + sort: { _id: 1 } + batchSize: 3 + limit: 4 + commandName: find + databaseName: *database0Name + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: { $$type: [ int, long ] } + ns: &namespace 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: *collection0Name + batchSize: 1 + commandName: getMore + databaseName: *database0Name + - commandSucceededEvent: + reply: + ok: 1 + cursor: + id: 0 + ns: *namespace + nextBatch: + - { _id: 4, x: 44 } + commandName: getMore + + - description: "A failed find event" + operations: + - name: find + object: *collection0 + arguments: + filter: { $or: true } + expectError: { isError: true } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { $or: true } + commandName: find + databaseName: *database0Name + - commandFailedEvent: + commandName: find diff --git a/source/unified-test-format/tests/valid-pass/poc-crud.json b/source/unified-test-format/tests/valid-pass/poc-crud.json new file mode 100644 index 0000000000..94e4ec5682 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-crud.json @@ -0,0 +1,450 @@ +{ + "description": "poc-crud", + "schemaVersion": "1.4", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "database": { + "id": "database1", + "client": "client0", + "databaseName": "admin" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + }, + { + "collection": { + "id": "collection1", + "database": "database0", + "collectionName": "coll1" + } + }, + { + "collection": { + "id": "collection2", + "database": "database0", + "collectionName": "coll2", + "collectionOptions": { + "readConcern": { + "level": "majority" + } + } + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + }, + { + "collectionName": "coll1", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + }, + { + "collectionName": "coll2", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + }, + { + "collectionName": "aggregate_out", + "databaseName": "crud-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "BulkWrite with mixed ordered operations", + "operations": [ + { + "name": "bulkWrite", + "object": "collection0", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 3, + "x": 33 + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "updateMany": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "update": { + "$inc": { + "x": 1 + } + } + } + }, + { + "insertOne": { + "document": { + "_id": 4, + "x": 44 + } + } + }, + { + "deleteMany": { + "filter": { + "x": { + "$nin": [ + 24, + 34 + ] + } + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 4 + }, + "replacement": { + "_id": 4, + "x": 44 + }, + "upsert": true + } + } + ], + "ordered": true + }, + "expectResult": { + "deletedCount": 2, + "insertedCount": 2, + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "3": 4 + } + }, + "matchedCount": 3, + "modifiedCount": 3, + "upsertedCount": 1, + "upsertedIds": { + "5": 4 + } + } + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "x": 24 + }, + { + "_id": 3, + "x": 34 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "InsertMany continue-on-error behavior with unordered (duplicate key in requests)", + "operations": [ + { + "name": "insertMany", + "object": "collection1", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ], + "ordered": false + }, + "expectError": { + "expectResult": { + "$$unsetOrMatches": { + "deletedCount": 0, + "insertedCount": 2, + "matchedCount": 0, + "modifiedCount": 0, + "upsertedCount": 0, + "upsertedIds": {} + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "ReplaceOne prohibits atomic modifiers", + "operations": [ + { + "name": "replaceOne", + "object": "collection1", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "$set": { + "x": 22 + } + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [] + } + ], + "outcome": [ + { + "collectionName": "coll1", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + }, + { + "description": "readConcern majority with out stage", + "runOnRequirements": [ + { + "minServerVersion": "4.1.0", + "topologies": [ + "replicaset", + "sharded" + ], + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection2", + "arguments": { + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "aggregate_out" + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll2", + "pipeline": [ + { + "$sort": { + "x": 1 + } + }, + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$out": "aggregate_out" + } + ], + "readConcern": { + "level": "majority" + } + }, + "commandName": "aggregate", + "databaseName": "crud-tests" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "aggregate_out", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + }, + { + "description": "Aggregate with $listLocalSessions", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "database1", + "arguments": { + "pipeline": [ + { + "$listLocalSessions": {} + }, + { + "$limit": 1 + }, + { + "$addFields": { + "dummy": "dummy field" + } + }, + { + "$project": { + "_id": 0, + "dummy": 1 + } + } + ] + }, + "expectResult": [ + { + "dummy": "dummy field" + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-crud.yml b/source/unified-test-format/tests/valid-pass/poc-crud.yml new file mode 100644 index 0000000000..5748c0779f --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-crud.yml @@ -0,0 +1,190 @@ +description: "poc-crud" + +schemaVersion: "1.4" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - database: + id: &database1 database1 + client: *client0 + databaseName: &database1Name admin + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + - collection: + id: &collection1 collection1 + database: *database0 + collectionName: &collection1Name coll1 + - collection: + id: &collection2 collection2 + database: *database0 + collectionName: &collection2Name coll2 + collectionOptions: + readConcern: { level: majority } + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - collectionName: *collection1Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - collectionName: *collection2Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - collectionName: &out aggregate_out + databaseName: *database0Name + documents: [] + +tests: + - description: "BulkWrite with mixed ordered operations" + operations: + - name: bulkWrite + object: *collection0 + arguments: + requests: + - insertOne: + document: { _id: 3, x: 33 } + - updateOne: + filter: { _id: 2 } + update: { $inc: { x: 1 } } + - updateMany: + filter: { _id: { $gt: 1 } } + update: { $inc: { x: 1 } } + - insertOne: + document: { _id: 4, x: 44 } + - deleteMany: + filter: { x: { $nin: [ 24, 34 ] } } + - replaceOne: + filter: { _id: 4 } + replacement: { _id: 4, x: 44 } + upsert: true + ordered: true + expectResult: + deletedCount: 2 + insertedCount: 2 + insertedIds: { $$unsetOrMatches: { 0: 3, 3: 4 } } + matchedCount: 3 + modifiedCount: 3 + upsertedCount: 1 + upsertedIds: { 5: 4 } + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - {_id: 2, x: 24 } + - {_id: 3, x: 34 } + - {_id: 4, x: 44 } + + - description: "InsertMany continue-on-error behavior with unordered (duplicate key in requests)" + operations: + - name: insertMany + object: *collection1 + arguments: + documents: + - { _id: 2, x: 22 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + ordered: false + expectError: + expectResult: + # insertMany throws BulkWriteException, which may optionally include + # an intermediary BulkWriteResult + $$unsetOrMatches: + deletedCount: 0 + insertedCount: 2 + # Since the map of insertedIds is generated before execution it + # could indicate inserts that did not actually succeed. We omit + # this field rather than expect drivers to provide an accurate + # map filtered by write errors. + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } + outcome: + - collectionName: *collection1Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + + - description: "ReplaceOne prohibits atomic modifiers" + operations: + - name: replaceOne + object: *collection1 + arguments: + filter: { _id: 1 } + replacement: { $set: { x: 22 }} + expectError: + isClientError: true + expectEvents: + - client: *client0 + events: [] + outcome: + - collectionName: *collection1Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + + - description: "readConcern majority with out stage" + runOnRequirements: + - minServerVersion: "4.1.0" + topologies: [ replicaset, sharded ] + serverless: "forbid" + operations: + - name: aggregate + object: *collection2 + arguments: + pipeline: &pipeline + - $sort: { x : 1 } + - $match: { _id: { $gt: 1 } } + - $out: *out + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection2Name + pipeline: *pipeline + readConcern: { level: majority } + # The following two assertions were not in the original test + commandName: aggregate + databaseName: *database0Name + outcome: + - collectionName: *out + databaseName: *database0Name + documents: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + + - description: "Aggregate with $listLocalSessions" + runOnRequirements: + - minServerVersion: "3.6.0" + # serverless does not support either of the current database-level aggregation stages ($listLocalSessions and + # $currentOp) + serverless: forbid + operations: + - name: aggregate + object: *database1 + arguments: + pipeline: + - $listLocalSessions: { } + - $limit: 1 + - $addFields: { dummy: "dummy field"} + - $project: { _id: 0, dummy: 1} + expectResult: + - { dummy: "dummy field" } diff --git a/source/unified-test-format/tests/valid-pass/poc-gridfs.json b/source/unified-test-format/tests/valid-pass/poc-gridfs.json new file mode 100644 index 0000000000..1f07a19bf6 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-gridfs.json @@ -0,0 +1,301 @@ +{ + "description": "poc-gridfs", + "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": "000000000000000000000005" + }, + "length": 10, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "md5": "57d83cd477bfb1ccd975ab33d827a92b", + "filename": "length-10", + "contentType": "application/octet-stream", + "aliases": [], + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_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" + } + } + } + ] + } + ], + "tests": [ + { + "description": "Delete when length is 10", + "operations": [ + { + "name": "delete", + "object": "bucket0", + "arguments": { + "id": { + "$oid": "000000000000000000000005" + } + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [] + } + ] + }, + { + "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": "Upload when length is 5", + "operations": [ + { + "name": "upload", + "object": "bucket0", + "arguments": { + "filename": "filename", + "source": { + "$$hexBytes": "1122334455" + }, + "chunkSizeBytes": 4 + }, + "expectResult": { + "$$type": "objectId" + }, + "saveResultAsEntity": "oid0" + }, + { + "name": "find", + "object": "bucket0_files_collection", + "arguments": { + "filter": {}, + "sort": { + "uploadDate": -1 + }, + "limit": 1 + }, + "expectResult": [ + { + "_id": { + "$$matchesEntity": "oid0" + }, + "length": 5, + "chunkSize": 4, + "uploadDate": { + "$$type": "date" + }, + "md5": { + "$$unsetOrMatches": "283d4fea5dded59cf837d3047328f5af" + }, + "filename": "filename" + } + ] + }, + { + "name": "find", + "object": "bucket0_chunks_collection", + "arguments": { + "filter": { + "_id": { + "$gt": { + "$oid": "000000000000000000000007" + } + } + }, + "sort": { + "n": 1 + } + }, + "expectResult": [ + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "oid0" + }, + "n": 0, + "data": { + "$binary": { + "base64": "ESIzRA==", + "subType": "00" + } + } + }, + { + "_id": { + "$$type": "objectId" + }, + "files_id": { + "$$matchesEntity": "oid0" + }, + "n": 1, + "data": { + "$binary": { + "base64": "VQ==", + "subType": "00" + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-gridfs.yml b/source/unified-test-format/tests/valid-pass/poc-gridfs.yml new file mode 100644 index 0000000000..9f60537420 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-gridfs.yml @@ -0,0 +1,155 @@ +description: "poc-gridfs" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - _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: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - _id: { $oid: "000000000000000000000005" } + files_id: { $oid: "000000000000000000000005" } + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex: 11223344 + - _id: { $oid: "000000000000000000000006" } + files_id: { $oid: "000000000000000000000005" } + n: 1 + data: { $binary: { base64: "VWZ3iA==", subType: "00" } } # hex: 55667788 + - _id: { $oid: "000000000000000000000007" } + files_id: { $oid: "000000000000000000000005" } + n: 2 + data: { $binary: { base64: "mao=", subType: "00" } } # hex: 99aa + +tests: + # Changed from original test ("length is 8") to operate on same initialData + - description: "Delete when length is 10" + operations: + - name: delete + object: *bucket0 + arguments: + id: { $oid: "000000000000000000000005" } + # Original test uses "assert.data" syntax to modify outcome collection for + # comparison. This can be accomplished using "outcome" directly. + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: [] + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: [] + + - description: "Download when there are three chunks" + operations: + # Original test uses "download" operation. We use an explicit operation + # that returns a stream and then assert the contents of that stream. + - 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" } + # Original test expects "FileNotFound" error, which isn't specified + expectError: { isError: true } + + - description: "Download when an intermediate chunk is missing" + operations: + # Original test uses "arrange" syntax to modify initialData. This can be + # accomplished as a delete operation on the chunks collection. + - 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" } + # Original test expects "ChunkIsMissing" error, which isn't specified + expectError: { isError: true } + + - description: "Upload when length is 5" + operations: + # Original test uses "upload" operation. We use an explicit operation + # that takes a stream, which has been created from the expected hex bytes. + - name: upload + object: *bucket0 + arguments: + filename: filename + source: { $$hexBytes: "1122334455" } + chunkSizeBytes: 4 + # Original test references the result directly in "assert.data". Here, + # we need to save the result as an entity, which we can later reference. + expectResult: { $$type: objectId } + saveResultAsEntity: &oid0 oid0 + # "outcome" does not allow operators, but we can perform the assertions + # with separate find operations. + - name: find + object: *bucket0_files_collection + arguments: + filter: {} + sort: { uploadDate: -1 } + limit: 1 + expectResult: + - _id: { $$matchesEntity: *oid0 } + length: 5 + chunkSize: 4 + uploadDate: { $$type: date } + # The md5 field is deprecated so some drivers do not calculate it when uploading files. + md5: { $$unsetOrMatches: "283d4fea5dded59cf837d3047328f5af" } + filename: filename + - name: find + object: *bucket0_chunks_collection + arguments: + # We cannot use the saved ObjectId when querying, but filtering by a + # non-zero timestamp will exclude initialData and sort can return the + # expected chunks in order. + filter: { _id: { $gt: { $oid: "000000000000000000000007" } } } + sort: { n: 1 } + expectResult: + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *oid0 } + n: 0 + data: { $binary: { base64: "ESIzRA==", subType: "00" } } # hex 11223344 + - _id: { $$type: objectId } + files_id: { $$matchesEntity: *oid0 } + n: 1 + data: { $binary: { base64: "VQ==", subType: "00" } } # hex 55 diff --git a/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.json b/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.json new file mode 100644 index 0000000000..309d1d3b4b --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.json @@ -0,0 +1,193 @@ +{ + "description": "poc-queryable-encryption", + "schemaVersion": "1.23", + "runOnRequirements": [ + { + "minServerVersion": "7.0", + "csfle": true, + "topologies": [ + "replicaset", + "load-balanced", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "autoEncryptOpts": { + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local": { + "key": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + } + } + }, + { + "database": { + "id": "encryptedDB", + "client": "client0", + "databaseName": "poc-queryable-encryption" + } + }, + { + "collection": { + "id": "encryptedColl", + "database": "encryptedDB", + "collectionName": "encrypted" + } + }, + { + "client": { + "id": "client1" + } + }, + { + "database": { + "id": "unencryptedDB", + "client": "client1", + "databaseName": "poc-queryable-encryption" + } + }, + { + "collection": { + "id": "unencryptedColl", + "database": "unencryptedDB", + "collectionName": "encrypted" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local" + } + } + ] + }, + { + "databaseName": "poc-queryable-encryption", + "collectionName": "encrypted", + "documents": [], + "createOptions": { + "encryptedFields": { + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encryptedInt", + "bsonType": "int", + "queries": { + "queryType": "equality", + "contention": { + "$numberLong": "0" + } + } + } + ] + } + } + } + ], + "tests": [ + { + "description": "insert, replace, and find with queryable encryption", + "operations": [ + { + "object": "encryptedColl", + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encryptedInt": 11 + } + } + }, + { + "object": "encryptedColl", + "name": "replaceOne", + "arguments": { + "filter": { + "encryptedInt": 11 + }, + "replacement": { + "encryptedInt": 22 + } + } + }, + { + "object": "encryptedColl", + "name": "find", + "arguments": { + "filter": { + "encryptedInt": 22 + } + }, + "expectResult": [ + { + "_id": 1, + "encryptedInt": 22 + } + ] + }, + { + "object": "unencryptedColl", + "name": "find", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "encryptedInt": { + "$$type": "binData" + }, + "__safeContent__": [ + { + "$binary": { + "base64": "rhS16TJojgDDBtbluxBokvcotP1mQTGeYpNt8xd3MJQ=", + "subType": "00" + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.yml b/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.yml new file mode 100644 index 0000000000..797904ee95 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-queryable-encryption.yml @@ -0,0 +1,86 @@ +description: poc-queryable-encryption + +schemaVersion: "1.23" + +runOnRequirements: + - minServerVersion: "7.0" + csfle: true + # QE is not supported on standalone servers + topologies: [ replicaset, load-balanced, sharded ] + +createEntities: + - client: + id: &client0 client0 + autoEncryptOpts: + keyVaultNamespace: keyvault.datakeys + kmsProviders: + local: + key: Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk + - database: + id: &encryptedDB encryptedDB + client: *client0 + databaseName: &encryptedDBName poc-queryable-encryption + - collection: + id: &encryptedColl encryptedColl + database: *encryptedDB + collectionName: &encryptedCollName encrypted + - client: + id: &client1 client1 + - database: + id: &unencryptedDB unencryptedDB + client: *client1 + databaseName: *encryptedDBName + - collection: + id: &unencryptedColl unencryptedColl + database: *unencryptedDB + collectionName: *encryptedCollName + +initialData: + - databaseName: keyvault + collectionName: datakeys + documents: + - _id: &keyid { $binary: { base64: EjRWeBI0mHYSNBI0VniQEg==, subType: "04" } } + keyMaterial: { $binary: { base64: sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: + provider: local + - databaseName: *encryptedDBName + collectionName: *encryptedCollName + documents: [] + createOptions: + encryptedFields: + fields: + - keyId: *keyid + path: 'encryptedInt' + bsonType: 'int' + queries: {'queryType': 'equality', 'contention': {'$numberLong': '0'}} + +tests: + - description: insert, replace, and find with queryable encryption + operations: + - object: *encryptedColl + name: insertOne + arguments: + document: + _id: 1 + encryptedInt: 11 + - object: *encryptedColl + name: replaceOne + arguments: + filter: { encryptedInt: 11 } + replacement: { encryptedInt: 22 } + - object: *encryptedColl + name: find + arguments: + filter: { encryptedInt: 22 } + expectResult: + - _id: 1 + encryptedInt: 22 + - object: *unencryptedColl + name: find + arguments: + filter: {} + expectResult: + - { _id: 1, encryptedInt: { $$type: binData }, __safeContent__: [ { "$binary" : { "base64" : "rhS16TJojgDDBtbluxBokvcotP1mQTGeYpNt8xd3MJQ=", "subType" : "00" } } ] } \ No newline at end of file diff --git a/source/unified-test-format/tests/valid-pass/poc-retryable-reads.json b/source/unified-test-format/tests/valid-pass/poc-retryable-reads.json new file mode 100644 index 0000000000..2b65d501a7 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-retryable-reads.json @@ -0,0 +1,433 @@ +{ + "description": "poc-retryable-reads", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "single", + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "client": { + "id": "client1", + "uriOptions": { + "retryReads": false + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-reads-tests" + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-reads-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-reads-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "Aggregate succeeds after InterruptedAtShutdown", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "aggregate" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "aggregate": "coll", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + }, + { + "$sort": { + "x": 1 + } + } + ] + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find succeeds on second attempt", + "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": {}, + "sort": { + "_id": 1 + }, + "limit": 2 + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 2 + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {}, + "sort": { + "_id": 1 + }, + "limit": 2 + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails on first attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection1", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {} + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "Find fails on second attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true + } + } + } + }, + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": {} + }, + "expectError": { + "isError": true + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {} + }, + "databaseName": "retryable-reads-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "find": "coll", + "filter": {} + }, + "databaseName": "retryable-reads-tests" + } + } + ] + } + ] + }, + { + "description": "ListDatabases succeeds on second attempt", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "listDatabases" + ], + "closeConnection": true + } + } + } + }, + { + "name": "listDatabases", + "object": "client0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + }, + { + "commandStartedEvent": { + "command": { + "listDatabases": 1 + } + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-retryable-reads.yml b/source/unified-test-format/tests/valid-pass/poc-retryable-reads.yml new file mode 100644 index 0000000000..b06b437f61 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-retryable-reads.yml @@ -0,0 +1,193 @@ +description: "poc-retryable-reads" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [ single, replicaset ] + - minServerVersion: "4.1.7" + topologies: [ sharded ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - client: + id: &client1 client1 + uriOptions: { retryReads: false } + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-reads-tests + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - {_id: 1, x: 11} + - {_id: 2, x: 22} + - {_id: 3, x: 33} + +tests: + - description: "Aggregate succeeds after InterruptedAtShutdown" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ aggregate ] + errorCode: 11600 # InterruptedAtShutdown + - name: aggregate + object: *collection0 + arguments: + pipeline: &pipeline + - $match: { _id: { $gt: 1 } } + - $sort: { x: 1 } + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: *pipeline + databaseName: *databaseName + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: *pipeline + databaseName: *databaseName + + - description: "Find succeeds on second attempt" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + closeConnection: true + # Find options and expected result changed to use common initialData + - name: find + object: *collection0 + arguments: + filter: {} + sort: { _id: 1 } + limit: 2 + expectResult: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + expectEvents: + - client: *client0 + events: + - &findAttempt + commandStartedEvent: + command: + find: *collectionName + filter: {} + sort: { _id: 1 } + limit: 2 + databaseName: *databaseName + - *findAttempt + + - description: "Find fails on first attempt" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ find ] + closeConnection: true + - name: find + object: *collection1 # client uses retryReads=false + arguments: + filter: {} + # Other arguments in the original test are not relevant + expectError: { isError: true } + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + find: *collectionName + filter: {} + databaseName: *databaseName + + - description: "Find fails on second attempt" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ find ] + closeConnection: true + - name: find + object: *collection0 + arguments: + filter: {} + # Other arguments in the original test are not relevant + expectError: { isError: true } + expectEvents: + - client: *client0 + events: + - &findAttempt + commandStartedEvent: + command: + find: *collectionName + filter: {} + databaseName: *databaseName + - *findAttempt + + - description: "ListDatabases succeeds on second attempt" + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ listDatabases ] + closeConnection: true + - name: listDatabases + object: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: { listDatabases: 1 } + - commandStartedEvent: + command: { listDatabases: 1 } diff --git a/source/unified-test-format/tests/valid-pass/poc-retryable-writes.json b/source/unified-test-format/tests/valid-pass/poc-retryable-writes.json new file mode 100644 index 0000000000..f19aa3f9d8 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-retryable-writes.json @@ -0,0 +1,491 @@ +{ + "description": "poc-retryable-writes", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "client": { + "id": "client1", + "uriOptions": { + "retryWrites": false + }, + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "retryable-writes-tests" + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "retryable-writes-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "coll" + } + } + ], + "initialData": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ], + "tests": [ + { + "description": "FindOneAndUpdate is committed on first attempt", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is not committed on first attempt", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 1 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectResult": { + "_id": 1, + "x": 11 + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 12 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "FindOneAndUpdate is never committed", + "runOnRequirements": [ + { + "minServerVersion": "3.6", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "onPrimaryTransactionalWrite", + "mode": { + "times": 2 + }, + "data": { + "failBeforeCommitExceptionCode": 1 + } + } + } + }, + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "Before" + }, + "expectError": { + "isError": true + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertMany succeeds after PrimarySteppedDown", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 189, + "errorLabels": [ + "RetryableWriteError" + ] + } + } + } + }, + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ], + "ordered": true + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedIds": { + "$$unsetOrMatches": { + "0": 3, + "1": 4 + } + } + } + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + }, + { + "description": "InsertOne fails after connection failure when retryWrites option is false", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.7", + "topologies": [ + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client1", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "errorLabelsOmit": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "InsertOne fails after multiple retryable writeConcernErrors", + "runOnRequirements": [ + { + "minServerVersion": "4.3.1", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "client0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorLabels": [ + "RetryableWriteError" + ], + "writeConcernError": { + "code": 91, + "errmsg": "Replication is being shut down" + } + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 3, + "x": 33 + } + }, + "expectError": { + "errorLabelsContain": [ + "RetryableWriteError" + ] + } + } + ], + "outcome": [ + { + "collectionName": "coll", + "databaseName": "retryable-writes-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-retryable-writes.yml b/source/unified-test-format/tests/valid-pass/poc-retryable-writes.yml new file mode 100644 index 0000000000..c11e8b6ef7 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-retryable-writes.yml @@ -0,0 +1,214 @@ +description: "poc-retryable-writes" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - client: + id: &client1 client1 + uriOptions: { retryWrites: false } + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName retryable-writes-tests + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + +tests: + - description: "FindOneAndUpdate is committed on first attempt" + runOnRequirements: &onPrimaryTransactionalWrite_requirements + - minServerVersion: "3.6" + topologies: [ replicaset ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x : 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + + - description: "FindOneAndUpdate is not committed on first attempt" + runOnRequirements: *onPrimaryTransactionalWrite_requirements + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 1 } + data: { failBeforeCommitExceptionCode: 1 } + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x : 1 } } + returnDocument: Before + expectResult: { _id: 1, x: 11 } + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 12 } + - { _id: 2, x: 22 } + + - description: "FindOneAndUpdate is never committed" + runOnRequirements: *onPrimaryTransactionalWrite_requirements + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: onPrimaryTransactionalWrite + mode: { times: 2 } + data: { failBeforeCommitExceptionCode: 1 } + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x : 1 } } + returnDocument: Before + expectError: { isError: true } + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + + - description: "InsertMany succeeds after PrimarySteppedDown" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: [ replicaset, sharded ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 189 # PrimarySteppedDown + errorLabels: [ RetryableWriteError ] + - name: insertMany + object: *collection0 + arguments: + documents: + # Documents are modified from original test for "initialData" + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + ordered: true + expectResult: + # InsertManyResult is optional because all of its fields are optional + $$unsetOrMatches: { insertedIds: { $$unsetOrMatches: { 0: 3, 1: 4 } } } + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + + - description: "InsertOne fails after connection failure when retryWrites option is false" + runOnRequirements: # failCommand + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.7" + topologies: [ sharded ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client1 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - name: insertOne + object: *collection1 + arguments: + document: { _id: 3, x: 33 } + expectError: + # If retryWrites is false, the driver should not add the + # RetryableWriteError label to the error. + errorLabelsOmit: [ RetryableWriteError ] + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + + - description: "InsertOne fails after multiple retryable writeConcernErrors" + runOnRequirements: + - minServerVersion: "4.3.1" # failCommand errorLabels option + topologies: [ replicaset, sharded ] + operations: + - name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 2 } + data: + failCommands: [ insert ] + errorLabels: [ RetryableWriteError ] + writeConcernError: + code: 91 # ShutdownInProgress + errmsg: "Replication is being shut down" + - name: insertOne + object: *collection0 + arguments: + document: { _id: 3, x: 33 } + expectError: + errorLabelsContain: [ RetryableWriteError ] + outcome: + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } # The write was still applied diff --git a/source/unified-test-format/tests/valid-pass/poc-sessions.json b/source/unified-test-format/tests/valid-pass/poc-sessions.json new file mode 100644 index 0000000000..117c9e7d00 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-sessions.json @@ -0,0 +1,466 @@ +{ + "description": "poc-sessions", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "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 + } + ] + } + ] + }, + { + "description": "Dirty explicit session is discarded", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ] + } + ], + "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 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-sessions.yml b/source/unified-test-format/tests/valid-pass/poc-sessions.yml new file mode 100644 index 0000000000..2090258328 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-sessions.yml @@ -0,0 +1,213 @@ +description: "poc-sessions" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "3.6.0" + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: false + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name session-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + 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 + - &find_with_implicit_session + name: find + object: *collection0 + arguments: + filter: { _id: -1 } + expectResult: [] + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: [ { _id: 2 } ] + ordered: true + lsid: { $$sessionLsid: *session0 } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: -1 } + lsid: { $$sessionLsid: *session0 } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + + - description: "Server supports implicit sessions" + operations: + - name: insertOne + object: *collection0 + arguments: + document: { _id: 2 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 2 } } } + - *find_with_implicit_session + - name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - { _id: 2 } + ordered: true + # Original test did not include any assertion, but we can use + # $$type to expect an arbitrary lsid document + lsid: { $$type: object } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: -1 } + lsid: { $$type: object } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + + - description: "Dirty explicit session is discarded" + # Original test specified retryWrites=true, but that is now the default. + runOnRequirements: + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.8" + topologies: [ sharded ] + 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 + - *find_with_implicit_session + - name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 + expectEvents: + - client: *client0 + events: + # ajv's YAML parser is unable to handle anchors on array elements, so + # we define an anchor on the commandStartedEvent object instead + - commandStartedEvent: &insert_attempt + command: + insert: *collection0Name + documents: + - { _id: 2 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + commandName: insert + databaseName: *database0Name + - commandStartedEvent: *insert_attempt + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - { _id: 3 } + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 2 + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: -1 } + lsid: { $$type: object } + commandName: find + databaseName: *database0Name + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json b/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json new file mode 100644 index 0000000000..9ab44a9c54 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json @@ -0,0 +1,505 @@ +{ + "description": "poc-transactions-convenient-api", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "client": { + "id": "client1", + "uriOptions": { + "readConcernLevel": "local", + "w": 1 + }, + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "database": { + "id": "database1", + "client": "client1", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "collection": { + "id": "collection1", + "database": "database1", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + }, + { + "session": { + "id": "session1", + "client": "client1" + } + }, + { + "session": { + "id": "session2", + "client": "client0", + "sessionOptions": { + "defaultTransactionOptions": { + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "withTransaction and no transaction options set", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "autocommit": false, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction inherits transaction options from client", + "operations": [ + { + "name": "withTransaction", + "object": "session1", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection1", + "arguments": { + "session": "session1", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client1", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "local" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session1" + }, + "txnNumber": 1, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction inherits transaction options from defaultTransactionOptions", + "operations": [ + { + "name": "withTransaction", + "object": "session2", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session2", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session2" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session2" + }, + "txnNumber": 1, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "withTransaction explicit transaction options", + "operations": [ + { + "name": "withTransaction", + "object": "session0", + "arguments": { + "callback": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 1 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 1 + } + } + } + } + ], + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "w": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 1 + } + ], + "ordered": true, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "readConcern": { + "level": "majority" + }, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "autocommit": false, + "writeConcern": { + "w": 1 + }, + "readConcern": { + "$$exists": false + }, + "startTransaction": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml b/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml new file mode 100644 index 0000000000..94fadda0aa --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.yml @@ -0,0 +1,235 @@ +description: "poc-transactions-convenient-api" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.8" + topologies: [ sharded ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - client: + id: &client1 client1 + uriOptions: + readConcernLevel: local + w: 1 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName transaction-tests + - database: + id: &database1 database1 + client: *client1 + databaseName: *databaseName + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName test + - collection: + id: &collection1 collection1 + database: *database1 + collectionName: *collectionName + - session: + id: &session0 session0 + client: *client0 + - session: + id: &session1 session1 + client: *client1 + - session: + id: &session2 session2 + client: *client0 + sessionOptions: + defaultTransactionOptions: + readConcern: { level: majority } + writeConcern: { w: 1 } + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + documents: [] + +tests: + - description: "withTransaction and no transaction options set" + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 1 } ] + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + # omitted fields + readConcern: { $$exists: false } + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + autocommit: false + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: &outcome + - collectionName: *collectionName + databaseName: *databaseName + documents: + - { _id: 1 } + + - description: "withTransaction inherits transaction options from client" + operations: + - name: withTransaction + object: *session1 + arguments: + callback: + - name: insertOne + object: *collection1 + arguments: + session: *session1 + document: { _id: 1 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } } + expectEvents: + - client: *client1 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 1 } ] + ordered: true + lsid: { $$sessionLsid: *session1 } + txnNumber: 1 + startTransaction: true + autocommit: false + readConcern: { level: local } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session1 } + txnNumber: 1 + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + + - description: "withTransaction inherits transaction options from defaultTransactionOptions" + operations: + - name: withTransaction + object: *session2 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session2 + document: { _id: 1 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 1 } ] + ordered: true + lsid: { $$sessionLsid: *session2 } + txnNumber: 1 + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session2 } + txnNumber: 1 + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome + + - description: "withTransaction explicit transaction options" + operations: + - name: withTransaction + object: *session0 + arguments: + callback: + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 1 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 1 } } } + readConcern: { level: majority } + writeConcern: { w: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 1 } ] + ordered: true + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + readConcern: { level: majority } + # omitted fields + writeConcern: { $$exists: false } + commandName: insert + databaseName: *databaseName + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + autocommit: false + writeConcern: { w: 1 } + # omitted fields + readConcern: { $$exists: false } + startTransaction: { $$exists: false } + commandName: commitTransaction + databaseName: admin + outcome: *outcome diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json b/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json new file mode 100644 index 0000000000..de08edec44 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json @@ -0,0 +1,409 @@ +{ + "description": "poc-transactions-mongos-pin-auto", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "remain pinned after non-transient Interrupted error on insertOne", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11601 + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsOmit": [ + "TransientTransactionError", + "UnknownTransactionCommitResult" + ], + "errorCodeName": "Interrupted" + } + }, + { + "name": "assertSessionPinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "commitTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$type": "object" + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ] + }, + { + "description": "unpin after transient error within a transaction", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 3 + } + }, + "expectResult": { + "$$unsetOrMatches": { + "insertedId": { + "$$unsetOrMatches": 3 + } + } + } + }, + { + "name": "targetedFailPoint", + "object": "testRunner", + "arguments": { + "session": "session0", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "closeConnection": true + } + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "session": "session0", + "document": { + "_id": 4 + } + }, + "expectError": { + "errorLabelsContain": [ + "TransientTransactionError" + ], + "errorLabelsOmit": [ + "UnknownTransactionCommitResult" + ] + } + }, + { + "name": "assertSessionUnpinned", + "object": "testRunner", + "arguments": { + "session": "session0" + } + }, + { + "name": "abortTransaction", + "object": "session0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 3 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "test", + "documents": [ + { + "_id": 4 + } + ], + "ordered": true, + "readConcern": { + "$$exists": false + }, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "insert", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "abortTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + }, + "recoveryToken": { + "$$type": "object" + } + }, + "commandName": "abortTransaction", + "databaseName": "admin" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml b/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml new file mode 100644 index 0000000000..33cd2a2521 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.yml @@ -0,0 +1,169 @@ +description: "poc-transactions-mongos-pin-auto" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.1.8" + topologies: [ sharded ] + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "remain pinned after non-transient Interrupted error on insertOne" + operations: + - &startTransaction + name: startTransaction + object: *session0 + - &firstInsert + name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 3 } + expectResult: { $$unsetOrMatches: { insertedId: { $$unsetOrMatches: 3 } } } + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + errorCode: 11601 # Interrupted + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsOmit: [ TransientTransactionError, UnknownTransactionCommitResult ] + errorCodeName: Interrupted + - name: assertSessionPinned + object: testRunner + arguments: + session: *session0 + - name: commitTransaction + object: *session0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: &firstInsertEvent + command: + insert: *collection0Name + documents: [ { _id: 3 } ] + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: &secondInsertEvent + command: + insert: *collection0Name + documents: [ { _id: 4 } ] + ordered: true + readConcern: { $$exists: false } + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: insert + databaseName: *database0Name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + # Original test expected any value, but we can assert an object + recoveryToken: { $$type: object } + commandName: commitTransaction + databaseName: admin + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + - { _id: 3 } + + - description: "unpin after transient error within a transaction" + operations: + - *startTransaction + - *firstInsert + - name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: [ insert ] + closeConnection: true + - name: insertOne + object: *collection0 + arguments: + session: *session0 + document: { _id: 4 } + expectError: + errorLabelsContain: [ TransientTransactionError ] + errorLabelsOmit: [ UnknownTransactionCommitResult ] + - name: assertSessionUnpinned + object: testRunner + arguments: + session: *session0 + - name: abortTransaction + object: *session0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: *firstInsertEvent + - commandStartedEvent: *secondInsertEvent + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + # Original test expected any value, but we can assert an object + recoveryToken: { $$type: object } + commandName: abortTransaction + databaseName: admin + outcome: + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions.json b/source/unified-test-format/tests/valid-pass/poc-transactions.json new file mode 100644 index 0000000000..2055a3b705 --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions.json @@ -0,0 +1,323 @@ +{ + "description": "poc-transactions", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "4.0", + "topologies": [ + "replicaset" + ] + }, + { + "minServerVersion": "4.1.8", + "topologies": [ + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "transaction-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "test" + } + }, + { + "session": { + "id": "session0", + "client": "client0" + } + } + ], + "initialData": [ + { + "collectionName": "test", + "databaseName": "transaction-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "Client side error in command starting transaction", + "operations": [ + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "session": "session0", + "filter": { + "_id": 1 + }, + "update": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + }, + { + "name": "assertSessionTransactionState", + "object": "testRunner", + "arguments": { + "session": "session0", + "state": "starting" + } + } + ] + }, + { + "description": "explicitly create collection using create command", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "createCollection", + "object": "database0", + "arguments": { + "session": "session0", + "collection": "test" + } + }, + { + "name": "assertCollectionNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertCollectionExists", + "object": "testRunner", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "create": "test", + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "create", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + }, + { + "description": "create index on a non-existing collection", + "runOnRequirements": [ + { + "minServerVersion": "4.3.4", + "topologies": [ + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "dropCollection", + "object": "database0", + "arguments": { + "collection": "test" + } + }, + { + "name": "startTransaction", + "object": "session0" + }, + { + "name": "createIndex", + "object": "collection0", + "arguments": { + "session": "session0", + "name": "x_1", + "keys": { + "x": 1 + } + } + }, + { + "name": "assertIndexNotExists", + "object": "testRunner", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "x_1" + } + }, + { + "name": "commitTransaction", + "object": "session0" + }, + { + "name": "assertIndexExists", + "object": "testRunner", + "arguments": { + "databaseName": "transaction-tests", + "collectionName": "test", + "indexName": "x_1" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "drop": "test", + "writeConcern": { + "$$exists": false + } + }, + "commandName": "drop", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "createIndexes": "test", + "indexes": [ + { + "name": "x_1", + "key": { + "x": 1 + } + } + ], + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": true, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "createIndexes", + "databaseName": "transaction-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "commitTransaction": 1, + "lsid": { + "$$sessionLsid": "session0" + }, + "txnNumber": 1, + "startTransaction": { + "$$exists": false + }, + "autocommit": false, + "writeConcern": { + "$$exists": false + } + }, + "commandName": "commitTransaction", + "databaseName": "admin" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/valid-pass/poc-transactions.yml b/source/unified-test-format/tests/valid-pass/poc-transactions.yml new file mode 100644 index 0000000000..8a12c8b39a --- /dev/null +++ b/source/unified-test-format/tests/valid-pass/poc-transactions.yml @@ -0,0 +1,171 @@ +description: "poc-transactions" + +schemaVersion: "1.0" + +runOnRequirements: + - minServerVersion: "4.0" + topologies: [ replicaset ] + - minServerVersion: "4.1.8" + topologies: [ sharded ] + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name transaction-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name test + - session: + id: &session0 session0 + client: *client0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: "Client side error in command starting transaction" + operations: + - name: startTransaction + object: *session0 + - name: updateOne + object: *collection0 + arguments: + session: *session0 + filter: { _id: 1 } + update: { x: 1 } + # Original test only asserted a generic error + expectError: { isClientError: true } + - name: assertSessionTransactionState + object: testRunner + arguments: + session: *session0 + state: starting + + - description: "explicitly create collection using create command" + runOnRequirements: + - minServerVersion: "4.3.4" + topologies: [ replicaset, sharded ] + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: startTransaction + object: *session0 + - name: createCollection + object: *database0 + arguments: + session: *session0 + collection: *collection0Name + - name: assertCollectionNotExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + - name: commitTransaction + object: *session0 + - name: assertCollectionExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database0Name + - commandStartedEvent: + command: + create: *collection0Name + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: create + databaseName: *database0Name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin + + - description: "create index on a non-existing collection" + runOnRequirements: + - minServerVersion: "4.3.4" + topologies: [ replicaset, sharded ] + operations: + - name: dropCollection + object: *database0 + arguments: + collection: *collection0Name + - name: startTransaction + object: *session0 + - name: createIndex + object: *collection0 + arguments: + session: *session0 + name: &indexName "x_1" + keys: { x: 1 } + - name: assertIndexNotExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + indexName: *indexName + - name: commitTransaction + object: *session0 + - name: assertIndexExists + object: testRunner + arguments: + databaseName: *database0Name + collectionName: *collection0Name + indexName: *indexName + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + drop: *collection0Name + writeConcern: { $$exists: false } + commandName: drop + databaseName: *database0Name + - commandStartedEvent: + command: + createIndexes: *collection0Name + indexes: + - name: *indexName + key: { x: 1 } + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: true + autocommit: false + writeConcern: { $$exists: false } + commandName: createIndexes + databaseName: *database0Name + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session0 } + txnNumber: 1 + startTransaction: { $$exists: false } + autocommit: false + writeConcern: { $$exists: false } + commandName: commitTransaction + databaseName: admin diff --git a/source/unified-test-format/unified-test-format.md b/source/unified-test-format/unified-test-format.md new file mode 100644 index 0000000000..df25ac5c6b --- /dev/null +++ b/source/unified-test-format/unified-test-format.md @@ -0,0 +1,3696 @@ +# Unified Test Format + +- Status: Accepted +- Minimum Server Version: N/A +- Current Schema Version: 1.28.0 + +______________________________________________________________________ + +## Abstract + +This project defines a unified schema for YAML and JSON specification tests, which run operations against a MongoDB +deployment. By conforming various spec tests to a single schema, drivers can implement a single test runner to execute +acceptance tests for multiple specifications, thereby reducing maintenance of existing specs and implementation time for +new specifications. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +This document tends to use "SHOULD" more frequently than other specifications, but mainly in the context of providing +guidance on writing test files. This is discussed in more detail in [Design Rationale](#design-rationale). + +## Goals + +This test format can be used to define tests for the following specifications: + +- [Change Streams](../change-streams/change-streams.md) +- [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md) +- [CRUD](../crud/crud.md) +- [GridFS](../gridfs/gridfs-spec.md) +- [Retryable Reads](../retryable-reads/retryable-reads.md) +- [Retryable Writes](../retryable-writes/retryable-writes.md) +- [Sessions](../sessions/driver-sessions.md) +- [Transactions](../transactions/transactions.md) +- [Convenient API for Transactions](../transactions-convenient-api/transactions-convenient-api.md) +- [Server Discovery and Monitoring](../server-discovery-and-monitoring/server-discovery-and-monitoring.md) + +This is not an exhaustive list. Specifications that are known to not be supported by this format may be discussed under +[Future Work](#future-work). + +## Specification + +### Terms + +**Entity** Any object or value that is indexed by a unique name and stored in the [Entity Map](#entity-map). This will +typically be a driver object (e.g. client, session) defined in [createEntities](#createentities) but may also be a +[saved operation result](#operation_saveResultAsEntity). A exhaustive list of supported types is presented in +[Supported Entity Types](#supported-entity-types). Entities are referenced by name throughout the test file (e.g. +[Entity Test Operations](#entity-test-operations)). + +**Internal MongoClient** A MongoClient created specifically for use with internal test operations, such as inserting +collection data before a test, performing special assertions during a test, or asserting collection data after a test. + +**Iterable** This term is used by various specifications as the return type for operations that return a sequence of +items, which may be iterated. For example, the CRUD spec uses this as the return value for `find` and permit API +flexibility rather than stipulate that a cursor object be returned directly. + +### Server Compatibility + +The unified test format is intended to only run against enterprise servers. Additionally, many tests make use of +[Server Fail Points](#server-fail-points), so servers MUST be launched with the `enableTestCommands` parameter enabled. + +### Schema Version + +This specification and the [Test Format](#test-format) follow [semantic versioning](https://semver.org/). The version is +primarily used to validate test files with a [JSON schema](https://json-schema.org/) and also allow test runners to +determine whether a particular test file is supported. + +New tests files SHOULD always be written using the latest major version of this specification; however, test files +SHOULD be conservative in the minor version they specify (as noted in [schemaVersion](#schemaVersion)). + +#### JSON Schema Validation + +Each major or minor version that changes the [Test Format](#test-format) SHALL have a corresponding JSON schema. When a +new schema file is introduced, any existing schema files MUST remain in place since they may be needed for validation. +For example: if an additive change is made to version 1.0 of the spec, the `schema-1.0.json` file will be copied to +`schema-1.1.json` and modified accordingly. A new or existing test file using [schemaVersion](#schemaVersion) "1.0" +would then be expected to validate against both schema files. Schema version bumps MUST be noted in the +[Changelog](#changelog). + +A particular minor version MUST be capable of validating any and all test files in that major version series up to and +including the minor version. For example, `schema-2.1.json` should validate test files with +[schemaVersion](#schemaVersion) "2.0" and "2.1", but would not be expected to validate files specifying "1.0", "2.2", or +"3.0". + +The JSON schema MUST remain consistent with the [Test Format](#test-format) section. If and when a new major version is +introduced, the [Breaking Changes](#breaking-changes) section MUST be updated. + +[Ajv](https://ajv.js.org/) MAY be used to programmatically validate both YAML and JSON files using the JSON schema. The +JSON schema MUST NOT use syntax that is unsupported by this tool, which bears mentioning because there are multiple +versions of the [JSON schema specification](https://json-schema.org/specification.html). + +#### Test Runner Support + +Each test file defines a [schemaVersion](#schemaVersion), which test runners will use to determine compatibility (i.e. +whether and how the test file will be interpreted). Test files are considered compatible with a test runner if their +[schemaVersion](#schemaVersion) is less than or equal to a supported version in the test runner, given the same major +version component. For example: + +- A test runner supporting version 1.5.1 could execute test files with versions 1.0 and 1.5 but *not* 1.6 and 2.0. +- A test runner supporting version 2.1 could execute test files with versions 2.0 and 2.1 but *not* 1.0 and 1.5. +- A test runner supporting *both* versions 1.5.1 and 2.0 could execute test files with versions 1.4, 1.5, and 2.0, but + *not* 1.6, 2.1, or 3.0. +- A test runner supporting version 2.0.1 could execute test files with versions 2.0 and 2.0.1 but *not* 2.0.2 or 2.1. + This example is provided for completeness, but test files SHOULD NOT need to refer to patch versions (as previously + mentioned). + +Test runners MUST NOT process incompatible files and MUST raise an error if they encounter an incompatible file (as +discussed in [Executing a Test File](#executing-a-test-file)). Test runners MAY support multiple schema versions (as +demonstrated in the example above). + +#### Impact of Spec Changes on Schema Version + +Backwards-breaking changes SHALL warrant a new major version. These changes include, but are not limited to: + +- Subtractive changes, such as removing a field, operation, or type of supported entity or event +- Changing an existing field from optional to required +- Introducing a new, required field in the test format +- Significant changes to test file execution (not BC) + +Backwards-compatible changes SHALL warrant a new minor version. These changes include, but are not limited to: + +- Additive changes, such as a introducing a new [Special Test Operations](#special-test-operations) or type of supported + entity or event +- Changing an existing field from required to optional +- Introducing a new, optional field in the test format +- Minor changes to test file execution (BC) + +Small fixes and internal spec changes (e.g. grammar, adding clarifying text to the spec) MAY warrant a new patch +version; however, patch versions SHOULD NOT alter the structure of the test format and thus SHOULD NOT be relevant to +test files (as noted in [schemaVersion](#schemaVersion)). + +### Entity Map + +The entity map indexes arbitrary objects and values by unique names, so that they can be referenced from test constructs +(e.g. [operation.object](#operation_object)). To ensure each test is executed in isolation, test runners MUST NOT share +entity maps between tests. Most entities will be driver objects created by the [createEntities](#createentities) +directive during test setup, but the entity map may also be modified during test execution via the +[operation.saveResultAsEntity](#operation_saveResultAsEntity) directive. + +Test runners MAY choose to implement the entity map in a fashion most suited to their language, but implementations MUST +enforce both uniqueness of entity names and referential integrity when fetching an entity. Test runners MUST raise an +error if an attempt is made to store an entity with a name that already exists in the map and MUST raise an error if an +entity is not found for a name or is found but has an unexpected type. + +Test runners MUST provide a mechanism to retrieve entities from the entity map prior to the clearing of the entity map, +as discussed in [Executing a Test](#executing-a-test). There MUST be a way to retrieve an entity by its name (for +example, to support retrieving the iteration count stored by the `storeIterationsAsEntity` option). + +Test runners MAY restrict access to driver objects (e.g. MongoClient, ChangeStream) and only allow access to BSON types +(see: [Supported Entity Types](#supported-entity-types)). This restriction may be necessary if the test runner needs to +ensure driver objects in its entity map are properly freed/destroyed between tests. + +The entity map MUST be implemented in a way that allows for safe concurrent access, since a test may include multiple +thread entities that all need to access the map concurrently. See [entity_thread](#entity_thread) for more information +on test runner threads. + +Consider the following examples: + +```yaml +# Error due to a duplicate name (client0 was already defined) +createEntities: + - client: { id: client0 } + - client: { id: client0 } + +# Error due to a missing entity (client1 is undefined) +createEntities: + - client: { id: client0 } + - session: { id: session0, client: client1 } + +# Error due to an unexpected entity type (session instead of client) +createEntities: + - client: { id: client0 } + - session: { id: session0, client: client0 } + - session: { id: session1, client: session0 } +``` + +#### Supported Entity Types + +Test runners MUST support the following types of entities: + +- MongoClient. See [entity_client](#entity_client) and [Client Operations](#client-operations). +- ClientEncryption. See [entity_clientEncryption](#entity_clientEncryption) and + [ClientEncryption Operations](#clientencryption-operations). +- Database. See [entity_database](#entity_database) and [Database Operations](#database-operations). +- Collection. See [entity_collection](#entity_collection) and [Collection Operations](#collection-operations) +- ClientSession. See [entity_session](#entity_session) and [Session Operations](#session-operations). +- GridFS Bucket. See [entity_bucket](#entity_bucket) and [Bucket Operations](#bucket-operations). + + + +- ChangeStream. Change stream entities are special in that they are not defined in [createEntities](#createentities) but + are instead created by using [operation.saveResultAsEntity](#operation_saveResultAsEntity) with a + [client_createChangeStream](#client_createChangeStream), + [database_createChangeStream](#database_createChangeStream), or + [collection_createChangeStream](#collection_createChangeStream) operation. + + Test files SHOULD NOT use a `watch` operation to create a change stream, as the implementation of that method may vary + among drivers. For example, some implementations of `watch` immediately execute `aggregate` and construct the + server-side cursor, while others may defer `aggregate` until the change stream object is iterated. + + See [Cursor Operations](#cursor-operations) for a list of operations. + +- FindCursor. These entities are not defined in [createEntities](#createentities) but are instead created by using + [operation.saveResultAsEntity](#operation_saveResultAsEntity) with a + [collection_createFindCursor](#collection_createFindCursor) operation. + + See [Cursor Operations](#cursor-operations) for a list of operations. + +- CommandCursor. These entities are not defined in [createEntities](#createentities) but are instead created by using + [operation.saveResultAsEntity](#operation_saveResultAsEntity) with a [createCommandCursor](#createcommandcursor) + operation. + + See [Cursor Operations](#cursor-operations) for a list of operations. + +- All known BSON types and/or equivalent language types for the target driver. For the present version of the spec, the + following BSON types are known: 0x01-0x13, 0x7F, 0xFF. + + Tests SHOULD NOT utilize deprecated types (e.g. 0x0E: Symbol), since they may not be supported by all drivers and + could yield runtime errors (e.g. while loading a test file with an Extended JSON parser). + + + +- Test runner thread. An entity representing a "thread" that can be used to concurrently execute operations. Thread + entities MUST be able to run concurrently with the main test runner thread and other thread entities, but they do + not have to be implemented as actual OS threads (e.g. they can be goroutines or async tasks). See + [entity_thread_object](#entity_thread_object) for more information on how they are created. + + + +- TopologyDescription. An entity representing a client's + [TopologyDescription](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#topologydescription) at + a certain point in time. These entities are not defined in [createEntities](#createentities) but are instead created + via [recordTopologyDescription](#recordtopologydescription) test runner operations. + +This is an exhaustive list of supported types for the entity map. Test runners MUST raise an error if an attempt is made +to store an unsupported type in the entity map. + +Adding new entity types (including known BSON types) to this list will require a minor version bump to the spec and +schema version. Removing entity types will require a major version bump. See +[Impact of Spec Changes on Schema Version](#impact-of-spec-changes-on-schema-version) for more information. + +### Test Format + +Each specification test file can define one or more tests, which inherit some top-level configuration (e.g. namespace, +initial data). YAML and JSON test files are parsed as an object by the test runner. This section defines the top-level +keys for that object and links to various sub-sections for definitions of nested structures (e.g. individual +[test](#test), [operation](#operation)). + +Although test runners are free to process YAML or JSON files, YAML is the canonical format for writing tests. YAML files +may be converted to JSON using a tool such as [js-yaml](https://github.com/nodeca/js-yaml) . + +#### Top-level Fields + +The top-level fields of a test file are as follows: + +- `description`: Required string. The name of the test file. + + This SHOULD describe the common purpose of tests in this file and MAY refer to the filename (e.g. "updateOne-hint"). + + + +- `schemaVersion`: Required string. Version of this specification with which the test file complies. + + Test files SHOULD be conservative when specifying a schema version. For example, if the latest schema version is 1.1 + but the test file complies with schema version 1.0, the test file should specify 1.0. + + Test runners will use this to determine compatibility (i.e. whether and how the test file will be interpreted). The + format of this string is defined in [Version String](#version-string); however, test files SHOULD NOT need to refer + to specific patch versions since patch-level changes SHOULD NOT alter the structure of the test format (as + previously noted in [Schema Version](#schema-version)). + + + +- `runOnRequirements`: Optional array of one or more [runOnRequirement](#runonrequirement) objects. List of server + version and/or topology requirements for which the tests in this file can be run. If no requirements are met, the + test runner MUST skip this test file. + + + +- `createEntities`: Optional array of one or more [entity](#entity) objects. List of entities (e.g. client, collection, + session objects) that SHALL be created before each test case is executed. + + Test files SHOULD define entities in dependency order, such that all referenced entities (e.g. client) are defined + before any of their dependent entities (e.g. database, session). + + + +- `initialData`: Optional array of one or more [collectionData](#collectiondata) objects. Data that will exist in + collections before each test case is executed. + + + +- `tests`: Required array of one or more [test](#test) objects. List of test cases to be executed independently of each + other. +- `_yamlAnchors`: Optional object containing arbitrary data. This is only used to define anchors within the YAML files + and MUST NOT be used by test runners. + +#### runOnRequirement + +A combination of server version, topology, and/or dependency requirements for running the test(s). + +The format of server version strings is defined in [Version String](#version-string). When comparing server version +strings, each component SHALL be compared numerically. For example, "4.0.10" is greater than "4.0.9" and "3.6" and less +than "4.2.0". + +The structure of this object is as follows: + +- `minServerVersion`: Optional string. The minimum server version (inclusive) required to successfully run the tests. If + this field is omitted, there is no lower bound on the required server version. The format of this string is defined + in [Version String](#version-string). + +- `maxServerVersion`: Optional string. The maximum server version (inclusive) against which the tests can be run + successfully. If this field is omitted, there is no upper bound on the required server version. The format of this + string is defined in [Version String](#version-string). + +- `topologies`: Optional array of one or more strings. Server topologies against which the tests can be run + successfully. Valid topologies are "single", "replicaset", "sharded", "load-balanced", and "sharded-replicaset" + (i.e. sharded cluster backed by replica sets). If this field is omitted, there is no topology requirement for the + test. + + When matching a "sharded-replicaset" topology, test runners MUST ensure that all shards are backed by a replica set. + The process for doing so is described in + [Determining if a Sharded Cluster Uses Replica Sets](#determining-if-a-sharded-cluster-uses-replica-sets). When + matching a "sharded" topology, test runners MUST accept any type of sharded cluster (i.e. "sharded" implies + "sharded-replicaset", but not vice versa). + + The "sharded-replicaset" topology type is deprecated. MongoDB 3.6+ requires that all shard servers be replica sets. + Therefore, tests SHOULD use "sharded" instead of "sharded-replicaset" when targeting 3.6+ server versions in order + to avoid unnecessary overhead. + + Note: load balancers were introduced in MongoDB 5.0. Therefore, any sharded cluster behind a load balancer implicitly + uses replica sets for its shards. + +- `serverless` (deprecated): Optional string. Whether or not the test should be run on Atlas Serverless instances. Valid + values are "require", "forbid", and "allow". If "require", the test MUST only be run on Atlas Serverless instances. + If "forbid", the test MUST NOT be run on Atlas Serverless instances. If omitted or "allow", this option has no + effect. + + The test runner MUST be informed whether or not Atlas Serverless is being used in order to determine if this + requirement is met (e.g. through an environment variable or configuration option). + + Note: the Atlas Serverless proxy imitates mongos, so the test runner is not capable of determining if Atlas Serverless + is in use by issuing commands such as `buildInfo` or `hello`. Furthermore, connections to Atlas Serverless use a + load balancer, so the topology will appear as "load-balanced". + + Note: serverless testing is no longer required, and drivers that no longer support serverless testing MAY omit + implementation of this requirement and instantly skip all tests that use `serverless: require`. + +- `serverParameters`: Optional object of server parameters to check against. To check server parameters, drivers send a + `{ getParameter: 1, : 1 }` command to the server using an internal MongoClient. Drivers MAY also choose + to send a `{ getParameter: '*' }` command and fetch all parameters at once. The result SHOULD be cached to avoid + repeated calls to fetch the same parameter. Test runners MUST apply the rules specified in + [Flexible Numeric Comparisons](#flexible-numeric-comparisons) when comparing values. If a server does not support a + parameter, test runners MUST treat the comparison as not equal and skip the test. This includes errors that occur + when fetching a single parameter using `getParameter`. + + + +- `auth`: Optional boolean. If true, the tests MUST only run if authentication is enabled. If false, tests MUST NOT run + if authentication is enabled. If this field is omitted, there is no authentication requirement. + +- `authMechanism`: Optional string. Specifies an authentication mechanism that the server needs to support for the test. + If set, tests MUST only run if the given string matches (case-insensitive) one of the strings in the + [authenticationMechanisms](https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.authenticationMechanisms) + server parameter. If this field is omitted, there is no authentication mechanism requirement. + +- `csfle`: Optional object or boolean. If not false, tests MUST only run if the driver and server support Client-Side + Field Level Encryption. CSFLE is supported when all of the following are true: + + - Server version is 4.2.0 or higher + - Driver has libmongocrypt enabled + - At least one of [crypt_shared](../client-side-encryption/client-side-encryption.md#crypt_shared) and/or + [mongocryptd](../client-side-encryption/client-side-encryption.md#mongocryptd) is available + + If false, tests MUST NOT run if CSFLE is supported. If this field is omitted, there is no CSFLE requirement. + + The structure of the `csfle` object is as follows: + + - `minLibmongocryptVersion`: Optional string. The minimum libmongocrypt (inclusive) required to successfully run the + tests. If this field is omitted, there is no lower bound on the required libmongocrypt version. The format of this + string is defined in [Version String](#version-string). + +Test runners MAY evaluate these conditions in any order. For example, it may be more efficient to evaluate `serverless` +or `auth` before communicating with a server to check its version. + +#### entity + +An entity (e.g. client, collection, session object) that will be created in the [Entity Map](#entity-map) before each +test is executed. + +This object MUST contain **exactly one** top-level key that identifies the entity type and maps to a nested object, +which specifies a unique name for the entity (`id` key) and any other parameters necessary for its construction. Tests +SHOULD use sequential names based on the entity type (e.g. "session0", "session1"). + +When defining an entity object in YAML, a [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) SHOULD be created +on the entity's `id` key. This anchor will allow the unique name to be referenced with an +[alias node](https://yaml.org/spec/1.2/spec.html#id2786196) later in the file (e.g. from another entity or +[operation](#operation) object) and also leverage YAML's parser for reference validation. + +The structure of this object is as follows: + + + +- `client`: Optional object. Defines a MongoClient object. In addition to the configuration defined below, test runners + for drivers that implement connection pooling MUST track the number of connections checked out at any given time for + the constructed MongoClient. This can be done using a single counter and + [CMAP events](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events). Each + `ConnectionCheckedOutEvent` should increment the counter and each `ConnectionCheckedInEvent` should decrement it. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &client0 client0`). + + - `uriOptions`: Optional object. Additional URI options to apply to the test suite's connection string that is used to + create this client. Any keys in this object MUST override conflicting keys in the connection string. + + Documentation for supported options may be found in the [URI Options](../uri-options/uri-options.md) spec, with one + notable exception: if `readPreferenceTags` is specified in this object, the key will map to an array of strings, + each representing a tag set, since it is not feasible to define multiple `readPreferenceTags` keys in the object. + + Note also that when specifying `directConnection` as true, the connection string used to instantiate a client MUST + only have a single seed and MUST NOT specify the `replicaSet` option. See the + [URI Options spec](../uri-options/uri-options.md#directconnection-uri-option-with-multiple-seeds-or-srv-uri) for + more information. + + Any field in `uriOptions` may be a [$$placeholder](#placeholder) document and the test runner MUST support replacing + the placeholder document with values loaded from the test environment. For example: + + ```yaml + uriOptions: + authMechanism: "MONGODB-OIDC" + authMechanismProperties: + ENVIRONMENT: { $$placeholder: 1 } + ``` + + + + - `useMultipleMongoses`: Optional boolean. If true and the topology is a sharded cluster, the test runner MUST assert + that this MongoClient connects to multiple mongos hosts (e.g. by inspecting the connection string). If false and + the topology is a sharded cluster, the test runner MUST ensure that this MongoClient connects to only a single + mongos host (e.g. by modifying the connection string). + + If this option is not specified and the topology is a sharded cluster, the test runner MUST NOT enforce any limit on + the number of mongos hosts in the connection string and any tests using this client SHOULD NOT depend on a + particular number of mongos hosts. + + This option SHOULD be set to true in test files if the resulting entity is used to conduct transactions against a + sharded cluster. This is advised because connecting to multiple mongos servers is necessary to test session + pinning. + + If the topology type is `LoadBalanced` and Atlas Serverless is not being used, the test runner MUST use one of the + two load balancer URIs described in [Initializing the Test Runner](#initializing-the-test-runner) to configure the + MongoClient. If `useMultipleMongoses` is true or unset, the test runner MUST use the URI of the load balancer + fronting multiple servers. Otherwise, the test runner MUST use the URI of the load balancer fronting a single + server. + + If the topology type is `LoadBalanced` and Atlas Serverless is being used, this option has no effect. This is + because provisioning an Atlas Serverless instance yields a single URI (i.e. a load balancer fronting a single + Atlas Serverless proxy). + + This option has no effect for topologies that are not sharded or load balanced. + + + + - `observeEvents`: Optional array of one or more strings. Types of events that can be observed for this client. + Unspecified event types MUST be ignored by this client's event listeners and SHOULD NOT be included in + [test.expectEvents](#test_expectEvents) for this client. + + Supported types correspond to the top-level keys (strings) documented in [expectedEvent](#expectedevent) and are as + follows: + + - [commandStartedEvent](#expectedEvent_commandStartedEvent) + - [commandSucceededEvent](#expectedEvent_commandSucceededEvent) + - [commandFailedEvent](#expectedEvent_commandFailedEvent) + - [poolCreatedEvent](#expectedEvent_poolCreatedEvent) + - [poolReadyEvent](#expectedEvent_poolReadyEvent) + - [poolClearedEvent](#expectedEvent_poolClearedEvent) + - [poolClosedEvent](#expectedEvent_poolClosedEvent) + - [connectionCreatedEvent](#expectedEvent_connectionCreatedEvent) + - [connectionReadyEvent](#expectedEvent_connectionReadyEvent) + - [connectionClosedEvent](#expectedEvent_connectionClosedEvent) + - [connectionCheckOutStartedEvent](#expectedEvent_connectionCheckOutStartedEvent) + - [connectionCheckOutFailedEvent](#expectedEvent_connectionCheckOutFailedEvent) + - [connectionCheckedOutEvent](#expectedEvent_connectionCheckedOutEvent) + - [connectionCheckedInEvent](#expectedEvent_connectionCheckedInEvent) + - [serverDescriptionChangedEvent](#expectedEvent_serverDescriptionChangedEvent) + - [serverHeartbeatStartedEvent](#expectedEvent_serverHeartbeatStartedEvent) + - [serverHeartbeatSucceededEvent](#expectedEvent_serverHeartbeatSucceededEvent) + - [serverHeartbeatFailedEvent](#expectedEvent_serverHeartbeatFailedEvent) + - [topologyDescriptionChangedEvent](#expectedEvent_topologyDescriptionChangedEvent) + - [topologyOpeningEvent](#expectedEvent_topologyOpeningEvent) + - [topologyClosedEvent](#expectedEvent_topologyClosedEvent) + + + + - `ignoreCommandMonitoringEvents`: Optional array of one or more strings. Command names for which the test runner MUST + ignore any observed command monitoring events. The command(s) will be ignored in addition to `configureFailPoint` + and any commands containing sensitive information (per the + [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md#security) + spec) unless `observeSensitiveCommands` is true. + + Test files SHOULD NOT use this option unless one or more command monitoring events are specified in + [observeEvents](#entity_client_observeEvents). + + + + - `observeSensitiveCommands`: Optional boolean. If true, events associated with sensitive commands (per the + [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md#security) + spec) will be observed for this client. Note that the command and replies for such events will already have been + redacted by the driver. If false or not specified, events for commands containing sensitive information MUST be + ignored. Authentication SHOULD be disabled when this property is true, i.e. [auth](#runOnRequirement_auth) should + be false for each `runOnRequirement`. See + [rationale_observeSensitiveCommands](#rationale_observeSensitiveCommands). + + + + - `observeLogMessages`: Optional object where the key names are log [components](../logging/logging.md#components) and + the values are minimum [log severity levels](../logging/logging.md#log-severity-levels) indicating which + components to collect log messages for and what the minimum severity level of collected messages should be. + Messages for unspecified components and/or with lower severity levels than those specified MUST be ignored by this + client's log collector(s) and SHOULD NOT be included in [test.expectLogMessages](#test_expectLogMessages) for this + client. + + - `observeTracingMessages`: Optional object that configures tracing behavior for the client. The structure of this + object is as follows: + + - `enableCommandPayload`: Optional boolean. When set to `true`, enables capturing of command payload details in + tracing spans. + - If `true`, the test runner SHOULD capture detailed command payload information in tracing spans. + - If `false` or omitted, the test runner SHOULD exclude command payload details. + + - `serverApi`: Optional [serverApi](#serverapi) object. + + + + - `autoEncryptOpts`: Optional object corresponding to + [AutoEncryptionOpts](../client-side-encryption/client-side-encryption.md#mongoclient-changes) with the following + fields: + - `kmsProviders`: The same as in [`clientEncryption`](#entity_clientEncryption). + - `keyVaultNamespace`: The same as in [`clientEncryption`](#entity_clientEncryption). + - `bypassAutoEncryption`: Optional, a boolean to indicate whether or not auto encryption should be bypassed. + - `schemaMap`: Optional object. Maps namespaces to CSFLE schemas. + - `encryptedFieldsMap`: Optional object. Maps namespaces to QE schemas. + - `extraOptions`: Optional object. Configuration options for the encryption library. + - If `extraOptions` is not present or omits `cryptSharedLibPath`, test runners MAY set `cryptSharedLibPath` to the + path of [crypt_shared](../client-side-encryption/client-side-encryption.md#crypt_shared) being tested. This + can avoid test errors loading crypt_shared from different paths. + - `bypassQueryAnalysis`: Optional. Disables analysis of outgoing commands. Defaults to `false`. + - `keyExpirationMS`: The same as in [`clientEncryption`](#entity_clientEncryption). + + + + - `awaitMinPoolSizeMS`: Optional, integer. When specified, this parameter defines the maximum duration (in + milliseconds) that the test runner MUST wait for each connection pool to be populated with `minPoolSize` URI + option. Only connection pools for data-bearing nodes should be considered. If any connection pool is not populated + within the specified timeframe, the test runner MUST raise an error and mark the test as failed. Any CMAP and SDAM + event/log listeners configured on the client should ignore any events that occur before the pool is being + populated. If the parameter is omitted or if `minPoolSize` URI option is set to 0, no waiting is required for a + specific pool state prior to test execution. + + + +- `clientEncryption`: Optional object. Defines a ClientEncryption object. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. + `id: &clientEncryption0 clientEncryption0`). + + - `clientEncryptionOpts`: Required document. A value corresponding to a + [ClientEncryptionOpts](../client-side-encryption/client-side-encryption.md#clientencryption). + + Note: the `tlsOptions` document is intentionally omitted from the test format. However, drivers MAY internally + configure TLS options as needed to satisfy the requirements of configured KMS providers. + + The structure of this document is as follows: + + - `keyVaultClient`: Required string. Client entity from which this ClientEncryption will be created. The YAML file + SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a client entity's `id` field (e.g. + `client: *client0`). + + - `keyVaultNamespace`: Required string. The database and collection to use as the key vault collection for this + clientEncryption. The namespace takes the form `database.collection` (e.g. + `keyVaultNamespace: keyvault.datakeys`). + + - `kmsProviders`: Required document. Drivers MUST NOT configure a KMS provider if it is not given. This is to permit + testing conditions where a required KMS provider is not configured. If a KMS provider is given as an empty + document (e.g. `kmsProviders: { aws: {} }`), drivers MUST configure the KMS provider without credentials to + permit testing conditions where KMS credentials are needed. If a KMS credentials field has a placeholder value + (e.g. `kmsProviders: { aws: { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } }`), + drivers MUST replace the field with credentials that satisfy the operations required by the unified test files. + Drivers MAY load the credentials from the environment or a configuration file as needed to satisfy the + requirements of the given KMS provider and tests. If a KMS credentials field is not given (e.g. the required + field `secretAccessKey` is omitted in: `kmsProviders: { aws: { accessKeyId: { $$placeholder: 1 } }`), drivers + MUST NOT include the field during KMS configuration. This is to permit testing conditions where required KMS + credentials fields are not provided. Otherwise, drivers MUST configure the KMS provider with the explicit value + of KMS credentials field given in the test file (e.g. + `kmsProviders: { aws: { accessKeyId: abc, secretAccessKey: def } }`). This is to permit testing conditions where + invalid KMS credentials are provided. + + Tests may also reference named KMS providers. KMS providers with the name `name1` are expected to be configured + exactly as the unnamed KMS providers. The `aws:name2` KMS provider and `aws:name1` KMS providers deliberately + use separate AWS accounts that do not have permission to the other's keys. + + See the [Client-Side Encryption test README](../client-side-encryption/tests/README.md#credentials) for + instructions to obtain test credentials. + + - `keyExpirationMS`: Optional integer. The cache expiration time for data encryption keys. + + + +- `database`: Optional object. Defines a Database object. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &database0 database0`). + - `client`: Required string. Client entity from which this database will be created. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a client entity's `id` field (e.g. + `client: *client0`). + - `databaseName`: Required string. Database name. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. + `databaseName: &database0Name foo`). + - `databaseOptions`: Optional [collectionOrDatabaseOptions](#collectionordatabaseoptions) object. + + + +- `collection`: Optional object. Defines a Collection object. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &collection0 collection0`). + - `database`: Required string. Database entity from which this collection will be created. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a database entity's `id` field (e.g. + `database: *database0`). + - `collectionName`: Required string. Collection name. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. + `collectionName: &collection0Name foo`). + - `collectionOptions`: Optional [collectionOrDatabaseOptions](#collectionordatabaseoptions) object. + + + +- `session`: Optional object. Defines an explicit ClientSession object. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &session0 session0`). + + - `client`: Required string. Client entity from which this session will be created. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a client entity's `id` field (e.g. + `client: *client0`). + + - `sessionOptions`: Optional object. Map of parameters to pass to + [MongoClient.startSession](../sessions/driver-sessions.md#startsession) when creating the session. Supported + options are defined in the following specifications: + + - [Causal Consistency](../causal-consistency/causal-consistency.md#sessionoptions-changes) + - [Snapshot Reads](../sessions/snapshot-sessions.md#sessionoptions-changes) + - [Transactions](../transactions/transactions.md#sessionoptions-changes) + - [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md#sessions) + + When specifying TransactionOptions for `defaultTransactionOptions`, the transaction options MUST remain nested under + `defaultTransactionOptions` and MUST NOT be flattened into `sessionOptions`. + + + +- `bucket`: Optional object. Defines a Bucket object, as defined in the [GridFS](../gridfs/gridfs-spec.md) spec. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &bucket0 bucket0`). + - `database`: Required string. Database entity from which this bucket will be created. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a database entity's `id` field (e.g. + `database: *database0`). + - `bucketOptions`: Optional object. Additional options used to construct the bucket object. Supported options are + defined in the [GridFS](../gridfs/gridfs-spec.md#configurable-gridfsbucket-class) specification. The + `readConcern`, `readPreference`, and `writeConcern` options use the same structure as defined in + [Common Options](#common-options). + + + +- `thread`: Optional object. Defines a test runner "thread". Once the "thread" has been created, it should be idle and + waiting for operations to be dispatched to it later on by [runOnThread](#runonthread) operations. + + The structure of this object is as follows: + + - `id`: Required string. Unique name for this entity. The YAML file SHOULD define a + [node anchor](https://yaml.org/spec/1.2/spec.html#id2785586) for this field (e.g. `id: &thread0 thread0`). + +#### serverApi + +Declares an API version for a [client entity](#entity_client). + +The structure of this object is as follows: + +- `version`: Required string. Test runners MUST fail if the given version string is not supported by the driver. + + Note: the format of this string is unrelated to [Version String](#version-string). + +- `strict`: Optional boolean. + +- `deprecationErrors`: Optional boolean. + +See the [Stable API](../versioned-api/versioned-api.md) spec for more details on these fields. + +#### collectionData + +List of documents corresponding to the contents of a collection. This structure is used by both +[initialData](#initialData) and [test.outcome](#test_outcome), which insert and read documents, respectively. + +The structure of this object is as follows: + +- `collectionName`: Required string. See [commonOptions_collectionName](#commonOptions_collectionName). +- `databaseName`: Required string. See [commonOptions_databaseName](#commonOptions_databaseName). +- `createOptions`: Optional object. When used in [initialData](#initialData), these options MUST be passed to the + [create](https://docs.mongodb.com/manual/reference/command/create/) command when creating the collection. Test files + MUST NOT specify `writeConcern` in this options document as that could conflict with the use of the `majority` write + concern when the collection is created during test execution. +- `documents`: Required array of objects. List of documents corresponding to the contents of the collection. This list + may be empty. + +#### test + +Test case consisting of a sequence of operations to be executed. + +The structure of this object is as follows: + +- `description`: Required string. The name of the test. + + This SHOULD describe the purpose of this test (e.g. "insertOne is retried"). + + + +- `runOnRequirements`: Optional array of one or more [runOnRequirement](#runonrequirement) objects. List of server + version and/or topology requirements for which this test can be run. If specified, these requirements are evaluated + independently and in addition to any top-level [runOnRequirements](#runOnRequirements). If no requirements in this + array are met, the test runner MUST skip this test. + + These requirements SHOULD be more restrictive than those specified in the top-level + [runOnRequirements](#runOnRequirements) (if any) and SHOULD NOT be more permissive. This is advised because both + sets of requirements MUST be satisfied in order for a test to be executed and more permissive requirements at the + test-level could be taken out of context on their own. + + + +- `skipReason`: Optional string. If set, the test will be skipped. The string SHOULD explain the reason for skipping the + test (e.g. JIRA ticket). + + + +- `operations`: Required array of [operation](#operation) objects. List of operations to be executed for the test case. + + + +- `expectEvents`: Optional array of one or more [expectedEventsForClient](#expectedeventsforclient) objects. For one or + more clients, a list of events that are expected to be observed in a particular order. + + If a driver only supports configuring event listeners globally (for all clients), the test runner SHOULD associate + each observed event with a client in order to perform these assertions. + + Tests SHOULD NOT specify multiple [expectedEventsForClient](#expectedeventsforclient) objects for a single client + entity with the same `eventType` field. For example, a test containing two + [expectedEventsForClient](#expectedeventsforclient) objects with the `eventType` set to `cmap` for both would either + be redundant (if the `events` arrays were identical) or likely to fail (if the `events` arrays differed). + + + +- `expectLogMessages`: Optional array of one or more [expectedLogMessagesForClient](#expectedlogmessagesforclient) + objects. For one or more clients, a list of log messages that are expected to be observed in a particular order. + + If a driver only supports configuring log collectors globally (for all clients), the test runner SHOULD associate each + observed message with a client in order to perform these assertions. One possible implementation is to add a + test-only option to MongoClient which enables the client to store its entity name and add the entity name to each + log message to enable filtering messages by client. + + Tests SHOULD NOT specify multiple [expectedLogMessagesForClient](#expectedlogmessagesforclient) objects for a single + client entity. + + + +- `expectTracingMessages`: Optional object that defines expected tracing [spans](../open-telemetry/open-telemetry.md) + for a test. The structure of this object is as follows: + + - `client`: Required string. The ID of the client entity associated with these tracing spans. + + - `ignoreExtraSpans`: Optional boolean. + + - If `true`, additional unexpected spans are allowed. + - If `false` or omitted, the test runner MUST fail if any unexpected spans are detected. + + - `spans`: Required array of span objects. Each span describes an expected tracing event. + + Span object properties: + + - `name`: Required string. The name of the tracing span. + - `attributes`: Required object. Key-value pairs describing span metadata. + - `nested`: Optional array of nested span objects, following the same structure. + + + +- `outcome`: Optional array of one or more [collectionData](#collectiondata) objects. Data that is expected to exist in + collections after each test case is executed. + + The list of documents herein SHOULD be sorted ascendingly by the `_id` field to allow for deterministic comparisons. + The procedure for asserting collection contents is discussed in [Executing a Test](#executing-a-test). + +#### operation + +An operation to be executed as part of the test. + +The structure of this object is as follows: + + + +- `name`: Required string. Name of the operation (e.g. method) to perform on the object. + + + +- `object`: Required string. Name of the object on which to perform the operation. This SHOULD correspond to either an + [entity](#entity) name (for [Entity Test Operations](#entity-test-operations)) or "testRunner" (for + [Special Test Operations](#special-test-operations)). If the object is an entity, The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for its `id` field (e.g. `object: *collection0`). + + + +- `arguments`: Optional object. Map of parameter names and values for the operation. The structure of this object will + vary based on the operation. See [Entity Test Operations](#entity-test-operations) and + [Special Test Operations](#special-test-operations). + + The `session` parameter is handled specially (see [commonOptions_session](#commonOptions_session)). + + + +- `ignoreResultAndError`: Optional boolean. If true, both the error and result for the operation MUST be ignored. + + This field is mutually exclusive with [expectResult](#operation_expectResult), [expectError](#operation_expectError), + and [saveResultAsEntity](#operation_saveResultAsEntity). + + This field SHOULD NOT be used for [Special Test Operations](#special-test-operations) (i.e. `object: testRunner`). + + + +- `expectError`: Optional [expectedError](#expectederror) object. One or more assertions for an error expected to be + raised by the operation. + + This field is mutually exclusive with [expectResult](#operation_expectResult) and + [saveResultAsEntity](#operation_saveResultAsEntity). + + This field SHOULD NOT be used for [Special Test Operations](#special-test-operations) (i.e. `object: testRunner`). + + + +- `expectResult`: Optional mixed type. A value corresponding to the expected result of the operation. This field may be + a scalar value, a single document, or an array of values. Test runners MUST follow the rules in + [Evaluating Matches](#evaluating-matches) when processing this assertion. + + This field is mutually exclusive with [expectError](#operation_expectError). + + This field SHOULD NOT be used for [Special Test Operations](#special-test-operations) (i.e. `object: testRunner`). + + + +- `saveResultAsEntity`: Optional string. If specified, the actual result returned by the operation (if any) will be + saved with this name in the [Entity Map](#entity-map). The test runner MUST raise an error if the name is already in + use or if the result does not comply with [Supported Entity Types](#supported-entity-types). + + This field is mutually exclusive with [expectError](#operation_expectError). + + This field SHOULD NOT be used for [Special Test Operations](#special-test-operations) (i.e. `object: testRunner`). + +#### expectedError + +One or more assertions for an error/exception, which is expected to be raised by an executed operation. At least one key +is required in this object. + +The structure of this object is as follows: + +- `isError`: Optional boolean. If true, the test runner MUST assert that an error was raised. This is primarily used + when no other error assertions apply but the test still needs to assert an expected error. Test files MUST NOT + specify false, as [expectedError](#expectederror) is only applicable when an operation is expected to raise an + error. + +- `isClientError`: Optional boolean. If true, the test runner MUST assert that the error originates from the client + (i.e. it is not derived from a server response). If false, the test runner MUST assert that the error does not + originate from the client. + + Client errors include, but are not limited to: parameter validation errors before a command is sent to the server; + network errors. + +- `isTimeoutError`: Optional boolean. If true, the test runner MUST assert that the error represents a timeout due to + use of the `timeoutMS` option. If false, the test runner MUST assert that the error does not represent a timeout. + +- `errorContains`: Optional string. A substring of the expected error message (e.g. "errmsg" field in a server error + document). The test runner MUST assert that the error message contains this string using a case-insensitive match. + + See [bulkWrite](#bulkwrite) for special considerations for BulkWriteExceptions. + +- `errorCode`: Optional integer. The expected "code" field in the server-generated error response. The test runner MUST + assert that the error includes a server-generated response whose "code" field equals this value. In the interest of + readability, YAML files SHOULD use a comment to note the corresponding code name (e.g. + `errorCode: 26 # NamespaceNotFound`). + + Server error codes are defined in + [error_codes.yml](https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.yml). + + Test files SHOULD NOT assert error codes for client errors, as specifications do not define standardized codes for + client errors. + +- `errorCodeName`: Optional string. The expected "codeName" field in the server-generated error response. The test + runner MUST assert that the error includes a server-generated response whose "codeName" field equals this value + using a case-insensitive comparison. + + See [bulkWrite](#bulkwrite) for special considerations for BulkWriteExceptions. + + Server error codes are defined in + [error_codes.yml](https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.yml). + + Test files SHOULD NOT assert error codes for client errors, as specifications do not define standardized codes for + client errors. + +- `errorLabelsContain`: Optional array of one or more strings. A list of error label strings that the error is expected + to have. The test runner MUST assert that the error contains all of the specified labels (e.g. using the + `hasErrorLabel` method). + +- `errorLabelsOmit`: Optional array of one or more strings. A list of error label strings that the error is expected not + to have. The test runner MUST assert that the error does not contain any of the specified labels (e.g. using the + `hasErrorLabel` method). + +- `writeErrors`: Optional document. The write errors expected to be present in the error. The `writeErrors` document + contains numeric keys representing the index of the write that failed and `writeError` object values. The test + runner MUST assert that the error contains a `writeError` for each index present in `writeErrors` and MUST assert + that the `writeError`s match as root-level documents according to the rules in + [Evaluating Matches](#evaluating-matches). The test runner MUST assert that the error does not contain any + additional `writeError`s. This field is only intended for use with the [clientBulkWrite](#clientbulkwrite) + operation. + +- `writeConcernErrors`: Optional array of one or more objects. An ordered list of write concern errors expected to be + present in the error. The test runner MUST assert that each `writeConcernError` in this list matches the + `writeConcernError` present at the same index in the error's list of `writeConcernError`s as a root-level document + according to the rules in [Evaluating Matches](#evaluating-matches). The test runner MUST assert that the error does + not contain any additional `writeConcernErrors`s. This field is only intended for use with the + [clientBulkWrite](#clientbulkwrite) operation. + + + +- `errorResponse`: Optional document. A value corresponding to the expected server response. The test runner MUST assert + that the error includes a server response that matches this value as a root-level document according to the rules in + [Evaluating Matches](#evaluating-matches). + + Note that some drivers may not be able to evaluate `errorResponse` for write commands (i.e. insert, update, delete) + and bulk write operations. For example, a BulkWriteException is derived from potentially multiple server responses + and may not provide direct access to a single response. Tests SHOULD avoid using `errorResponse` for such operations + if possible; otherwise, affected drivers SHOULD skip such tests if necessary. + + + +- `expectResult`: Optional mixed type. This field follows the same rules as + [operation.expectResult](#operation_expectResult) and is only used in cases where the error includes a result (e.g. + [bulkWrite](#bulkwrite)). If specified, the test runner MUST assert that the error includes a result and that it + matches this value. If the result is optional (e.g. BulkWriteResult reported through the `writeResult` property of a + BulkWriteException), this assertion SHOULD utilize the [$$unsetOrMatches](#unsetormatches) operator. + +#### expectedEventsForClient + +A list of events that are expected to be observed (in that order) for a client while executing +[operations](#test_operations). + +The structure of each object is as follows: + +- `client`: Required string. Client entity on which the events are expected to be observed. See + [commonOptions_client](#commonOptions_client). +- `eventType`: Optional string. Specifies the type of the monitor which captured the events. Valid values are `command` + for [Command Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md#events-api) events, + `cmap` for [CMAP](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) events, and + `sdam` for + [SDAM](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + events. Defaults to `command` if omitted. +- `events`: Required array of [expectedEvent](#expectedevent) objects. List of events, which are expected to be observed + (in this order) on the corresponding client while executing [operations](#test_operations). If the array is empty, + the test runner MUST assert that no events were observed on the client (excluding ignored events). +- `ignoreExtraEvents`: Optional boolean. Specifies how the `events` array is matched against the observed events. If + `false`, observed events after all specified events have matched MUST cause a test failure; if `true`, observed + events after all specified events have been matched MUST NOT cause a test failure. Defaults to `false`. + +#### expectedEvent + +An event (e.g. APM), which is expected to be observed while executing the test's operations. + +This object MUST contain **exactly one** top-level key that identifies the event type and maps to a nested object, which +contains one or more assertions for the event's properties. + +Some event properties are omitted in the following structures because they cannot be reliably tested. Taking command +monitoring events as an example, `requestId` and `operationId` are nondeterministic and types for `connectionId` and +`failure` can vary by implementation. + +The events allowed in an `expectedEvent` object depend on the value of `eventType` in the corresponding +[expectedEventsForClient](#expectedeventsforclient) object, which can have one of the following values: + +- `command` or omitted: only the event types defined in [expectedCommandEvent](#expectedcommandevent) are allowed. +- `cmap`: only the event types defined in [expectedCmapEvent](#expectedcmapevent) are allowed. +- `sdam`: only the event types defined in [expectedSdamEvent](#expectedsdamevent) are allowed. + +##### expectedCommandEvent + +The structure of this object is as follows: + + + +- `commandStartedEvent`: Optional object. Assertions for one or more + [CommandStartedEvent](../command-logging-and-monitoring/command-logging-and-monitoring.md#events-api) fields. + + The structure of this object is as follows: + + - `command`: Optional document. A value corresponding to the expected command document. Test runners MUST follow the + rules in [Evaluating Matches](#evaluating-matches) when processing this assertion and match the value as a + root-level document. + - `commandName`: Optional string. Test runners MUST assert that the command name matches this value. + - `databaseName`: Optional string. Test runners MUST assert that the database name matches this value. The YAML file + SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for this value (e.g. + `databaseName: *database0Name`). + - `hasServiceId`: Defined in [hasServiceId](#hasserviceid). + - `hasServerConnectionId`: Defined in [hasServerConnectionId](#hasserverconnectionid). + + + +- `commandSucceededEvent`: Optional object. Assertions for one or more + [CommandSucceededEvent](../command-logging-and-monitoring/command-logging-and-monitoring.md#events-api) fields. + + The structure of this object is as follows: + + - `reply`: Optional document. A value corresponding to the expected reply document. Test runners MUST follow the rules + in [Evaluating Matches](#evaluating-matches) when processing this assertion and match the value as a root-level + document. + - `commandName`: Optional string. Test runners MUST assert that the command name matches this value. + - `databaseName`: Optional string. Test runners MUST assert that the database name matches this value. The YAML file + SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for this value (e.g. + `databaseName: *database0Name`). + - `hasServiceId`: Defined in [hasServiceId](#hasserviceid). + - `hasServerConnectionId`: Defined in [hasServerConnectionId](#hasserverconnectionid). + + + +- `commandFailedEvent`: Optional object. Assertions for one or more + [CommandFailedEvent](../command-logging-and-monitoring/command-logging-and-monitoring.md#events-api) fields. + + The structure of this object is as follows: + + - `commandName`: Optional string. Test runners MUST assert that the command name matches this value. + - `databaseName`: Optional string. Test runners MUST assert that the database name matches this value. The YAML file + SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for this value (e.g. + `databaseName: *database0Name`). + - `hasServiceId`: Defined in [hasServiceId](#hasserviceid). + - `hasServerConnectionId`: Defined in [hasServerConnectionId](#hasserverconnectionid). + +##### expectedCmapEvent + + + +- `poolCreatedEvent`: Optional object. If present, this object MUST be an empty document as all fields in this event are + non-deterministic. + + + +- `poolReadyEvent`: Optional object. If present, this object MUST be an empty document as all fields in this event are + non-deterministic. + + + +- `poolClearedEvent`: Optional object. Assertions for one or more + [PoolClearedEvent](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) fields. + + The structure of this object is as follows: + + - `hasServiceId`: Defined in [hasServiceId](#hasserviceid). + - `interruptInUseConnections`: Optional boolean. If specified, test runners MUST assert that the field is set and + matches this value. + + + +- `poolClosedEvent`: Optional object. If present, this object MUST be an empty document as all fields in this event are + non-deterministic. + + + +- `connectionCreatedEvent`: Optional object. If present, this object MUST be an empty document as all fields in this + event are non-deterministic. + + + +- `connectionReadyEvent`: Optional object. If present, this object MUST be an empty document as all fields in this event + are non-deterministic. + + + +- `connectionClosedEvent`: Optional object. Assertions for one or more + [ConnectionClosedEvent](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) fields. + + The structure of this object is as follows: + + - `reason`: Optional string. Test runners MUST assert that the reason in the published event matches this value. Valid + values for this field are defined in the CMAP spec. + + + +- `connectionCheckOutStartedEvent`: Optional object. If present, this object MUST be an empty document as all fields in + this event are non-deterministic. + + + +- `connectionCheckOutFailedEvent`: Optional object. Assertions for one or more + [ConnectionCheckOutFailedEvent](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) + fields. + + The structure of this object is as follows: + + - `reason`: Optional string. Test runners MUST assert that the reason in the published event matches this value. Valid + values for this field are defined in the CMAP spec. + + + +- `connectionCheckedOutEvent`: Optional object. If present, this object MUST be an empty document as all fields in this + event are non-deterministic. + + + +- `connectionCheckedInEvent`: Optional object. If present, this object MUST be an empty document as all fields in this + event are non-deterministic. + +##### expectedSdamEvent + +The structure of this object is as follows: + + + +- `serverDescriptionChangedEvent`: Optional object. Assertions for one or more + [ServerDescriptionChangedEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + fields. + + The structure of this object is as follows: + + - `previousDescription`: Optional object. A value corresponding to the server description as it was before the change + that triggered this event. + - `newDescription`: Optional object. A value corresponding to the server description as it was after the change that + triggered this event. + + The structure of a server description object (which the `previousDescription` and `newDescription` fields contain) is + as follows: + + - `type`: Optional string. The type of the server in the description. Test runners MUST assert that the type in the + published event matches this value. See + [SDAM: ServerType](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#servertype) for a list of + valid values. + + + +- `serverHeartbeatStartedEvent`: Optional object. Assertions for one or more + [ServerHeartbeatStartedEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + fields. + + The structure of this object is as follows: + + - `awaited`: Optional boolean. If specified, test runners MUST assert that the field is set and matches this value. + + + +- `serverHeartbeatSucceededEvent`: Optional object. Assertions for one or more + [ServerHeartbeatSucceededEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + fields. + + The structure of this object is as follows: + + - `awaited`: Optional boolean. If specified, test runners MUST assert that the field is set and matches this value. + + + +- `serverHeartbeatFailedEvent`: Optional object. Assertions for one or more + [ServerHeartbeatFailedEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + fields. + + The structure of this object is as follows: + + - `awaited`: Optional boolean. If specified, test runners MUST assert that the field is set and matches this value. + + + +- `topologyDescriptionChangedEvent`: Optional object. Assertions for one + [TopologyDescriptionChangedEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + object. + + The structure of this object is as follows: + + - `previousDescription`: Optional Object. A value corresponding to the topology description as it was before the + change that triggered the event. + + - `newDescription` : Optional Object. A value corresponding to the topology description as it is after the change that + triggered the event. + + Test runners SHOULD ignore any other fields than the `previousDescription` and `newDescription` fields. + + The structure of a topology description object (which the `previousDescription` and `newDescription` fields contain + is as follows: + + - `type`: Optional string. The type of the topology in the description. Test runners MUST assert that the type in + the published event matches this value. See + [SDAM: TopologyType](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#topologytype) for a + list of valid values. + + Test runners SHOULD ignore any other fields present on the `previousDescription` and `newDescription` fields of the + captured `topologyDescriptionChangedEvent`. + + + +- `topologyOpeningEvent`: Optional object. Assertions for one + [topologyOpeningEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + object. + + + +- `topologyClosedEvent`: Optional object. Assertions for one + [topologyClosedEvent](../server-discovery-and-monitoring/server-discovery-and-monitoring-logging-and-monitoring.md#events-api) + object. + +##### hasServiceId + +This field is an optional boolean that specifies whether or not the `serviceId` field of an event is set. If true, test +runners MUST assert that the field is set and is a non-empty BSON ObjectId (i.e. all bytes of the ObjectId are not 0). +If false, test runners MUST assert that the field is not set or is an empty BSON ObjectId. + +##### hasServerConnectionId + +This field is an optional boolean that specifies whether or not the `serverConnectionId` field of an event is set. If +true, test runners MUST assert that the field is set and is a positive Int32. If false, test runners MUST assert that +the field is not set, or, if the driver uses a nonpositive Int32 value to indicate the field being unset, MUST assert +that `serverConnectionId` is a nonpositive Int32. + +#### expectedLogMessagesForClient + +A list of log messages that are expected to be observed (in that order) for a client while executing +[operations](#test_operations). + +The structure of each object is as follows: + +- `client`: Required string. Client entity for which the messages are expected to be observed. See + [commonOptions_client](#commonOptions_client). +- `messages`: Required array of [expectedLogMessage](#expectedlogmessage) objects. List of messages, which are expected + to be observed (in this order) on the corresponding client while executing [operations](#test_operations). If the + array is empty, the test runner MUST assert that no messages were observed on the client. The driver MUST assert + that the messages produced are an exact match, i.e. that the expected and actual message counts are the same and + that there are no extra messages emitted by the client during the test run. Note: `ignoreMessages` and + `ignoreExtraMessages` may exclude log messages from this evaluation. +- `ignoreMessages`: Optional array of [expectedLogMessage](#expectedlogmessage) objects. Unordered set of messages, + which MUST be ignored on the corresponding client while executing [operations](#test_operations). The test runner + MUST exclude all log messages from observed messages that match any of the messages in `ignoreMessages` array before + `messages` evaluation. Matching rules used to match messages in `ignoreMessages` are identical to match rules used + for `messages` matching. +- `ignoreExtraMessages`: Optional boolean. Specifies how the `messages` array is matched against the observed logs. If + `false`, observed logs after all specified logs have matched MUST cause a test failure; if `true`, observed logs + after all specified logs have been matched MUST NOT cause a test failure. Defaults to `false`. + + + +#### expectedLogMessage + +A log message which is expected to be observed while executing the test's operations. + +The structure of each object is as follows: + +- `level`: Required string. This MUST be one of the level names listed in + [log severity levels](../logging/logging.md#log-severity-levels). This specifies the expected level for the log + message and corresponds to the level used for the message in the specification that defines it. Note that since not + all drivers will necessarily support all log levels, some driver may need to map the specified level to the + corresponding driver-supported level. Test runners MUST assert that the actual level matches this value. + +- `component`: Required string. This MUST be one of the component names listed in + [components](../logging/logging.md#components). This specifies the expected component for the log message. Note that + since naming variations are permitted for components, some drivers may need to map this to a corresponding + language-specific component name. Test runners MUST assert that the actual component matches this value. + +- `failureIsRedacted`: Optional boolean. This field SHOULD only be specified when the log message data is expected to + contain a `failure` value. + + When `failureIsRedacted` is present and its value is `true`, the test runner MUST assert that a failure is present and + that the failure has been redacted according to the rules defined for error redaction in the + [command logging and monitoring specification](../command-logging-and-monitoring/command-logging-and-monitoring.md#security). + + When `false`, the test runner MUST assert that a failure is present and that the failure has NOT been redacted. + + The exact form of these assertions and how thorough they are will vary based on the driver's chosen error + representation in logs; e.g. drivers that use strings may only be able to assert on the presence/absence of + substrings. + +- `data`: Required object. Contains key-value pairs that are expected to be attached to the log message. Test runners + MUST assert that the actual data contained in the log message matches the expected data, and MUST treat the log + message data as a root-level document. + + A suggested implementation approach is to decode `data` as a BSON document and serialize the data attached to each log + message to a BSON document, and match those documents. + + Note that for drivers that do not implement structured logging, this requires designing logging internals such that + data is first gathered in a structured form (e.g. a document or hashmap) which can be intercepted for testing + purposes. + +#### collectionOrDatabaseOptions + +Map of parameters used to construct a collection or database object. + +The structure of this object is as follows: + +- `readConcern`: Optional object. See [commonOptions_readConcern](#commonOptions_readConcern). +- `readPreference`: Optional object. See [commonOptions_readPreference](#commonOptions_readPreference). +- `timeoutMS`: Optional integer. See + [Client Side Operations Timeout](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms). +- `writeConcern`: Optional object. See [commonOptions_writeConcern](#commonOptions_writeConcern). + +### Common Options + +This section defines the structure of common options that are referenced from various contexts in the test format. +Comprehensive documentation for some of these types and their parameters may be found in the following specifications: + +- [Read and Write Concern](../read-write-concern/read-write-concern.md). +- [Server Selection: Read Preference](../server-selection/server-selection.md#read-preference). +- [Server Discovery and Monitoring: TopologyDescription](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#topologydescription). + +The structure of these common options is as follows: + + + +- `collectionName`: String. Collection name. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a collection entity's `collectionName` field (e.g. + `collectionName: *collection0Name`). + + + +- `databaseName`: String. Database name. The YAML file SHOULD use an + [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a database entity's `databaseName` field (e.g. + `databaseName: *database0Name`). + + + +- `readConcern`: Object. Map of parameters to construct a read concern. + + The structure of this object is as follows: + + - `level`: Required string. + + + +- `readPreference`: Object. Map of parameters to construct a read preference. + + The structure of this object is as follows: + + - `mode`: Required string. + - `tagSets`: Optional array of objects. + - `maxStalenessSeconds`: Optional integer. + - `hedge`: Optional object. + + + +- `client`: String. Client entity name, which the test runner MUST resolve to a MongoClient object. The YAML file SHOULD + use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a client entity's `id` field (e.g. + `client: *client0`). + + + +- `session`: String. Session entity name, which the test runner MUST resolve to a ClientSession object. The YAML file + SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for a session entity's `id` field (e.g. + `session: *session0`). + + + +- `writeConcern`: Object. Map of parameters to construct a write concern. + + The structure of this object is as follows: + + - `journal`: Optional boolean. + - `w`: Optional integer or string. + - `wtimeoutMS`: Optional integer. + +### Version String + +Version strings, which are used for [schemaVersion](#schemaVersion) and [runOnRequirement](#runonrequirement), MUST +conform to one of the following formats, where each component is a non-negative integer: + +- `..` +- `.` (`` is assumed to be zero) +- `` (`` and `` are assumed to be zero) + +Any component other than `major`, `minor`, and `patch` MUST be discarded prior to comparing versions. This is necessary +to ensure that spec tests run on pre-release versions of the MongoDB server. As an example, when checking if a server +with the version `4.9.0-alpha4-271-g7d5cf02` passes the requirement for a test, only `4.9.0` is relevant for the +comparison. When reading the server version from the `buildInfo` command reply, the three elements of the `versionArray` +field MUST be used, and all other fields MUST be discarded for this comparison. + +### Entity Test Operations + +Entity operations correspond to an API method on a driver object. If [operation.object](#operation_object) refers to an +[entity](#entity) name (e.g. "collection0") then [operation.name](#operation_name) is expected to reference an API +method on that class. + +Test files SHALL use camelCase when referring to API methods and parameters, even if the defining specifications use +other forms (e.g. snake_case in GridFS). Test files SHOULD use the exact API method names defined in specifications for +entity test operations. Test files MAY use a different descriptive name if a naming conflict occurs. For example, the +name "clientBulkWrite" is used for the client-level bulk write operation to differentiate it from the collection-level +bulk write operation. + +This spec does not provide exhaustive documentation for all possible API methods that may appear in a test; however, the +following sections discuss all supported entities and their operations in some level of detail. Special handling for +certain operations is also discussed as needed. + +#### Expressing Required and Optional Parameters + +Some specifications group optional parameters for API methods under an `options` parameter (e.g. +`options: Optional` in the CRUD spec); however, driver APIs vary in how they accept options (e.g. +Python's keyword/named arguments, `session` as either an option or required parameter depending on whether a language +supports method overloading). Therefore, test files SHALL declare all required and optional parameters for an API method +directly within [operation.arguments](#operation_arguments) (e.g. `upsert` for `updateOne` is *not* nested under an +`options` key). + +#### Special Handling for Arguments + +If `session` is specified in [operation.arguments](#operation_arguments), it is defined according to +[commonOptions_session](#commonOptions_session). Test runners MUST resolve the `session` argument to +[session](#entity_session) entity *before* passing it as a parameter to any API method. + +If `readConcern`, `readPreference`, or `writeConcern` are specified in [operation.arguments](#operation_arguments), test +runners MUST interpret them according to the corresponding definition in [Common Options](#common-options) and MUST +convert the value into the appropriate object *before* passing it as a parameter to any API method. + +#### Converting Returned Model Objects to Documents + +For operations that return a model object (e.g. `BulkWriteResult` for `bulkWrite`), the test runner MUST convert the +model object to a document when evaluating [expectResult](#operation_expectResult) or +[saveResultAsEntity](#operation_saveResultAsEntity). Similarly, for operations that may return iterables of model +objects (e.g. `DatabaseInfo` for `listDatabases`), the test runner MUST convert the iterable to an array of documents +when evaluating [expectResult](#operation_expectResult) or [saveResultAsEntity](#operation_saveResultAsEntity). + +#### Iterating Returned Iterables + +Unless otherwise stated by an operation below, test runners MUST fully iterate any iterable returned by an operation as +part of that operation's execution. This is necessary to ensure consistent behavior among drivers, as discussed in +[collection_aggregate](#collection_aggregate) and [find](#find), and also ensures that error and event assertions can be +evaluated consistently. + +### Client Operations + +These operations and their arguments may be documented in the following specifications: + +- [Change Streams](../change-streams/change-streams.md) +- [Enumerating Databases](../enumerate-databases/enumerate-databases.md) + +Client operations that require special handling or are not documented by an existing specification are described below. + +#### close + +Closes the client, i.e. close underlying connection pool(s) and cease monitoring the topology. For languages that rely +on built-in language mechanisms such as reference counting to automatically close/deinitialize clients once they go out +of scope, this may require implementing an abstraction to allow a client entity's underlying client to be set to null. +Because drivers do not consistently propagate errors encountered while closing a client, test files SHOULD NOT specify +[expectResult](#operation_expectResult) or [expectError](#operation_expectError) for this operation. Test files SHOULD +NOT specify any operations for a client entity or any entity descended from it following a `close` operation on it, as +driver behavior when an operation is attempted on a closed client or one of its descendant objects is not consistent. + + + +#### appendMetadata + +Appends client metadata to the client. + +The following arguments are supported: + +- `driverInfoOptions`: Required object with the following fields: + - `name`: Required string. The name of the wrapping library or framework. + - `version`: Optional string. The version of the wrapping library or framework. + - `platform`: Optional string. The platform of the wrapping library or framework. + +See the [handshake spec](../mongodb-handshake/handshake.md#metadata-updates-after-mongoclient-initialization) for more +details. + +#### createChangeStream + +Creates a cluster-level change stream and ensures that the server-side cursor has been created. + +This operation proxies the client's `watch` method and supports the same arguments and options. Test files SHOULD NOT +use the client's `watch` operation directly for reasons discussed in [ChangeStream](#entity_changestream). Test runners +MUST ensure that the server-side cursor is created (i.e. `aggregate` is executed) as part of this operation and before +the resulting change stream might be saved with [operation.saveResultAsEntity](#operation_saveResultAsEntity). + +Test runners MUST NOT iterate the change stream when executing this operation and test files SHOULD NOT specify +[operation.expectResult](#operation_expectResult) for this operation. + +#### clientBulkWrite + +These considerations only apply to the `MongoClient.bulkWrite` method. See [bulkWrite](#bulkwrite) for special +considerations for `MongoCollection.bulkWrite`. + +The `models` parameter for `clientBulkWrite` is documented as a list of WriteModel interfaces. Each WriteModel +implementation (e.g. InsertOneModel) provides important context to the method, but that type information is not easily +expressed in YAML and JSON. To account for this, test files MUST nest each WriteModel object in a single-key object, +where the key identifies the request type (e.g. "insertOne") and its value is an object expressing the parameters, as in +the following example: + +```yaml +arguments: + models: + - insertOne: + document: { _id: 1, x: 1 } + - replaceOne: + filter: { _id: 2 } + replacement: { x: 2 } + upsert: true + - updateOne: + filter: { _id: 3 } + update: { $set: { x: 3 } } + upsert: true + - updateMany: + filter: { } + update: { $inc: { x: 1 } } + - deleteOne: + filter: { x: 2 } + - deleteMany: + filter: { x: { $gt: 2 } } + ordered: true +``` + +Because the `insertResults`, `updateResults`, and `deleteResults` may be absent or empty in the `BulkWriteResult` +returned from a summary-only bulk write, the `clientBulkWrite` operation SHOULD use the +[$$unsetOrMatches](#unsetormatches) operator for assertions on these fields when `verboseResults` is not set to true. +This also applies to result objects defined in the `expectedResult` field of [expectedError](#expectederror). + +The `BulkWriteException` thrown by `MongoClient.bulkWrite` contains an optional `error` field that stores a top-level +error that occurred during the bulk write. Test runners MUST inspect the contents of this field when making assertions +based on the contents of the `errorCode` and `errorContains` fields in [expectedError](#expectederror). + +`BulkWriteException` also contains `writeErrors` and `writeConcernErrors` fields that define the individual write errors +and write concern errors that occurred during the bulk write. Unified tests SHOULD use `writeErrors` and +`writeConcernErrors` in `expectedError` to assert on the contents of these fields. Test runners MUST NOT inspect the +contents of these fields when making assertions based on any other fields defined in `expectedError`. + +While operations typically raise an error *or* return a result, the `MongoClient.bulkWrite` operation may report both +via the `partialResult` property of a `BulkWriteException`. In this case, the intermediary write result may be matched +with [expectedError_expectResult](#expectedError_expectResult) + +#### watch + +This operation SHOULD NOT be used in test files. See [client_createChangeStream](#client_createChangeStream). + +### ClientEncryption Operations + +These operations and their arguments may be documented in the following specifications: + +- [Client Side Encryption](../client-side-encryption/client-side-encryption.md) + +Operations that require sending and receiving KMS requests to encrypt or decrypt data keys may require appropriate KMS +credentials to be loaded by the driver. Drivers MUST load appropriate KMS credentials (i.e. from the environment or a +configuration file) when prompted by a test providing a placeholder value in a corresponding `kmsProviders` field as +described under [entity.clientEncryption](#entity_clientEncryption). + +See the [Client-Side Encryption test README](../client-side-encryption/tests/README.md#credentials) for instructions to +obtain test credentials. + +Drivers MUST be running the mock +[KMS KMIP server](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_kmip_server.py) +when evaluating tests that require KMS requests to a KMIP KMS provider. + +Drivers MAY enforce a unique index on `keyAltNames` as described in the +[Client Side Field Level Encryption spec](../client-side-encryption/client-side-encryption.md#why-arent-we-creating-a-unique-index-in-the-key-vault-collection) +when running key management operations on the key vault collection. Although unified tests are written assuming the +existence of the unique index, no unified test currently requires its implementation for correctness (e.g. no unified +test currently attempts to create a data key with an existing keyAltName or add an existing keyAltName to a data key). + +### Database Operations + +These operations and their arguments may be documented in the following specifications: + +- [Change Streams](../change-streams/change-streams.md) +- [CRUD](../crud/crud.md) +- [Enumerating Collections](../enumerate-collections/enumerate-collections.md) + +Database operations that require special handling or are not documented by an existing specification are described +below. + +#### aggregate + +When executing an `aggregate` operation, the test runner MUST fully iterate the result. This will ensure consistent +behavior between drivers that eagerly create a server-side cursor and those that do so lazily when iteration begins. + + + +#### createChangeStream + +Creates a database-level change stream and ensures that the server-side cursor has been created. + +This operation proxies the database's `watch` method and supports the same arguments and options. Test files SHOULD NOT +use the database's `watch` operation directly for reasons discussed in [ChangeStream](#entity_changestream). Test +runners MUST ensure that the server-side cursor is created (i.e. `aggregate` is executed) as part of this operation and +before the resulting change stream might be saved with [operation.saveResultAsEntity](#operation_saveResultAsEntity). + +Test runners MUST NOT iterate the change stream when executing this operation and test files SHOULD NOT specify +[operation.expectResult](#operation_expectResult) for this operation. + +#### listCollections + +When executing a `listCollections` operation, the test runner MUST fully iterate the resulting cursor. + +#### runCommand + +Generic command runner. + +This method does not inherit a read preference (per the +[Server Selection](../server-selection/server-selection.md#use-of-read-preferences-with-commands) spec); however, +`readPreference` may be specified as an argument. + +The following arguments are supported: + +- `command`: Required document. The command to be executed. +- `commandName`: Required string. The name of the command to run. This is used by languages that are unable preserve the + order of keys in the `command` argument when parsing YAML/JSON. +- `readPreference`: Optional object. See [commonOptions_readPreference](#commonOptions_readPreference). +- `session`: Optional string. See [commonOptions_session](#commonOptions_session). + +#### runCursorCommand + +[Generic cursor returning command runner](../run-command/run-command.md). + +This method does not inherit a read preference (per the +[Server Selection](../server-selection/server-selection.md#use-of-read-preferences-with-commands) spec); however, +`readPreference` may be specified as an argument. + +This operation proxies the database's `runCursorCommand` method and supports the same arguments and options (note: +handling for `getMore` options may vary by driver implementation). + +When executing the provided command, the test runner MUST fully iterate the cursor. This will ensure consistent behavior +between drivers that eagerly create a server-side cursor and those that do so lazily when iteration begins. + +The following arguments are supported: + +- `command`: Required document. The command to be executed. +- `commandName`: Required string. The name of the command to run. This is used by languages that are unable preserve the + order of keys in the `command` argument when parsing YAML/JSON. +- `readPreference`: Optional object. See [commonOptions_readPreference](#commonOptions_readPreference). +- `session`: Optional string. See [commonOptions_session](#commonOptions_session). +- `batchSize`: Optional positive integer value. Use this value to configure the `batchSize` option sent on subsequent + `getMore` commands. +- `maxTimeMS`: Optional non-negative integer value. Use this value to configure the `maxTimeMS` option sent on + subsequent `getMore` commands. +- `comment`: Optional BSON value. Use this value to configure the `comment` option sent on subsequent `getMore` + commands. +- `cursorType`: Optional string enum value, one of `'tailable' | 'tailableAwait' | 'nonTailable'`. Use this value to + configure the enum passed to the `cursorType` option. +- `timeoutMode`: Optional string enum value, one of `'iteration' | 'cursorLifetime'`. Use this value to configure the + enum passed to the `timeoutMode` option. + +#### createCommandCursor + +This operation proxies the database's `runCursorCommand` method and supports the same arguments and options (note: +handling for `getMore` options may vary by driver implementation). Test runners MUST ensure that the server-side cursor +is created (i.e. the command document has executed) as part of this operation and before the resulting cursor might be +saved with [operation.saveResultAsEntity](#operation_saveResultAsEntity). Test runners for drivers that lazily execute +the command on the first iteration of the cursor MUST iterate the resulting cursor once. The result from this iteration +MUST be used as the result for the first iteration operation on the cursor. + +Test runners MUST NOT iterate the resulting cursor when executing this operation and test files SHOULD NOT specify +[operation.expectResult](#operation_expectResult) for this operation. + +#### watch + +This operation SHOULD NOT be used in test files. See [database_createChangeStream](#database_createChangeStream). + +### Collection Operations + +These operations and their arguments may be documented in the following specifications: + +- [Change Streams](../change-streams/change-streams.md) +- [CRUD](../crud/crud.md) +- [Index Management](../index-management/index-management.md) + +Collection operations that require special handling or are not documented by an existing specification are described +below. + + + +#### aggregate + +When executing an `aggregate` operation, the test runner MUST fully iterate the result. This will ensure consistent +behavior between drivers that eagerly create a server-side cursor and those that do so lazily when iteration begins. + +When evaluating the iterated result for [expectResult](#operation_expectResult), the number of actual and expected +documents MUST be equal and each document value within the `expectResult` array MUST be matched as a root-level document +(for [Evaluating Matches](#evaluating-matches)). + +#### bulkWrite + +These considerations only apply to the `MongoCollection.bulkWrite` method. See [clientBulkWrite](#clientbulkwrite) for +special considerations for `MongoClient.bulkWrite`. + +The `requests` parameter for `bulkWrite` is documented as a list of WriteModel interfaces. Each WriteModel +implementation (e.g. InsertOneModel) provides important context to the method, but that type information is not easily +expressed in YAML and JSON. To account for this, test files MUST nest each WriteModel object in a single-key object, +where the key identifies the request type (e.g. "insertOne") and its value is an object expressing the parameters, as in +the following example: + +```yaml +arguments: + requests: + - insertOne: + document: { _id: 1, x: 1 } + - replaceOne: + filter: { _id: 2 } + replacement: { x: 2 } + upsert: true + - updateOne: + filter: { _id: 3 } + update: { $set: { x: 3 } } + upsert: true + - updateMany: + filter: { } + update: { $inc: { x: 1 } } + - deleteOne: + filter: { x: 2 } + - deleteMany: + filter: { x: { $gt: 2 } } + ordered: true +``` + +Because the `insertedIds` field of BulkWriteResult is optional for drivers to implement, assertions for that field +SHOULD utilize the [$$unsetOrMatches](#unsetormatches) operator. + +While operations typically raise an error *or* return a result, the `bulkWrite` operation is unique in that it may +report both via the `writeResult` property of a BulkWriteException. In this case, the intermediary write result may be +matched with [expectedError_expectResult](#expectedError_expectResult). Because `writeResult` is optional for drivers to +implement, such assertions SHOULD utilize the [$$unsetOrMatches](#unsetormatches) operator. + +Additionally, BulkWriteException is unique in that it aggregates one or more server errors in its `writeConcernError` +and `writeErrors` properties. When test runners evaluate [expectedError](#expectederror) assertions for `errorContains` +and `errorCodeName`, they MUST examine the aggregated errors and consider any match therein to satisfy the assertion(s). +Drivers that concatenate all write and write concern error messages into the BulkWriteException message MAY optimize the +check for `errorContains` by examining the concatenated message. Drivers that expose `code` but not `codeName` through +BulkWriteException MAY translate the expected code name to a number (see: +[error_codes.yml](https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.yml)) and compare with `code` +instead, but MUST raise an error if the comparison cannot be attempted (e.g. `code` is also not available, translation +fails). + + + +#### createChangeStream + +Creates a collection-level change stream and ensures that the server-side cursor has been created. + +This operation proxies the collection's `watch` method and supports the same arguments and options. Test files SHOULD +NOT use the collection's `watch` operation directly for reasons discussed in [ChangeStream](#entity_changestream). Test +runners MUST ensure that the server-side cursor is created (i.e. `aggregate` is executed) as part of this operation and +before the resulting change stream might be saved with [operation.saveResultAsEntity](#operation_saveResultAsEntity). + +Test runners MUST NOT iterate the change stream when executing this operation and test files SHOULD NOT specify +[operation.expectResult](#operation_expectResult) for this operation. + + + +#### createFindCursor + +This operation proxies the collection's `find` method and supports the same arguments and options. Test runners MUST +ensure that the server-side cursor is created (i.e. a `find` command is executed) as part of this operation and before +the resulting cursor might be saved with [operation.saveResultAsEntity](#operation_saveResultAsEntity). Test runners for +drivers that lazily execute the `find` command on the first iteration of the cursor MUST iterate the resulting cursor +once. The result from this iteration MUST be used as the result for the first iteration operation on the cursor. + +Test runners MUST NOT iterate the resulting cursor when executing this operation and test files SHOULD NOT specify +[operation.expectResult](#operation_expectResult) for this operation. + +#### createSearchIndex + +This operations proxies the collection's `createSearchIndex` helper with the same arguments. + +Each `createSearchIndex` operation receives a +[SearchIndexModel](../index-management/index-management.md#common-interfaces). If a driver has chosen to implement the +`createSearchIndex(name: String, definition: Document)` overload of `createSearchIndex`, then the `SearchIndexModel` +should be parsed by `createSearchIndex` unified test runner helper and the correct arguments should be passed into the +driver's helper. + +#### createSearchIndexes + +This operations proxies the collection's `createSearchIndexes` helper with the same arguments. + +#### dropSearchIndex + +This operation proxies the collection's `dropSearchIndex` helper with the same arguments. + +#### find + +When executing a `find` operation, the test runner MUST fully iterate the result. This will ensure consistent behavior +between drivers that eagerly create a server-side cursor and those that do so lazily when iteration begins. + +When evaluating the iterated result for [expectResult](#operation_expectResult), the number of actual and expected +documents MUST be equal and each document value within the `expectResult` array MUST be matched as a root-level document +(for [Evaluating Matches](#evaluating-matches)). + +#### findOneAndReplace and findOneAndUpdate + +The `returnDocument` option for `findOneAndReplace` and `findOneAndUpdate` is documented as an enum with possible values +"Before" and "After". Test files SHOULD express `returnDocument` as a string and test runners MUST raise an error if its +value does not case-insensitively match either enum value. + +When evaluating the result for [expectResult](#operation_expectResult), the document MUST be matched as a root-level +document (for [Evaluating Matches](#evaluating-matches)). + +#### insertMany + +The CRUD spec documents `insertMany` as returning a BulkWriteResult. Because the `insertedIds` field of BulkWriteResult +is optional for drivers to implement, assertions for that field SHOULD utilize the [$$unsetOrMatches](#unsetormatches) +operator. + +#### insertOne + +The CRUD spec documents `insertOne` as returning an InsertOneResult; however, because all fields InsertOneResult are +optional drivers are permitted to forgo it entirely and have `insertOne` return nothing (i.e. void method). Tests +asserting InsertOneResult SHOULD utilize the [$$unsetOrMatches](#unsetormatches) operator for *both* the result object +and any optional fields within, as in the following examples: + +```yaml +- name: insertOne + object: *collection0 + arguments: + document: { _id: 2 } + expectResult: + $$unsetOrMatches: + insertedId: { $$unsetOrMatches: 2 } +``` + +#### listSearchIndexes + +This operation proxies the collection's `listSearchIndexes` helper and returns the result of the cursor as a list. + +#### updateSearchIndex + +This operation proxies the collection's `updateSearchIndex` helper with the same arguments. + +#### watch + +This operation SHOULD NOT be used in test files. See [collection_createChangeStream](#collection_createChangeStream). + +### Session Operations + +These operations and their arguments may be documented in the following specifications: + +- [Convenient API for Transactions](../transactions-convenient-api/transactions-convenient-api.md) +- [Driver Sessions](../sessions/driver-sessions.md) + +Session operations that require special handling or are not documented by an existing specification are described below. + +#### withTransaction + +The `withTransaction` operation's `callback` parameter is a function and not easily expressed in YAML/JSON. For ease of +testing, this parameter is expressed as an array of [operation](#operation) objects (analogous to +[test.operations](#test_operations)). Test runners MUST evaluate error and result assertions when executing these +operations in the callback. + +Test runners MUST allow any errors from the callback operation(s) to propagate to `withTransaction`, irrespective of +[ignoreResultAndError](#operation_ignoreResultAndError) or [expectError](#operation_expectError). For example, if a +callback operation raises an error, it should be possible to assert that error at both the callback operation and +`withTransaction` level. + +#### getSnapshotTime + +The operation `getSnapshotTime` returns the value of `snapshotTime` on the session, so that it can be used in subsequent +operations. + +When parsing `snapshotTime` from `sessionOptions` for unified tests, the parsed string is the name of the key for the +actual value of `snapshotTime` to be found in the [Entity Map](#entity-map). + +### Bucket Operations + +These operations and their arguments may be documented in the following specifications: + +- [GridFS](../gridfs/gridfs-spec.md) + +Bucket operations that require special handling or are not documented by an existing specification are described below. + + + +#### download and downloadByName + +These operations proxy the bucket's `openDownloadStream` and `openDownloadStreamByName` methods and support the same +parameters and options, but return a string containing the stream's contents instead of the stream itself. Test runners +MUST fully read the stream to yield the returned string. This is also necessary to ensure that any expected errors are +raised (e.g. missing chunks). Test files SHOULD use [$$matchesHexBytes](#matcheshexbytes) in +[expectResult](#operation_expectResult) to assert the contents of the returned string. + +#### downloadToStream and downloadToStreamByName + +These operations SHOULD NOT be used in test files. See +[IO operations for GridFS streams](#io-operations-for-gridfs-streams) in [Future Work](#future-work). + +#### openDownloadStream and openDownloadStreamByName + +These operations SHOULD NOT be used in test files. See [download and downloadByName](#download-and-downloadbyname). + + + +#### openUploadStream and openUploadStreamWithId + +These operations SHOULD NOT be used in test files. See +[IO operations for GridFS streams](#io-operations-for-gridfs-streams) in [Future Work](#future-work). + + + +#### upload and uploadWithId + +These operations proxy the bucket's `uploadFromStream` and `uploadFromStreamWithId` methods and support the same +parameters and options with one exception: the `source` parameter is an object specifying hex bytes from which test +runners MUST construct a readable stream for the underlying methods. The structure of `source` is as follows: + +```yaml +{ $$hexBytes: } +``` + +The string MUST contain an even number of hexadecimal characters (case-insensitive) and MAY be empty. The test runner +MUST raise an error if the structure of `source` or its string is malformed. The test runner MUST convert the string to +a byte sequence denoting the stream's readable data (if any). For example, "12ab" would denote a stream with two bytes: +"0x12" and "0xab". + +#### uploadFromStream and uploadFromStreamWithId + +These operations SHOULD NOT be used in test files. See [upload and uploadWithId](#upload-and-uploadwithid). + +### Cursor Operations + +There are no defined APIs for change streams and cursors since the mechanisms for iteration may differ between +synchronous and asynchronous drivers. To account for this, this section explicitly defines the supported operations for +the `ChangeStream`, `FindCursor`, and `CommandCursor` entity types. + +Test runners MUST ensure that the iteration operations defined in this section will not inadvertently skip the first +document for a cursor. Albeit rare, this could happen if an operation were to blindly invoke `next` (or equivalent) on a +cursor in a driver where newly created cursors are already positioned at their first element and the cursor had a +non-empty `firstBatch`. Alternatively, some drivers may use a different iterator method for advancing a cursor to its +first position (e.g. `rewind` in PHP). + +#### iterateUntilDocumentOrError + +Iterates the cursor until either a single document is returned or an error is raised. This operation takes no arguments. +If [expectResult](#operation_expectResult) is specified, it SHOULD be a single document and MUST be matched as a +root-level document according to the rules in [Evaluating Matches](#evaluating-matches). + +Some specification sections (e.g. +[Iterating the Change Stream](../change-streams/tests/README.md#iterating-the-change-stream)) caution drivers that +implement a blocking mode of iteration (e.g. asynchronous drivers) not to iterate the cursor unnecessarily, as doing so +could cause the test runner to block indefinitely. This should not be a concern for `iterateUntilDocumentOrError` as +iteration only continues until either a document or error is encountered. + +#### iterateOnce + +Performs a single iteration of the cursor. If the cursor's current batch is empty, one `getMore` MUST be attempted to +get more results. This operation takes no arguments. If [expectResult](#operation_expectResult) is specified, it SHOULD +be a single document. + +Due to the non-deterministic nature of some cursor types (e.g. change streams on sharded clusters), test files SHOULD +only use this operation to perform command monitoring assertions on the `getMore` command. Tests that perform assertions +about the result of iteration should use [iterateUntilDocumentOrError](#iterateuntildocumentorerror) instead. + +#### close + +Closes the cursor. Because drivers do not consistently propagate errors from the `killCursors` command, test runners +MUST suppress all errors when closing the cursor. Test files SHOULD NOT specify [expectResult](#operation_expectResult) +or [expectError](#operation_expectError) for this operation. To assert whether the `killCursors` command succeeded or +failed, test files SHOULD use command monitoring assertions with +[commandSucceededEvent](#expectedEvent_commandSucceededEvent) and +[commandFailedEvent](#expectedEvent_commandFailedEvent) events. + +### Special Test Operations + +Certain operations do not correspond to API methods but instead represent special test operations (e.g. assertions). +These operations are distinguished by [operation.object](#operation_object) having a value of "testRunner". The +[operation.name](#operation_name) field will correspond to an operation defined below. + +Special test operations return no result and are always expected to succeed. These operations SHOULD NOT be combined +with [expectError](#operation_expectError), [expectResult](#operation_expectResult), or +[saveResultAsEntity](#operation_saveResultAsEntity). + +#### failPoint + +The `failPoint` operation instructs the test runner to configure a fail point using a "primary" read preference using +the specified client entity (fail points are not configured using an internal MongoClient). + +The following arguments are supported: + +- `failPoint`: Required document. The `configureFailPoint` command to be executed. + +- `client`: Required string. See [commonOptions_client](#commonOptions_client). + + The client entity SHOULD specify false for [useMultipleMongoses](#entity_client_useMultipleMongoses) if this operation + could be executed on a sharded topology (according to [runOnRequirements](#runOnRequirements) or + [test.runOnRequirements](#test_runOnRequirements)). This is advised because server selection rules for mongos could + lead to unpredictable behavior if different servers were selected for configuring the fail point and executing + subsequent operations. + +When executing this operation, the test runner MUST keep a record of the fail point so that it can be disabled after the +test. The test runner MUST also ensure that the `configureFailPoint` command is excluded from the list of observed +command monitoring events for this client (if applicable). + +An example of this operation follows: + +```yaml +# Enable the fail point on the server selected with a primary read preference +- name: failPoint + object: testRunner + arguments: + client: *client0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + closeConnection: true +``` + +#### targetedFailPoint + +The `targetedFailPoint` operation instructs the test runner to configure a fail point on a specific mongos. + +The following arguments are supported: + +- `failPoint`: Required document. The `configureFailPoint` command to be executed. + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). + + The client entity associated with this session SHOULD specify true for + [useMultipleMongoses](#entity_client_useMultipleMongoses). This is advised because targeted fail points are intended + to test mongos pinning, which warrants multiple mongoses. + +The mongos on which to set the fail point is determined by the `session` argument (after resolution to a session +entity). Test runners MUST error if the session is not pinned to a mongos server at the time this operation is executed. + +If the driver exposes an API to target a specific server for a command, the test runner SHOULD use the client entity +associated with the session to execute the `configureFailPoint` command. In this case, the test runner MUST also ensure +that this command is excluded from the list of observed command monitoring events for this client (if applicable). If +such an API is not available, but the test runner creates an internal MongoClient for each mongos, the test runner +SHOULD use the internal MongoClient corresponding to the session's pinned server for this operation. Otherwise, test +runners MUST create a new MongoClient that is directly connected to the session's pinned server for this operation. The +new MongoClient instance MUST be closed once the command has finished executing. + +A test utilizing `targetedFailPoint` SHOULD NOT be permitted execute on a load balancer (according to +[runOnRequirements](#runOnRequirements) or [test.runOnRequirements](#test_runOnRequirements)). This is advised because +the `configureFailPoint` command does not use the session directly, which means there is no guarantee that the load +balancer will direct the command to the intended mongos. + +When executing this operation, the test runner MUST keep a record of both the fail point and pinned mongos server so +that the fail point can be disabled on the same mongos server after the test. + +An example of this operation follows: + +```yaml +# Enable the fail point on the mongos to which session0 is pinned +- name: targetedFailPoint + object: testRunner + arguments: + session: *session0 + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["commitTransaction"] + closeConnection: true +``` + +Since mongos pinning only applies when multiple mongoses are used, tests SHOULD NOT use `targetedFailPoint` unless the +session's MongoClient is configured with `useMultipleMongoses: true`. + +#### assertSessionTransactionState + +The `assertSessionTransactionState` operation instructs the test runner to assert that the given session has a +particular transaction state. + +The following arguments are supported: + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). +- `state`: Required string. Expected transaction state for the session. Possible values are as follows: `none`, + `starting`, `in_progress`, `committed`, and `aborted`. + +An example of this operation follows: + +```yaml +- name: assertSessionTransactionState + object: testRunner + arguments: + session: *session0 + state: in_progress +``` + +#### assertSessionPinned + +The `assertSessionPinned` operation instructs the test runner to assert that the given session is pinned to a mongos +server. + +The following arguments are supported: + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). + +An example of this operation follows: + +```yaml +- name: assertSessionPinned + object: testRunner + arguments: + session: *session0 +``` + +#### assertSessionUnpinned + +The `assertSessionUnpinned` operation instructs the test runner to assert that the given session is not pinned to a +mongos server. + +The following arguments are supported: + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). + +An example of this operation follows: + +```yaml +- name: assertSessionUnpinned + object: testRunner + arguments: + session: *session0 +``` + +#### assertDifferentLsidOnLastTwoCommands + +The `assertDifferentLsidOnLastTwoCommands` operation instructs the test runner to assert that the last two +CommandStartedEvents observed on the client have different `lsid` fields. This assertion is primarily used to test that +dirty server sessions are discarded from the pool. + +The following arguments are supported: + +- `client`: Required string. See [commonOptions_client](#commonOptions_client). + + The client entity SHOULD include "commandStartedEvent" in [observeEvents](#entity_client_observeEvents). + +The test runner MUST fail this assertion if fewer than two CommandStartedEvents have been observed on the client or if +either command does not include an `lsid` field. + +An example of this operation follows: + +```yaml +- name: assertDifferentLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 +``` + +#### assertSameLsidOnLastTwoCommands + +The `assertSameLsidOnLastTwoCommands` operation instructs the test runner to assert that the last two +CommandStartedEvents observed on the client have identical `lsid` fields. This assertion is primarily used to test that +non-dirty server sessions are not discarded from the pool. + +The following arguments are supported: + +- `client`: Required string. See [commonOptions_client](#commonOptions_client). + + The client entity SHOULD include "commandStartedEvent" in [observeEvents](#entity_client_observeEvents). + +The test runner MUST fail this assertion if fewer than two CommandStartedEvents have been observed on the client or if +either command does not include an `lsid` field. + +An example of this operation follows: + +```yaml +- name: assertSameLsidOnLastTwoCommands + object: testRunner + arguments: + client: *client0 +``` + +#### assertSessionDirty + +The `assertSessionDirty` operation instructs the test runner to assert that the given session is marked dirty. + +The following arguments are supported: + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). + +An example of this operation follows: + +```yaml +- name: assertSessionDirty + object: testRunner + arguments: + session: *session0 +``` + +#### assertSessionNotDirty + +The `assertSessionNotDirty` operation instructs the test runner to assert that the given session is not marked dirty. + +The following arguments are supported: + +- `session`: Required string. See [commonOptions_session](#commonOptions_session). + +An example of this operation follows: + +```yaml +- name: assertSessionNotDirty + object: testRunner + arguments: + session: *session0 +``` + +#### assertCollectionExists + +The `assertCollectionExists` operation instructs the test runner to assert that the given collection exists in the +database. The test runner MUST use an internal MongoClient for this operation. + +The following arguments are supported: + +- `collectionName`: Required string. See [commonOptions_collectionName](#commonOptions_collectionName). +- `databaseName`: Required string. See [commonOptions_databaseName](#commonOptions_databaseName). + +An example of this operation follows: + +```yaml +- name: assertCollectionExists + object: testRunner + arguments: + collectionName: *collection0Name + databaseName: *database0Name +``` + +Use a `listCollections` command to check whether the collection exists. Note that it is currently not possible to run +`listCollections` from within a transaction. + +#### assertCollectionNotExists + +The `assertCollectionNotExists` operation instructs the test runner to assert that the given collection does not exist +in the database. The test runner MUST use an internal MongoClient for this operation. + +The following arguments are supported: + +- `collectionName`: Required string. See [commonOptions_collectionName](#commonOptions_collectionName). +- `databaseName`: Required string. See [commonOptions_databaseName](#commonOptions_databaseName). + +An example of this operation follows: + +```yaml +- name: assertCollectionNotExists + object: testRunner + arguments: + collectionName: *collection0Name + databaseName: *database0Name +``` + +Use a `listCollections` command to check whether the collection exists. Note that it is currently not possible to run +`listCollections` from within a transaction. + +#### assertIndexExists + +The `assertIndexExists` operation instructs the test runner to assert that an index with the given name exists on the +collection. The test runner MUST use an internal MongoClient for this operation. + +The following arguments are supported: + +- `collectionName`: Required string. See [commonOptions_collectionName](#commonOptions_collectionName). +- `databaseName`: Required string. See [commonOptions_databaseName](#commonOptions_databaseName). +- `indexName`: Required string. Index name. + +An example of this operation follows: + +```yaml +- name: assertIndexExists + object: testRunner + arguments: + collectionName: *collection0Name + databaseName: *database0Name + indexName: t_1 +``` + +Use a `listIndexes` command to check whether the index exists. Note that it is currently not possible to run +`listIndexes` from within a transaction. + +#### assertIndexNotExists + +The `assertIndexNotExists` operation instructs the test runner to assert that an index with the given name does not +exist on the collection. The test runner MUST use an internal MongoClient for this operation. + +The following arguments are supported: + +- `collectionName`: Required string. See [commonOptions_collectionName](#commonOptions_collectionName). +- `databaseName`: Required string. See [commonOptions_databaseName](#commonOptions_databaseName). +- `indexName`: Required string. Index name. + +An example of this operation follows: + +```yaml +- name: assertIndexNotExists + object: testRunner + arguments: + collectionName: *collection0Name + databaseName: *database0Name + indexName: t_1 +``` + +Use a `listIndexes` command to check whether the index exists. Note that it is currently not possible to run +`listIndexes` from within a transaction. + + + +#### createEntities + +The `createEntities` operation instructs the test runner to create the provided entities and store them in the current +test's [Entity Map](#entity-map). + +- `entities`: Required array of one or more [entity](#entity) objects. As with the file-level + [createEntities](#createentities) directive, test files SHOULD declare entities in dependency order, such that all + referenced entities are defined before any of their dependent entities. + +An example of this operation follows: + +```yaml +- name: createEntities + object: testRunner + arguments: + entities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name test +``` + +#### assertNumberConnectionsCheckedOut + +The `assertNumberConnectionsCheckedOut` operation instructs the test runner to assert that the given number of +connections are currently checked out for the specified client entity. This operation only applies to drivers that +implement connection pooling and should be skipped for drivers that do not. + +The following arguments are supported: + +- `client`: Required string. See [commonOptions_client](#commonOptions_client). +- `connections`: Required integer. The number of connections expected to be checked out. + +An example of this operation follows: + +```yaml +- name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 1 +``` + +#### runOnThread + +The `runOnThread` operation instructs the test runner to schedule an operation to be run on a given thread. The given +thread MUST begin executing the operation immediately. `runOnThread` MUST NOT wait for the operation to complete. If any +of the operation's test assertions fail, the entire test case MUST fail as well. + +When writing test cases that use `runOnThread`, it's important to note that certain entities are not concurrency-safe +(e.g. sessions, cursors) and therefore SHOULD NOT be used in operations on multiple different threads entities. + +The following arguments are supported: + +- `thread`: Required string. Thread entity on which this operation should be scheduled. +- `operation`: Required [operation](#operation) object. The operation to schedule on the thread. This object must be a + valid operation as described in [Entity Test Operations](#entity-test-operations). + +An example of this operation follows: + +```yaml +- name: runOnThread + object: testRunner + arguments: + thread: *thread0 + operation: + name: insertOne + object: *collection0 + arguments: + document: { _id: 2 } + expectResult: + $$unsetOrMatches: + insertedId: { $$unsetOrMatches: 2 } +``` + +#### waitForThread + +The `waitForThread` operation instructs the test runner to notify the given thread that no more operations are +forthcoming, wait for it to complete its last operation, and assert that it exited without any errors. + +If the `waitForThread` operation is not satisfied after 10 seconds, this operation MUST cause a test failure. + +The `test.operations` list SHOULD contain a `waitForThread` operation for each thread entity that the test creates. + +The following arguments are supported: + +- `thread`: Required string. Thread entity that should be stopped and awaited for completion. + +An example of this operation follows: + +```yaml +- name: waitForThread + object: testRunner + arguments: + thread: *thread0 +``` + +#### waitForEvent + +The `waitForEvent` operation instructs the test runner to wait until the specified MongoClient has published a specific, +matching event a given number of times. Note that this includes any events published before the `waitForEvent` operation +started. + +If the `waitForEvent` operation is not satisfied after 10 seconds, this operation MUST cause a test failure. + +The following arguments are supported: + +- `client`: Required string. Client entity whose events the runner will be waiting for. +- `event`: Required [expectedEvent](#expectedevent) object. The event which the test runner is waiting to be observed on + the provided client. The assertions laid out in [expectedEvent](#expectedevent) MUST be used to determine if an + observed event matches `event`. `event` SHOULD have an event type that was included in the `client`'s + `observeEvents` field. +- `count`: Required integer. The number of matching events to wait for before resuming execution of subsequent + operations. + +For example, the following instructs the test runner to wait for at least one poolClearedEvent to be published: + +```yaml +- name: waitForEvent + object: testRunner + arguments: + client: *client0 + event: + poolClearedEvent: {} + count: 1 +``` + +#### assertEventCount + +The `assertEventCount` operation instructs the test runner to assert the specified MongoClient has published a specific, +matching event a given number of times so far in the test. + +The following arguments are supported: + +- `client`: Required string. Client entity whose events the runner will be counting. +- `event`: Required [expectedEvent](#expectedevent) object. The event which the test runner will be counting on the + provided client. The assertions laid out in [expectedEvent](#expectedevent) MUST be used to determine if an observed + event matches `event`. `event` SHOULD have an event type that was included in the `client`'s `observeEvents` field. +- `count`: Required integer. The exact number of matching events that `client` should have seen so far. + +For example, the following instructs the test runner to assert that a single PoolClearedEvent was published: + +```yaml +- name: assertEventCount + object: testRunner + arguments: + client: *client0 + event: + poolClearedEvent: {} + count: 1 +``` + +#### recordTopologyDescription + +The `recordTopologyDescription` operation instructs the test runner to retrieve the specified MongoClient's current +[TopologyDescription](#entity_topologydescription) and store it in the [Entity Map](#entity-map). + +The following arguments are supported: + +- `client`: Required string. Client entity whose TopologyDescription will be recorded. +- `id`: Required string. The name with which the TopologyDescription will be stored in the [Entity Map](#entity-map). + +For example: + +```yaml +- name: recordTopologyDescription + object: testRunner + arguments: + client: *client0 + id: &postInsertTopology postInsertTopology +``` + +#### assertTopologyType + +The `assertTopologyType` operation instructs the test runner to assert that the given +[TopologyDescription](#entity_topologydescription) has a particular TopologyType. + +The following arguments are supported: + +- `topologyDescription`: Required string. TopologyDescription entity whose TopologyType will be inspected. +- `topologyType`: Required string. Expected TopologyType for the TopologyDescription. See + [SDAM: TopologyType](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#topologytype) for a list + of possible values. + +For example: + +```yaml +- name: assertTopologyType + object: testRunner + arguments: + topologyDescription: *postInsertTopology + topologyType: ReplicaSetWithPrimary +``` + +#### waitForPrimaryChange + +The `waitForPrimaryChange` operation instructs the test runner to wait until the provided MongoClient discovers a +different primary from the one in the provided [TopologyDescription](#entity_topologydescription). If the provided +TopologyDescription does not include a primary, then this operation will wait until the client discovers any primary. + +The following arguments are supported: + +- `client`: Required string. See [commonOptions_client](#commonOptions_client). + + The client entity MUST be the same one from which `topologyDescription` was derived. Test runners do not need to + verify this. + +- `priorTopologyDescription`: Required string. The name of a [TopologyDescription](#entity_topologydescription) entity + which will be used to determine if the primary has changed or not. + +- `timeoutMS`: Optional integer. The number of milliseconds to wait for the primary change before timing out and failing + the test. If unspecified, a default timeout of 10 seconds MUST be used. + +For example: + +```yaml +- name: waitForPrimaryChange + object: testRunner + arguments: + client: *client0 + priorTopologyDescription: *postInsertTopology + timeoutMS: 1000 +``` + +#### wait + +The `wait` operation instructs the test runner to sleep for a provided number of milliseconds. + +The following arguments are supported: + +- `ms`: Required integer. The number of milliseconds for which the current test runner thread should sleep. + +For example: + +```yaml +- name: wait + object: testRunner + arguments: + ms: 1000 +``` + +### Special Placeholder Value + +#### $$placeholder + +Syntax: + +```yaml +{ field: { $$placeholder: 1 } } +``` + +This special key-value pair can be used anywhere the value for a key might be specified in an test file. It is intended +to act as a placeholder value in contexts where the test runner cannot provide a definite value or may be expected to +replace the placeholder with a value that cannot be specified by the test file (e.g. KMS provider credentials). The test +runner MUST raise an error if a placeholder value is used in an unexpected context or a replacement cannot be made. + +An example of using this placeholder value follows: + +```yaml +kmsProviders: + aws: + accessKeyId: { $$placeholder: 1 } + privateAccessKey: { $$placeholder: 1 } +``` + +Note: the test runner is not required to validate the type or value of a `$$placeholder` field. + +### Evaluating Matches + +Expected values in tests (e.g. [operation.expectResult](#operation_expectResult)) are expressed as either relaxed or +canonical [Extended JSON](../extended-json/extended-json.md). + +The algorithm for matching expected and actual values is specified with the following pseudo-code: + +```c +function match (expected, actual): + if expected is a document: + // handle special operators (e.g. $$type) + if first and only key of expected starts with "$$": + execute any assertion(s) for the special operator + return + + assert that actual is a document + + for every key/value in expected: + // handle key-based operators (e.g. $$exists, $$unsetOrMatches) + if value is a document and its first and only key starts with "$$": + execute any assertion(s) for the special operator + continue to the next iteration unless actual value must be matched + + assert that actual[key] exists + assert that actual[key] matches value + + if expected is not the root document: + assert that actual does not contain additional keys + + return + + if expected is an array: + assert that actual is an array + assert that actual and expected have the same number of elements + + for every index/value in expected: + assert that actual[index] matches value + + return + + // expected is neither a document nor array + assert that actual and expected are the same type, noting flexible numerics + assert that actual and expected are equal +``` + +The rules for comparing documents and arrays are discussed in more detail in subsequent sections. When comparing types +*other* than documents and arrays, test runners MAY adopt any of the following approaches to compare expected and actual +values, as long as they are consistent: + +- Convert both values to relaxed or canonical [Extended JSON](../extended-json/extended-json.md) and compare strings +- Convert both values to BSON, and compare bytes +- Convert both values to native representations, and compare accordingly + +When comparing types that contain documents as internal properties (e.g. CodeWScope), the rules in +[Evaluating Matches](#evaluating-matches) do not apply and the documents MUST match exactly; however, test runners MUST +permit variation in document key order or otherwise normalize the documents before comparison. + +#### Flexible Numeric Comparisons + +When comparing numeric types (excluding Decimal128), test runners MUST consider 32-bit, 64-bit, and floating point +numbers to be equal if their values are numerically equivalent. For example, an expected value of `1` would match an +actual value of `1.0` (e.g. `ok` field in a server response) but would not match `1.5`. + +#### Allowing Extra Fields in Root-level Documents + +When matching root-level documents, test runners MUST permit the actual document to contain additional fields not +present in the expected document. + +For example, the following documents match: + +```yaml +expected: { x: 1 } +actual: { x: 1, y: 1 } +``` + +The inverse is not true. For example, the following documents do not match: + +```yaml +expected: { x: 1, y: 1 } +actual: { x: 1 } +``` + +Test runners MUST NOT permit additional fields in nested documents. For example, the following documents do not match: + +```yaml +expected: { x: { y: 1 } } +actual: { x: { y: 1, z: 1 } } +``` + +It may be helpful to think of expected documents as a form of query criteria. The intention behind this rule is that it +is not always feasible or relevant for a test to exhaustively specify all fields in an expected document (e.g. cluster +time in `command` for [CommandStartedEvent](#expectedEvent_commandStartedEvent)). + +When the expected value is an array, test runners MUST differentiate between an array of values, which may be documents, +(e.g. `distinct`) and an array of root-level documents derived from an iterable result (e.g. `find`, `aggregate`). For +example, the following array of documents would not match if returned by `distinct`, but would match if returned via +`find` (after iterating the cursor): + +```yaml +expected: [ { x: 1 }, { x: 2 } ] +actual: [ { x: 1, y: 1 }, { x: 2, y: 2 } ] +``` + +##### Examples of Root-level Documents + +Contexts where one might encounter a root-level document include: + +- `command` for [CommandStartedEvent](#expectedEvent_commandStartedEvent) + +- `reply` for [CommandSucceededEvent](#expectedEvent_commandSucceededEvent) + +- [expectResult](#operation_expectResult) for operations that return a document. Operations include: + + - findOneAndDelete, [findOneAndReplace](#findoneandreplace-and-findoneandupdate), and + [findOneAndUpdate](#findoneandreplace-and-findoneandupdate) + - [iterateUntilDocumentOrError](#iterateuntildocumentorerror) + - [iterateOnce](#iterateonce) + +- [expectResult](#operation_expectResult) array elements for operations that return an iterable of documents. Operations + include: + + - [aggregate](#aggregate) + - [find](#find) + - [listCollections](#listcollections), listDatabases, and listIndexes + - [listSearchIndexes](#listsearchindexes) + - [runCursorCommand](#runcursorcommand) + +- [errorResponse](#expectedError_errorResponse) for [expectedError](#expectederror) + +- `writeErrors` object values for [expectedError](#expectederror) + +- `writeConcernErrors` array elements for [expectedError](#expectederror) + +- `data` for [expectedLogMessage](#expectedlogmessage) + +#### Document Key Order Variation + +When matching documents, test runners MUST NOT require keys in the expected and actual document to appear in the same +order. For example, the following documents would match: + +```yaml +expected: { x: 1, y: 1 } +actual: { y: 1, x: 1 } +``` + +#### Arrays Must Contain the Same Number of Elements + +When comparing arrays, expected and actual values MUST contain the same number of elements. For example, the following +arrays corresponding to a `distinct` operation result would not match: + +```yaml +expected: [ 1, 2, 3 ] +actual: [ 1, 2, 3, 4 ] +``` + +#### Special Operators for Matching Assertions + +When matching expected and actual values, an equality comparison is not always sufficient. For instance, a test file +cannot anticipate what a session ID will be at runtime, but may still want to analyze the contents of an `lsid` field in +a command document. To address this need, special operators can be used. + +These operators are objects with a single key identifying the operator. Such keys are prefixed with `$$` to ease in +detecting an operator (test runners need only inspect the first key of each object) and differentiate the object from +MongoDB query operators, which use a single `$` prefix. The key will map to some value that influences the operator's +behavior (if applicable). + +When examining the structure of an expected value during a comparison, test runners MUST check if the value is an object +whose first and only key starts with `$$` and, if so, defer to the special logic defined in this section. + +##### $$exists + +Syntax: + +```yaml +{ field: { $$exists: } } +``` + +This operator can be used anywhere the value for a key might be specified in an expected document. If true, the test +runner MUST assert that the key exists in the actual document, irrespective of its value (e.g. a key with a `null` value +would match). If false, the test runner MUST assert that the key does not exist in the actual document. This operator is +modeled after the [$exists](https://www.mongodb.com/docs/manual/reference/operator/query/exists/) query operator. + +An example of this operator checking for a field's presence follows: + +```yaml +command: + getMore: { $$exists: true } + collection: *collectionName, + batchSize: 5 +``` + +An example of this operator checking for a field's absence follows: + +```yaml +command: + update: *collectionName + updates: [ { q: {}, u: { $set: { x: 1 } } } ] + ordered: true + writeConcern: { $$exists: false } +``` + +##### $$type + +Syntax: + +```yaml +{ $$type: } +{ $$type: [ , , ... ] } +``` + +This operator can be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)). The +test runner MUST assert that the actual value exists and matches one of the expected types, which correspond to the +documented string types for the [$type](https://www.mongodb.com/docs/manual/reference/operator/query/type/) query +operator. + +An example of this operator follows: + +```yaml +command: + getMore: { $$type: [ "int", "long" ] } + collection: { $$type: "string" } +``` + +When the actual value is an array, test runners MUST NOT examine types of the array's elements. Only the type of actual +field SHALL be checked. This is admittedly inconsistent with the behavior of the +[$type](https://www.mongodb.com/docs/manual/reference/operator/query/type/) query operator, but there is presently no +need for this behavior in tests. + +##### $$matchesEntity + +Syntax, where `entityName` is a string: + +```yaml +{ $$matchesEntity: } +``` + +This operator can be used to reference a BSON entity anywhere a matched value is expected (including +[expectResult](#operation_expectResult)). If the BSON entity is defined in the current test's [Entity Map](#entity-map), +the test runner MUST fetch that entity and assert that the actual value matches the entity using the standard rules in +[Evaluating Matches](#evaluating-matches); otherwise, the test runner MUST raise an error for an undefined or mistyped +entity. The YAML file SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) for the entity name. + +This operator does not influence whether or not an actual document value is considered a root-level document. + +This operator is primarily used to assert identifiers for uploaded GridFS files. + +An example of this operator follows: + +```yaml +operations: + - + object: *bucket0 + name: upload + arguments: + filename: "filename" + source: { $$hexBytes: "12AB" } + expectResult: { $$type: "objectId" } + saveResultAsEntity: &objectid0 "objectid0" + - object: *filesCollection + name: find + arguments: + sort: { uploadDate: -1 } + limit: 1 + expectResult: + - _id: { $$matchesEntity: *objectid0 } +``` + +##### $$matchesHexBytes + +Syntax, where `hexBytes` is an even number of hexadecimal characters (case-insensitive) and MAY be empty: + +```yaml +{ $$matchesHexBytes: } +``` + +This operator can be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)) and +the actual value is a string. The test runner MUST raise an error if the `hexBytes` string is malformed. This operator +is primarily used to assert the results of [download](#download) and [downloadByName](#downloadByName), which return +stream contents as a string. + +##### $$unsetOrMatches + +Syntax: + +```yaml +{ $$unsetOrMatches: } +``` + +This operator can be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)), +excluding an array element because +[Arrays Must Contain the Same Number of Elements](#arrays-must-contain-the-same-number-of-elements). The test runner +MUST assert that the actual value either does not exist or matches the expected value. Matching the expected value MUST +use the standard rules in [Evaluating Matches](#evaluating-matches), which means that it may contain special operators. + +This operator does not influence whether or not an actual document value is considered a root-level document. + +This operator is primarily used to assert driver-optional fields from the CRUD spec (e.g. `insertedId` for +InsertOneResult, `writeResult` for BulkWriteException). + +An example of this operator used for a result's field follows: + +```yaml +expectResult: + insertedId: { $$unsetOrMatches: 2 } +``` + +An example of this operator used for an entire result follows: + +```yaml +expectError: + expectResult: + $$unsetOrMatches: + deletedCount: 0 + insertedCount: 2 + matchedCount: 0 + modifiedCount: 0 + upsertedCount: 0 + upsertedIds: { } +``` + +##### $$sessionLsid + +Syntax: + +```yaml +{ $$sessionLsid: } +``` + +This operator can be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)). If +the [session entity](#entity_session) is defined in the current test's [Entity Map](#entity-map), the test runner MUST +assert that the actual value equals its logical session ID; otherwise, the test runner MUST raise an error for an +undefined or mistyped entity. The YAML file SHOULD use an [alias node](https://yaml.org/spec/1.2/spec.html#id2786196) +for a session entity's `id` field (e.g. `session: *session0`). + +An example of this operator follows: + +```yaml +command: + ping: 1 + lsid: { $$sessionLsid: *session0 } +``` + +##### $$lte + +Syntax: + +```yaml +{ $$lte: 5 } +``` + +This operator can be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)). The +test runner MUST assert that the actual value is less than or equal to the specified value. Test runners MUST also apply +the rules specified in [Flexible Numeric Comparisons](#flexible-numeric-comparisons) for this operator. For example, an +expected value of `1` would match an actual value of `1.0` and `0.0` but would not match `1.1`. + +##### $$matchAsDocument + +Syntax: + +```yaml +{ $$matchAsDocument: } +``` + +This operator may be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)) and +the actual value is an extended JSON string. The test runner MUST parse the actual value into a BSON document and match +it against the expected value. + +Matching the expected value MUST use the standard rules in [Evaluating Matches](#evaluating-matches), which means that +it may contain special operators. The actual value MUST NOT be considered a root-level document (i.e. extra fields are +not permitted); however, [$$matchAsRoot](#matchasroot) may be used to change that behavior. + +This operator is primarily used to assert on extended JSON commands and command replies included in log messages. + +##### $$matchAsRoot + +This operator may be used anywhere a matched value is expected (including [expectResult](#operation_expectResult)) and +the expected and actual values are documents. The test runner MUST treat the actual value as a root-level document as +described in [Evaluating Matches](#evaluating-matches) and match it against the expected value. + +### Test Runner Implementation + +The sections below describe instructions for instantiating the test runner, loading each test file, and executing each +test within a test file. Test runners MUST NOT share state created by processing a test file with the processing of +subsequent test files, and likewise for tests within a test file. + +#### Initializing the Test Runner + +The test runner MUST be configurable with a connection string (or equivalent configuration), which will be used to +initialize any internal MongoClient(s) and any [client entities](#entity_client) (in combination with other URI +options). This specification is not prescriptive about how this information is provided. For example, it may be read +from an environment variable or configuration file. + +Create a new MongoClient, which will be used for internal operations (e.g. processing [initialData](#initialData) and +[test.outcome](#test_outcome)). This is referred to elsewhere in the specification as the internal MongoClient. If this +MongoClient would connect multiple mongos nodes and the driver does not provide a way to target operations to specific +servers, the test runner MAY construct internal MongoClients for each mongos. + +Determine the server version and topology type using an internal MongoClient. This information will be used to evaluate +any future [runOnRequirement](#runonrequirement) checks. Test environments SHOULD NOT use mixed version clusters, so it +is not necessary to check multiple servers. + +In addition to the aforementioned connection string, the test runner MUST also be configurable with two other connection +strings (or equivalent configuration) that point to TCP load balancers: one fronting multiple servers and one fronting a +single server. These will be used to initialize client entities when executing tests against a load balanced sharded +cluster. If the topology type is `LoadBalanced` and Atlas Serverless is not being used, the test runner MUST error if +either of these URIs is not provided. When testing against other topology types or Atlas Serverless, these URIs SHOULD +NOT be provided and MUST be ignored if provided. + +The test runner SHOULD terminate any open transactions (see: +[Terminating Open Transactions](#terminating-open-transactions)) using the internal MongoClient(s) before executing any +tests. + +#### Executing a Test File + +The instructions in this section apply for each test file loaded by the test runner. + +Test files, which may be YAML or JSON files, MUST be interpreted using an +[Extended JSON](../extended-json/extended-json.md) parser. The parser MUST accept relaxed and canonical Extended JSON +(per [Extended JSON: Parsers](../extended-json/extended-json.md#parsers)), as test files may use either. + +Upon loading a file, the test runner MUST read the [schemaVersion](#schemaVersion) field and determine if the test file +can be processed further. Test runners MAY support multiple versions and MUST NOT process incompatible files (as +discussed in [Test Runner Support](#test-runner-support)). If a test file is incompatible, test runners MUST raise an +error and MAY do so by reporting a test failure. Test runners MAY make an effort to infer the number of tests (and their +descriptions) from an incompatible file and report a failure for each test. + +If [runOnRequirements](#runOnRequirements) is specified, the test runner MUST skip the test file unless one or more +[runOnRequirement](#runonrequirement) objects are satisfied. + +For each element in [tests](#tests), follow the process in [Executing a Test](#executing-a-test). + +#### Executing a Test + +The instructions in this section apply for each [test](#test) occurring in a test file loaded by the test runner. After +processing a test, test runners MUST reset any internal state that resulted from doing so. For example, the +[Entity Map](#entity-map) created for one test MUST NOT be shared with another. + +If at any point while executing this test an unexpected error is encountered or an assertion fails, the test runner MUST +consider this test to have failed and SHOULD continue with the instructions in this section to ensure that the test +environment is cleaned up (e.g. disable fail points, kill sessions) while also forgoing any additional assertions. + +If [test.skipReason](#test_skipReason) is specified, the test runner MUST skip this test and MAY use the string value to +log a message. + +If [test.runOnRequirements](#test_runOnRequirements) is specified, the test runner MUST skip the test unless one or more +[runOnRequirement](#runonrequirement) objects are satisfied. + +If [initialData](#initialData) is specified, for each [collectionData](#collectiondata) therein the test runner MUST set +up the collection. All setup operations MUST use the internal MongoClient and a "majority" write concern. The test +runner MUST first drop the collection. The test runner must also drop the collections `enxcol_..esc` and +`enxcol_..ecoc`. If a `createOptions` document is present, the test runner MUST execute a `create` +command to create the collection with the specified options. The test runner MUST then insert the specified documents +(if any). If no documents are present and `createOptions` is not set, the test runner MUST create the collection. If the +topology is sharded, the test runner SHOULD use a single mongos for handling [initialData](#initialData) to avoid +possible runtime errors. + +If the test might execute a transaction against a sharded deployment, the test runner MUST collect the latest cluster +time from processing [initialData](#initialData). See +[MigrationConflict Errors on Sharded Clusters](#migrationconflict-errors-on-sharded-clusters) for more information. + +Create a new [Entity Map](#entity-map) that will be used for this test. If [createEntities](#createentities) is +specified, the test runner MUST create each [entity](#entity) accordingly and add it to the map. If the topology is a +sharded cluster, the test runner MUST handle [useMultipleMongoses](#entity_client_useMultipleMongoses) accordingly if it +is specified for any client entities. If the topology type is `LoadBalanced`, client entities MUST be initialized with +the appropriate load balancer connection string as discussed in +[useMultipleMongoses](#entity_client_useMultipleMongoses). + +If the test might execute a transaction against a sharded deployment, the test runner MUST advance the cluster time of +any session entities created during the test using the cluster time collected from processing +[initialData](#initialData). See +[MigrationConflict Errors on Sharded Clusters](#migrationconflict-errors-on-sharded-clusters) for more information. + +If the test might execute a `distinct` command within a sharded transaction, for each target collection the test runner +SHOULD execute a non-transactional `distinct` command on each mongos server using an internal MongoClient. See +[StaleDbVersion Errors on Sharded Clusters](#staledbversion-errors-on-sharded-clusters) for more information. + +If the test might execute a `configureFailPoint` command, for each target client the test runner MAY specify a reduced +value for `heartbeatFrequencyMS` (and `minHeartbeatFrequencyMS` if possible) to speed up SDAM recovery time and server +selection after a failure; however, test runners MUST NOT do so for any client that specifies `heartbeatFrequencyMS` in +its `uriOptions`. + +For each client entity where [observeEvents](#entity_client_observeEvents) has been specified, the test runner MUST +enable all event listeners necessary to collect the specified event types. Test runners MAY leave event listeners +disabled for tests that do not assert events (i.e. tests that omit both [test.expectEvents](#test_expectEvents) and +special operations such as [assertSameLsidOnLastTwoCommands](#assertsamelsidonlasttwocommands)). + +For each client with command monitoring enabled, the test runner MUST ignore events for the following: + +- Any command(s) specified in [ignoreCommandMonitoringEvents](#entity_client_ignoreCommandMonitoringEvents). +- Any `configureFailPoint` commands executed for [failPoint](#failpoint) and [targetedFailPoint](#targetedfailpoint) + operations. +- Any commands containing sensitive information (per the + [Command Logging and Monitoring](../command-logging-and-monitoring/command-logging-and-monitoring.md#security) spec) + unless [observeSensitiveCommands](#entity_client_observeSensitiveCommands) is true. Note that drivers will redact + commands and replies for sensitive commands. For `hello` and legacy hello, which are conditionally sensitive based + on the presence of a `speculativeAuthenticate` field, the test runner may need to infer that the events are + sensitive based on whether or not the command and reply documents are redacted (i.e. empty documents). + +For each element in [test.operations](#test_operations), follow the process in +[Executing an Operation](#executing-an-operation). If an unexpected error is encountered or an assertion fails, the test +runner MUST consider this test to have failed. + +If any event listeners were enabled on any client entities, the test runner MUST now disable those event listeners. + +If any fail points were configured, the test runner MUST now disable those fail points (on the same server) to avoid +spurious failures in subsequent tests. For any fail points configured using [targetedFailPoint](#targetedfailpoint), the +test runner MUST disable the fail point on the same mongos server on which it was originally configured. See +[Disabling Fail Points](#disabling-fail-points) for more information. + +If [test.expectEvents](#test_expectEvents) is specified, for each object therein the test runner MUST assert that the +number and sequence of expected events match the number and sequence of actual events observed on the specified client. +If the list of expected events is empty, the test runner MUST assert that no events were observed on the client. The +process for matching events is described in [expectedEvent](#expectedevent). + +If [test.outcome](#test_outcome) is specified, for each [collectionData](#collectiondata) therein the test runner MUST +assert that the collection contains exactly the expected data. The test runner MUST query each collection using the +internal MongoClient, an ascending sort order on the `_id` field (i.e. `{ _id: 1 }`), a "primary" read preference, and a +"local" read concern. When comparing collection data, the rules in [Evaluating Matches](#evaluating-matches) do not +apply and the documents MUST match exactly; however, test runners MUST permit variations in document key order or +otherwise normalize the documents before comparison. If the list of documents is empty, the test runner MUST assert that +the collection is empty. + +Before clearing the entity map at the end of each test, the test runner MUST allow its entities to be accessed +externally. The exact mechanism for facilitating this access is not prescribed by this specification, but drivers should +be mindful of concurrency if applicable. As an example, the test runner MAY be configured with a callback method, which +will be invoked at the end of each test and provided with the entity map (or an equivalent data structure). As +previously discussed in [Entity Map](#entity-map), test runners MAY restrict access to driver objects if necessary. + +Clear the entity map for this test. For each ClientSession in the entity map, the test runner MUST end the session (e.g. +call [endSession](../sessions/driver-sessions.md#endsession)). For each ChangeStream and FindCursor in the entity map, +the test runner MUST close the cursor. + +If the test started a transaction (i.e. executed a `startTransaction` or `withTransaction` operation), the test runner +MUST terminate any open transactions (see: [Terminating Open Transactions](#terminating-open-transactions)). + +Proceed to the subsequent test. + +#### Executing an Operation + +The instructions in this section apply for each [operation](#operation) occurring in a [test](#test) contained within a +test file loaded by the test runner. + +If at any point while executing an operation an unexpected error is encountered or an assertion fails, the test runner +MUST consider the parent test to have failed and proceed from [Executing a Test](#executing-a-test) accordingly. + +If [operation.object](#operation_object) is "testRunner", this is a special operation. If +[operation.name](#operation_name) is defined in [Special Test Operations](#special-test-operations), the test runner +MUST execute the operation accordingly and, if successful, proceed to the next operation in the test; otherwise, the +test runner MUST raise an error for an undefined operation. The test runner MUST keep a record of any fail points +configured by special operations so that they may be disabled after the current test. + +If [operation.object](#operation_object) is not "testRunner", this is an entity operation. If +[operation.object](#operation_object) is defined in the current test's [Entity Map](#entity-map), the test runner MUST +fetch that entity and note its type; otherwise, the test runner MUST raise an error for an undefined entity. If +[operation.name](#operation_name) does not correspond to a known operation for the entity type (per +[Entity Test Operations](#entity-test-operations)), the test runner MUST raise an error for an unsupported operation. +Test runners MAY skip tests that include operations that are intentionally unimplemented (e.g. `listCollectionNames`). + +Proceed with preparing the operation's arguments. If `session` is specified in +[operation.arguments](#operation_arguments), the test runner MUST resolve it to a session entity and MUST raise an error +if the name is undefined or maps to an unexpected type. If a key in [operation.arguments](#operation_arguments) does not +correspond to a known parameter/option for the operation, the test runner MUST raise an error for an unsupported +argument. + +Before executing the operation, the test runner MUST be prepared to catch a potential error from the operation (e.g. +enter a `try` block). Proceed with executing the operation and capture its result or error. + +Note that some operations require special handling, as discussed in [Entity Test Operations](#entity-test-operations). +For example, model objects may need to be converted to documents (before matching or saving in the entity map) and +returned iterables may need to be fully iterated. + +If [operation.ignoreResultAndError](#operation_ignoreResultAndError) is true, the test runner MUST NOT make any +assertions regarding the result or error of the operation and MUST proceed to the subsequent operation. + +If [operation.expectError](#operation_expectError) is specified, the test runner MUST assert that the operation yielded +an error; otherwise, the test runner MUST assert that the operation did not yield an error. If an error was expected, +the test runner MUST evaluate any assertions in [expectedError](#expectederror) accordingly. + +If [operation.expectResult](#operation_expectResult) is specified, the test MUST assert that it matches the actual +result of the operation according to the rules outlined in [Evaluating Matches](#evaluating-matches). + +If [operation.saveResultAsEntity](#operation_saveResultAsEntity) is specified, the test runner MUST store the result in +the current test's entity map using the specified name. If the operation did not return a result or the result does not +comply with [Supported Entity Types](#supported-entity-types) then the test runner MUST raise an error. + +After asserting the operation's error and/or result and optionally saving the result, proceed to the subsequent +operation. + +#### Special Procedures + +This section describes some procedures that may be referenced from earlier sections. + +##### Terminating Open Transactions + +Open transactions can cause tests to block indiscriminately. When connected to MongoDB 3.6 or later, test runners SHOULD +terminate all open transactions at the start of a test suite and after each failed test by killing all sessions in the +cluster. Using the internal MongoClient(s), execute the `killAllSessions` command on either the primary or, if connected +to a sharded cluster, each mongos server. + +For example: + +```javascript +db.adminCommand({ + killAllSessions: [] +}); +``` + +The test runner MAY ignore the following command failures: + +- Interrupted(11601) to work around [SERVER-38335](https://jira.mongodb.org/browse/SERVER-38335). +- Unauthorized(13) to work around [SERVER-54216](https://jira.mongodb.org/browse/SERVER-54216). +- CommandNotFound(59) if the command is executed on a pre-3.6 server + +Note that Atlas, by design, does not allow database users to kill sessions belonging to other users. This makes it +impossible to guarantee that an existing transaction will not block test execution. To work around this, test runners +SHOULD either ignore Unauthorized(13) command failures or avoid calling `killAllSessions` altogether when connected to +Atlas (e.g. by detecting `mongodb.net` in the hostname or allowing the test runner to be configured externally). + +##### MigrationConflict Errors on Sharded Clusters + +Following [SERVER-82353](https://jira.mongodb.org/browse/SERVER-82353), drivers may encounter MigrationConflict errors +when executing transactions on sharded clusters due to an outdated cluster time. For example: + +```text +Transaction fe0b9a7d-4b64-4e38-aad2-4c7dd5ab0c2b - 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - - :1 was aborted on statement 0 due to: a non-retryable snapshot error :: caused by :: Encountered error from localhost:27217 during a transaction :: caused by :: Database transaction-tests has undergone a catalog change operation at time Timestamp(1708062645, 94) and no longer satisfies the requirements for the current transaction which requires Timestamp(1708062645, 93). Transaction will be aborted. +``` + +To work around this issue, a test runner MUST collect the latest cluster time from processing +[initialData](#initialData), which is executed using the internal MongoClient, and use it to advance the cluster time of +every session entity created during the test, by either [createEntities](#createentities) or the +[special test operation](#operation_createEntities)). This will ensure that the cluster time is gossipped to any other +mongo hosts that may be used to execute a transaction during the test. + +Depending on the test runner implementation, the cluster time may be collected in various ways. For example: + +- Execute a `ping` command against the same mongos host used for [initialData](#initialData) and read the `$clusterTime` + response field +- Execute all operations for [initialData](#initialData) using an explicit session and read its cluster time after the + last operation. + +This logic is only necessary for tests that might execute a transaction against a sharded deployment transaction (i.e. +[test.operations](#test_operations) includes `startTransaction` or `withTransaction` and the topology is sharded or +load-balanced). To ease the implementation, test runners MAY apply this logic to *every* test. + +##### StaleDbVersion Errors on Sharded Clusters + +When a shard receives its first command that contains a `databaseVersion`, the shard returns a StaleDbVersion error and +mongos retries the operation. In a sharded transaction, mongos does not retry these operations and instead returns the +error to the client. For example: + +```text +Command distinct failed: Transaction aa09e296-472a-494f-8334-48d57ab530b6:1 was aborted on statement 0 due to: an error from cluster data placement change :: caused by :: got stale databaseVersion response from shard sh01 at host localhost:27217 :: caused by :: don't know dbVersion. +``` + +To work around this limitation, a test runners MUST execute a non-transactional `distinct` command on each mongos server +before running any test that might execute `distinct` within a transaction. To ease the implementation, test runners MAY +execute `distinct` before *every* test. + +Test runners can remove this workaround once [SERVER-39704](https://jira.mongodb.org/browse/SERVER-39704) is resolved, +after which point mongos should retry the operation transparently. The `distinct` command is the only command allowed in +a sharded transaction that uses the `databaseVersion` concept so it is the only command affected. + +### Server Fail Points + +Many tests utilize the `configureFailPoint` command to trigger server-side errors such as dropped connections or command +errors. Tests can configure fail points using the special [failPoint](#failpoint) or +[targetedFailPoint](#targetedfailpoint) operations. + +This internal command is not documented in the MongoDB manual (pending +[DOCS-10784](https://jira.mongodb.org/browse/DOCS-10784)); however, there is scattered documentation available on the +server wiki ([The "failCommand" Fail Point](https://github.com/mongodb/mongo/wiki/The-%22failCommand%22-fail-point)) and +employee blogs (e.g. [Intro to Fail Points](https://kchodorow.com/2013/01/15/intro-to-fail-points/), +[Testing Network Errors With MongoDB](https://emptysqua.re/blog/mongodb-testing-network-errors/)). Documentation can +also be gleaned from JIRA tickets (e.g. [SERVER-35004](https://jira.mongodb.org/browse/SERVER-35004), +[SERVER-35083](https://jira.mongodb.org/browse/SERVER-35083)). This specification does not aim to provide comprehensive +documentation for all fail points available for driver testing, but some fail points are documented in +[Fail Points Commonly Used in Tests](#fail-points-commonly-used-in-tests). + +#### Configuring Fail Points + +The `configureFailPoint` command is executed on the `admin` database and has the following structure: + +```javascript +db.adminCommand({ + configureFailPoint: , + mode: , + data: +}); +``` + +The value of `configureFailPoint` is a string denoting the fail point to be configured (e.g. "failCommand"). + +The `mode` option is a generic fail point option and may be assigned a string or object value. The string values +"alwaysOn" and "off" may be used to enable or disable the fail point, respectively. An object may be used to specify +either `times` or `skip`, which are mutually exclusive: + +- `{ times: }` may be used to limit the number of times the fail point may trigger before transitioning to + "off". +- `{ skip: }` may be used to defer the first trigger of a fail point, after which it will transition to + "alwaysOn". + +The `data` option is an object that may be used to specify any options that control the particular fail point's +behavior. + +In order to use `configureFailPoint`, the undocumented `enableTestCommands` +[server parameter](https://www.mongodb.com/docs/manual/reference/parameters/) must be enabled by either the +configuration file or command line option (e.g. `--setParameter enableTestCommands=1`). It cannot be enabled at runtime +via the [setParameter](https://www.mongodb.com/docs/manual/reference/command/setParameter/) command). This parameter +should already be enabled for most configuration files in +[mongo-orchestration](https://github.com/10gen/mongo-orchestration). + +#### Disabling Fail Points + +A fail point may be disabled like so: + +```javascript +db.adminCommand({ + configureFailPoint: , + mode: "off" +}); +``` + +#### Fail Points Commonly Used in Tests + +##### failCommand + +The `failCommand` fail point allows the client to force the server to return an error for commands listed in the +`data.failCommands` field. Additionally, this fail point is documented in server wiki: +[The failCommand Fail Point](https://github.com/mongodb/mongo/wiki/The-%22failCommand%22-fail-point). + +The `failCommand` fail point was introduced in mongod 4.0.0 +([SERVER-34551](https://jira.mongodb.org/browse/SERVER-34551)) and mongos 4.1.5 +([SERVER-35518](https://jira.mongodb.org/browse/SERVER-35518)); however, the fail point was not usable for testing on +mongos until version 4.1.7 ([SERVER-34943](https://jira.mongodb.org/browse/SERVER-34943)). + +The `failCommand` fail point may be configured like so: + +```javascript +db.adminCommand({ + configureFailPoint: "failCommand", + mode: , + data: { + failCommands: [, ...], + closeConnection: , + errorCode: , + errorLabels: [, ...], + writeConcernError: , + appName: , + blockConnection: , + blockTimeMS: , + namespace: , + } +}); +``` + +`failCommand` supports the following `data` options, which may be combined if desired: + +- `failCommands`: Required array of strings. Lists the command names to fail. +- `closeConnection`: Optional boolean, which defaults to `false`. If `true`, the command will not be executed, the + connection will be closed, and the client will see a network error. +- `errorCode`: Optional integer, which is unset by default. If set, the command will not be executed and the specified + command error code will be returned as a command error. +- `errorLabels`: Optional array of strings. If set, this list overrides the server's normal behavior for adding error + labels. An empty array may be used to suppress the server from adding error labels to the response. New in server + 4.3.1 ([SERVER-43941](https://jira.mongodb.org/browse/SERVER-43941)). +- `appName`: Optional string, which is unset by default. If set, the fail point will only apply to connections for + MongoClients created with this `appname`. New in server 4.4.0-rc2 and backported to 4.2.9 + ([SERVER-47195](https://jira.mongodb.org/browse/SERVER-47195)). +- `blockConnection`: Optional boolean, which defaults to `false`. If `true`, the server should block the affected + commands for `blockTimeMS`. New in server 4.3.4 and backported to 4.2.9 + ([SERVER-41070](https://jira.mongodb.org/browse/SERVER-41070)). +- `blockTimeMS`: Optional integer, which is required when `blockConnection` is `true`. The number of milliseconds for + which the affected commands should be blocked. New in server 4.3.4 and backported to 4.2.9 + ([SERVER-41070](https://jira.mongodb.org/browse/SERVER-41070)). +- `namespace`: Optional string. If set, the fail point will only trigger for commands on the specified namespace (e.g. + `db.coll`). New in server 4.3.1 and backported to 4.2.6 + ([SERVER-43011](https://jira.mongodb.org/browse/SERVER-43011)). + +##### onPrimaryTransactionalWrite + +The `onPrimaryTransactionalWrite` fail point allows the client to force a network error before the server would return a +write result to the client. The fail point also allows control whether the server will successfully commit the write via +its `failBeforeCommitExceptionCode` option. Keep in mind that the fail point only triggers for transaction writes (i.e. +write commands including `txnNumber` and `lsid` fields). + +The `onPrimaryTransactionalWrite` fail point was introduced in mongod 3.6.0 +([SERVER-29606](https://jira.mongodb.org/browse/SERVER-29606)) to facilitate testing of retryable writes. It is only +supported on mongod and cannot be used with sharded clusters. + +`onPrimaryTransactionalWrite` supports the following `data` options: + +- `closeConnection`: Optional boolean, which defaults to `true`. If `true`, the connection on which the write is + executed will be closed before a result can be returned. +- `failBeforeCommitExceptionCode`: Optional integer, which is unset by default. If set, the specified exception code + will be thrown and the write will not be committed. If unset, the write will be allowed to commit. + +### Determining if a Sharded Cluster Uses Replica Sets + +When connected to a mongos server, the test runner can query the +[config.shards](https://www.mongodb.com/docs/manual/reference/config-database/#mongodb-data-config.shards) collection. +Each shard in the cluster is represented by a document in this collection. If the shard is backed by a single server, +the `host` field will contain a single host. If the shard is backed by a replica set, the `host` field contain the name +of the replica followed by a forward slash and a comma-delimited list of hosts. + +Note: MongoDB 3.6+ requires that all shard servers be replica sets. + +## Design Rationale + +This specification was primarily derived from the test formats used by the +[Transactions](../transactions/transactions.md) and [CRUD](../crud/crud.md) specs, which have served models or other +specs. + +This specification commonly uses "SHOULD" when providing guidance on writing test files. While this may appear +contradictory to the driver mantra preferring "MUST", it is intentional. Some of this guidance addresses style (e.g. +adding comments, using YAML anchors) and cannot be enforced with a JSON schema. Other guidance needs to be purposefully +ignored in order to test the test runner implementation (e.g. defining entities out of order to trigger runtime errors). +The specification does prefer "MUST" in other contexts, such as discussing parts of the test file format that *are* +enforceable by the JSON schema or the test runner implementation. + + + +### Why are `enxcol_` collections dropped? + +The collections `enxcol_..esc` and `enxcol_..ecoc` are +[automatically created](../client-side-encryption/client-side-encryption.md#create-collection-helper) for Queryable +Encryption collections. If these collections are present and non-empty, the server generated `__safeContent__` field may +differ. `__safeContent__` includes a count of the number of instances of the given value. To do exact matching on +`__safeContent__` the test runner is required to drop these collections. + + + +### Why can't `observeSensitiveCommands` be true when authentication is enabled? + +When running against authentication-enabled clusters, the events observed by a client will always begin with +auth-related commands (e.g. `authenticate`, `saslStart`, `saslContinue`) because the MongoClient will need to +authenticate a connection before issuing the first command in the test specification. Since the exact form of the +authentication command event will depend on whether authentication is enabled, as well as, the auth mechanism in use, it +is not possible to anticipate the command monitoring output and perform the appropriate assertions. Consequently, we +have restricted use of this property to situations where authentication is disabled on the server. This allows tests to +explicitly test sensitive commands via the `runCommand` helper. + +## Breaking Changes + +This section is reserved for future use. Any breaking changes to the test format SHOULD be described here in detail for +historical reference, in addition to any shorter description that may be added to the [Changelog](#changelog). + +## Future Work + +### Target failPoint by read preference + +The [failPoint](#failpoint) operation currently uses a "primary" read preference. To date, no spec has needed behavior +to configure a fail point on a non-primary node. If the need does arise, [failPoint](#failpoint) can be enhanced to +support a `readPreference` argument. + +### IO operations for GridFS streams + +Original GridFS spec tests refer to "upload", "download", and "download_by_name" methods, which allow the tests to +abstract stream IO and either upload a byte sequence or assert a downloaded byte sequence. These operations correspond +to the [download](#download), [downloadByName](#downloadByName), [upload](#upload-and-uploadWithId), and +[uploadWithId](#upload-and-uploadWithId) [Bucket Operations](#bucket-operations). + +In order to support methods such as `downloadToStream`, `openUploadStream`, and `openUploadStreamWithId`, test runners +would need to represent streams as entities and support IO operations to directly read from and write to a stream +entity. This may not be worth the added complexity if the existing operations provide adequate test coverage for GridFS +implementations. + +### Incorporate referenced entity operations into the schema version + +The [Schema Version](#schema-version) is not impacted by changes to operations defined in other specs and referenced in +`Entity Test Operations` (e.g. `find` for CRUD). The [operation.name](#operation_name) and +[operation.arguments](#operation_arguments) fields are loosely defined in the JSON schema as string and object types, +respectively. + +Ideally, all operations (and their arguments) would be enforced by the JSON schema *and* any changes to operations would +affect the schema version accordingly. For example, a new `find` option would warrant a minor version bump both for the +CRUD spec and this spec and its schema. + +As discussed in [Executing an Operation](#executing-an-operation), test runners MUST raise errors for unsupported +operations and arguments. This is a concession until such time that better processes can be established for versioning +other specs *and* collating spec changes developed in parallel or during the same release cycle. + +## Changelog + +- 2026-03-17: **Schema version 1.28.** + + Add `accessToken` to `kmsProviders` (Azure and GCP). Replace unnecessary `anyOf` with `oneOf` for `csfle`. + +- 2026-01-08: Remove Future Work entry for log message assertions (implemented in schema version 1.13). + +- 2025-10-22: Added `getSnapshotTime` operation on sessions. + +- 2025-09-17: **Schema version 1.27.** + + Add `observeTracingMessages` configuration for clients and `expectTracingMessages` for test expectations. This allows + capturing and validating detailed tracing information during test execution. + +- 2025-09-16: **Schema version 1.26.** Add `awaitMinPoolSize` client parameter to await on connection pool population. + +- 2025-09-15: Remove note about Atlas Data Lake since Atlas Data Lake testing is removed. + +- 2025-08-20: Fix typo `_enxcol` => `enxcol_` + +- 2025-07-28: **Schema version 1.25.** + + Add alternate form of `csfle`. Previously it was only a bool. Now it can also be an object containing + `minLibmongocryptVersion`. + +- 2025-07-15: Clarify test runner may apply a default `cryptSharedLibPath`. + +- 2025-06-10: **Schema version 1.24.** + + Deprecate `storeEventsAsEntities` option for client entities and `loop` operation in the schema and remove them from + this spec. + +- 2025-06-09: Add `appendMetadata` operation. + +- 2025-06-04: Deprecate the `serverless` runOnRequirement + +- 2025-04-25: Drop `enxcol_` collections. + +- 2025-04-07: Add `topologyOpeningEvent` and `topologyClosedEvent` to the unified test format and schema 1.20+ as they + were omitted in error. + +- 2025-01-21: **Schema version 1.23.** + + Support automatic encryption. Add `autoEncryptOpts` to `client` entity. + +- 2024-11-12: **Schema version 1.22.** + + Add `keyExpirationMS` to `clientEncryption` entity. + +- 2024-10-31: Test operations array may be empty. + +- 2024-10-29: List more examples of root-level documents + +- 2024-10-24: Document `namespace` option for `failCommand` fail point. + +- 2024-09-18: Note that `failCommand` `appName` option was backported + +- 2024-05-08: **Schema version 1.21.** + + Add `writeErrors` and `writeConcernErrors` field to `expectedError` for the client-level bulk write API. + +- 2024-04-15: Note that when `directConnection` is set to true test runners should only provide a single seed. + +- 2024-03-25: **Schema version 1.20.** + + Add `previousDescription` and `newDescription` assertions to `topologyDescriptionChangedEvent` when checking events + with `expectEvents` + +- 2024-03-11: Note that `killAllSessions` should not be executed on Atlas Data Lake + +- 2024-02-27: Document `onPrimaryTransactionalWrite` fail point, which is used in retryable writes tests. + +- 2024-02-23: Require test runners to gossip cluster time from internal MongoClient to each session entity. + +- 2024-02-14: Clarify that errors raised from callback operations should always propagate to `withTransaction`. + +- 2024-02-12: Clarify that `targetedFailPoint` should only be used when `useMultipleMongoses` is true and not on + load-balanced topologies. + +- 2024-02-06: Migrated from reStructuredText to Markdown. + +- 2024-01-17: **Schema version 1.19.** + + Add `authMechanism` to `runOnRequirement` and require that `uriOptions` supports placeholder documents. + +- 2024-01-11: **Schema version 1.18.** + + Allow named KMS providers in `kmsProviders`. Note location of Client-Side Encryption test credentials. + +- 2024-01-03: Document server version requirements for `errorLabels` and `blockConnection` options for `failCommand` + fail point. + +- 2023-10-04: **Schema version 1.17.** + + Add `serverHeartbeatStartedEvent`, `serverHeartbeatSucceededEvent`, and `serverHeartbeatFailedEvent` for asserting on + SDAM server heartbeat events. + +- 2023-09-25: Clarify that the UTR is intended to be run against enterprise servers. + +- 2022-07-18: **Schema version 1.16.** + + Add `ignoreMessages` and `ignoreExtraMessages` fields to `expectedLogMessagesForClient` section. + +- 2023-06-26: `runOnRequirement.csfle` should check for crypt_shared and/or mongocryptd. + +- 2023-06-13: **Schema version 1.15.** + + Add `databaseName` field to `CommandFailedEvent` and `CommandSucceededEvent`. + +- 2023-05-26: **Schema version 1.14.** + + Add `topologyDescriptionChangedEvent`. + +- 2023-05-17: Add `runCursorCommand` and `createCommandCursor` operations. Added `commandCursor` entity type which can + be used with existing cursor operations. + +- 2023-05-12: Deprecate "sharded-replicaset" topology type. Note that server 3.6+ requires replica sets for shards, + which is also relevant to load balanced topologies. + +- 2023-04-13: Remove `readConcern` and `writeConcern` options from `runCommand` operation. + +- 2023-02-24: Fix typo in the description of the `$$matchAsRoot` matching operator. + +- 2022-10-17: Add description of a `close` operation for client entities. + +- 2022-10-14: **Schema version 1.13.** + + Add support for logging assertions via the `observeLogMessages` field for client entities, along with a new top-level + field `expectLogMessages` containing `expectedLogMessagesForClient` objects. Add new special matching operators to + enable command logging assertions, `$$matchAsDocument` and `$$matchAsRoot`. + +- 2022-10-14: **Schema version 1.12.** + + Add `errorResponse` to `expectedError`. + +- 2022-10-05: Remove spec front matter, add "Current Schema Version" field, and reformat changelog. Add comment to + remind editors to note schema version bumps in changelog updates (where applicable). + +- 2022-09-02: **Schema version 1.11.** + + Add `interruptInUseConnections` field to `poolClearedEvent` + +- 2022-07-28: **Schema version 1.10.** + + Add support for `thread` entities (`runOnThread`, `waitForThread`), TopologyDescription entities + (`recordTopologyDescription`, `waitForPrimaryChange`, `assertTopologyType`), testRunner event assertion operations + (`waitForEvent`, `assertEventCount`), expected SDAM events, and the `wait` operation. + +- 2022-07-27: Retroactively note schema version bumps in the changelog and require doing so for future changes. + +- 2022-07-11: Update [Future Work](#future-work) to reflect that support for ignoring extra observed events was added in + schema version 1.7. + +- 2022-06-16: Require server 4.2+ for `csfle: true`. + +- 2022-05-10: Add reference to Client Side Encryption spec under + [ClientEncryption Operations](#clientencryption-operations). + +- 2022-04-27: **Schema version 1.9.** + + Added `createOptions` field to `initialData`, introduced a new `timeoutMS` field in `collectionOrDatabaseOptions`, and + added an `isTimeoutError` field to `expectedError`. Also introduced the `createEntities` operation and `$$lte` + operator. + +- 2022-04-27: **Schema version 1.8.** + + Add `runOnRequirement.csfle`. + +- 2022-04-26: Add `clientEncryption` entity and `$$placeholder` syntax. + +- 2022-04-22: Revise `useMultipleMongoses` and "Initializing the Test Runner" for Atlas Serverless URIs using a load + balancer fronting a single proxy. + +- 2022-03-01: **Schema version 1.7.** + + Add `ignoreExtraEvents` field to `expectedEventsForClient`. + +- 2022-02-24: Rename Versioned API to Stable API + +- 2021-08-30: **Schema version 1.6.** + + Add `hasServerConnectionId` field to `commandStartedEvent`, `commandSuccededEvent` and `commandFailedEvent`. + +- 2021-08-30: Test runners may create an internal MongoClient for each mongos. Better clarify how internal MongoClients + may be used. Clarify that drivers creating an internal MongoClient for each mongos should use those clients for + `targetedFailPoint` operations. + +- 2021-08-23: Allow `runOnRequirement` conditions to be evaluated in any order. + +- 2021-08-09: Updated all existing schema files to require at least one element in `test.expectEvents` if specified. + +- 2021-07-29: Note that events for sensitive commands will have redacted commands and replies when using + `observeSensitiveCommands`, and how that affects conditionally sensitive commands such as `hello` and legacy hello. + +- 2021-07-01: Note that `expectError.expectResult` should use `$$unsetOrMatches` when the result is optional. + +- 2021-06-09: **Schema version 1.5.** + + Added an `observeSensitiveCommands` property to the `client` entity type. + +- 2021-05-17: Ensure old JSON schema files remain in place + +- 2021-04-19: **Schema version 1.4.** + + Introduce `serverless` [runOnRequirement](#runonrequirement). + +- 2021-04-12: **Schema version 1.3.** + + Added a `FindCursor` entity type. Defined a set of cursor operations. Added an `auth` property to `runOnRequirements` + and modified the `topologies` property to accept `load-balanced`. Added CMAP events to the possible event types for + `expectedEvent`. Add `assertNumberConnectionsCheckedOut` operation. Add `ignoreResultAndError` operation option. + +- 2021-04-08: List additional error codes that may be ignored when calling `killAllSessions` and note that the command + should not be called when connected to Atlas. + +- 2021-03-22: Split `serverApi` into its own section. Note types for `loop` operation arguments. Clarify how `loop` + iterations are counted for `storeIterationsAsEntity`. + +- 2021-03-10: Clarify that `observedAt` field measures time in seconds for `storeEventsAsEntities`. + +- 2021-03-09: Clarify which components of a version string are relevant for comparisons. + +- 2021-03-04: Change `storeEventsAsEntities` from a map to an array of `storeEventsAsEntity` objects. + +- 2021-03-01: **Schema version 1.2.** + + Added `storeEventsAsEntities` option for client entities and `loop` operation, which is needed for Atlas Driver + Testing. + +- 2020-12-23: Clarify how JSON schema is renamed for new minor versions. + +- 2020-11-06: **Schema version 1.1.** + + Added `serverApi` option for client entities, `_yamlAnchors` property to define values for later use in YAML tests, + and `serverParameters` property for `runOnRequirements`. diff --git a/source/unified-test-format/unified-test-format.rst b/source/unified-test-format/unified-test-format.rst new file mode 100644 index 0000000000..2e82fad0f0 --- /dev/null +++ b/source/unified-test-format/unified-test-format.rst @@ -0,0 +1,3 @@ +.. note:: + This specification has been converted to Markdown and renamed to + `unified-test-format.md `_. diff --git a/source/uri-options/tests/README.md b/source/uri-options/tests/README.md new file mode 100644 index 0000000000..bda997051a --- /dev/null +++ b/source/uri-options/tests/README.md @@ -0,0 +1,43 @@ +# URI Options Tests + +The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their +conformance to the URI Options spec. + +These tests use the same format as the Connection String spec tests. + +## Version + +Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. + +## Format + +Each YAML file contains an object with a single `tests` key. This key is an array of test case objects, each of which +have the following keys: + +- `description`: A string describing the test. +- `uri`: A string containing the URI to be parsed. +- `valid`: A boolean indicating if the URI should be considered valid. +- `warning`: A boolean indicating whether URI parsing should emit a warning. +- `hosts`: Included for compatibility with the Connection String spec tests. This will always be `~`. +- `auth`: Included for compatibility with the Connection String spec tests. This will always be `~`. +- `options`: An object containing key/value pairs for each parsed query string option. + +If a test case includes a null value for one of these keys (e.g. `auth: ~`, `hosts: ~`), no assertion is necessary. This +both simplifies parsing of the test files (keys should always exist) and allows flexibility for drivers that might +substitute default values *during* parsing (e.g. omitted `hosts` could be parsed as `["localhost"]`). + +The `valid` and `warning` fields are boolean in order to keep the tests flexible. We are not concerned with asserting +the format of specific error or warnings messages strings. + +Under normal circumstances, it should not be necessary to specify both `valid: false` and `warning: true`. Typically, a +URI test case will either yield an error (e.g. options conflict) or a warning (e.g. invalid type or value for an +option), but not both. + +### Use as unit tests + +Testing whether a URI is valid or not requires testing whether URI parsing (or MongoClient construction) causes a +warning due to a URI option being invalid and asserting that the options parsed from the URI match those listed in the +`options` field. + +Note that there are tests for each of the options marked as optional; drivers will need to implement logic to skip over +the optional tests that they don't implement. diff --git a/source/uri-options/tests/auth-options.json b/source/uri-options/tests/auth-options.json new file mode 100644 index 0000000000..d7fa14a134 --- /dev/null +++ b/source/uri-options/tests/auth-options.json @@ -0,0 +1,33 @@ +{ + "tests": [ + { + "description": "Valid auth options are parsed correctly (GSSAPI)", + "uri": "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authSource=$external", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "authMechanism": "GSSAPI", + "authMechanismProperties": { + "SERVICE_NAME": "other", + "SERVICE_HOST": "example.com", + "CANONICALIZE_HOST_NAME": "forward" + }, + "authSource": "$external" + } + }, + { + "description": "Valid auth options are parsed correctly (SCRAM-SHA-1)", + "uri": "mongodb://foo:bar@example.com/?authMechanism=SCRAM-SHA-1&authSource=authSourceDB", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "authMechanism": "SCRAM-SHA-1", + "authSource": "authSourceDB" + } + } + ] +} diff --git a/source/uri-options/tests/auth-options.yml b/source/uri-options/tests/auth-options.yml new file mode 100644 index 0000000000..4a46516f13 --- /dev/null +++ b/source/uri-options/tests/auth-options.yml @@ -0,0 +1,25 @@ +tests: + - + description: "Valid auth options are parsed correctly (GSSAPI)" + uri: "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authSource=$external" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + authMechanism: "GSSAPI" + authMechanismProperties: + SERVICE_NAME: "other" + SERVICE_HOST: "example.com" + CANONICALIZE_HOST_NAME: "forward" + authSource: "$external" + - + description: "Valid auth options are parsed correctly (SCRAM-SHA-1)" + uri: "mongodb://foo:bar@example.com/?authMechanism=SCRAM-SHA-1&authSource=authSourceDB" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + authMechanism: "SCRAM-SHA-1" + authSource: "authSourceDB" diff --git a/source/uri-options/tests/ca.pem b/source/uri-options/tests/ca.pem new file mode 100644 index 0000000000..b4bdaefa85 --- /dev/null +++ b/source/uri-options/tests/ca.pem @@ -0,0 +1 @@ +# This file exists solely for the purpose of facilitating drivers which check for the existence of files specified in the URI options at parse time. diff --git a/source/uri-options/tests/cert.pem b/source/uri-options/tests/cert.pem new file mode 100644 index 0000000000..b4bdaefa85 --- /dev/null +++ b/source/uri-options/tests/cert.pem @@ -0,0 +1 @@ +# This file exists solely for the purpose of facilitating drivers which check for the existence of files specified in the URI options at parse time. diff --git a/source/uri-options/tests/client-backpressure-options.json b/source/uri-options/tests/client-backpressure-options.json new file mode 100644 index 0000000000..3e501d1f4c --- /dev/null +++ b/source/uri-options/tests/client-backpressure-options.json @@ -0,0 +1,66 @@ +{ + "tests": [ + { + "description": "maxAdaptiveRetries is parsed correctly", + "uri": "mongodb://example.com/?maxAdaptiveRetries=3", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "maxAdaptiveRetries": 3 + } + }, + { + "description": "maxAdaptiveRetries=0 is parsed correctly", + "uri": "mongodb://example.com/?maxAdaptiveRetries=0", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "maxAdaptiveRetries": 0 + } + }, + { + "description": "maxAdaptiveRetries with invalid value causes a warning", + "uri": "mongodb://example.com/?maxAdaptiveRetries=-5", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "enableOverloadRetargeting is parsed correctly", + "uri": "mongodb://example.com/?enableOverloadRetargeting=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "enableOverloadRetargeting": true + } + }, + { + "description": "enableOverloadRetargeting=false is parsed correctly", + "uri": "mongodb://example.com/?enableOverloadRetargeting=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "enableOverloadRetargeting": false + } + }, + { + "description": "enableOverloadRetargeting with invalid value causes a warning", + "uri": "mongodb://example.com/?enableOverloadRetargeting=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/client-backpressure-options.yml b/source/uri-options/tests/client-backpressure-options.yml new file mode 100644 index 0000000000..c750b32e23 --- /dev/null +++ b/source/uri-options/tests/client-backpressure-options.yml @@ -0,0 +1,53 @@ +tests: + - + description: "maxAdaptiveRetries is parsed correctly" + uri: "mongodb://example.com/?maxAdaptiveRetries=3" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + maxAdaptiveRetries: 3 + - + description: "maxAdaptiveRetries=0 is parsed correctly" + uri: "mongodb://example.com/?maxAdaptiveRetries=0" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + maxAdaptiveRetries: 0 + - + description: "maxAdaptiveRetries with invalid value causes a warning" + uri: "mongodb://example.com/?maxAdaptiveRetries=-5" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "enableOverloadRetargeting is parsed correctly" + uri: "mongodb://example.com/?enableOverloadRetargeting=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + enableOverloadRetargeting: true + - + description: "enableOverloadRetargeting=false is parsed correctly" + uri: "mongodb://example.com/?enableOverloadRetargeting=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + enableOverloadRetargeting: false + - + description: "enableOverloadRetargeting with invalid value causes a warning" + uri: "mongodb://example.com/?enableOverloadRetargeting=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/client.pem b/source/uri-options/tests/client.pem new file mode 100644 index 0000000000..b4bdaefa85 --- /dev/null +++ b/source/uri-options/tests/client.pem @@ -0,0 +1 @@ +# This file exists solely for the purpose of facilitating drivers which check for the existence of files specified in the URI options at parse time. diff --git a/source/uri-options/tests/compression-options.json b/source/uri-options/tests/compression-options.json new file mode 100644 index 0000000000..3c13dee062 --- /dev/null +++ b/source/uri-options/tests/compression-options.json @@ -0,0 +1,59 @@ +{ + "tests": [ + { + "description": "Valid compression options are parsed correctly", + "uri": "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=9", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "compressors": [ + "zlib" + ], + "zlibCompressionLevel": 9 + } + }, + { + "description": "Multiple compressors are parsed correctly", + "uri": "mongodb://example.com/?compressors=snappy,zlib", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "compressors": [ + "snappy", + "zlib" + ] + } + }, + { + "description": "Non-numeric zlibCompressionLevel causes a warning", + "uri": "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low zlibCompressionLevel causes a warning", + "uri": "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too high zlibCompressionLevel causes a warning", + "uri": "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=10", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/compression-options.yml b/source/uri-options/tests/compression-options.yml new file mode 100644 index 0000000000..5e140e59c9 --- /dev/null +++ b/source/uri-options/tests/compression-options.yml @@ -0,0 +1,48 @@ +tests: + - + description: "Valid compression options are parsed correctly" + uri: "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=9" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + compressors: + - "zlib" + zlibCompressionLevel: 9 + - + description: "Multiple compressors are parsed correctly" + uri: "mongodb://example.com/?compressors=snappy,zlib" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + compressors: + - "snappy" + - "zlib" + - + description: "Non-numeric zlibCompressionLevel causes a warning" + uri: "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low zlibCompressionLevel causes a warning" + uri: "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too high zlibCompressionLevel causes a warning" + uri: "mongodb://example.com/?compressors=zlib&zlibCompressionLevel=10" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + diff --git a/source/uri-options/tests/concern-options.json b/source/uri-options/tests/concern-options.json new file mode 100644 index 0000000000..f55f298087 --- /dev/null +++ b/source/uri-options/tests/concern-options.json @@ -0,0 +1,67 @@ +{ + "tests": [ + { + "description": "Valid read and write concern are parsed correctly", + "uri": "mongodb://example.com/?readConcernLevel=majority&w=5&wTimeoutMS=30000&journal=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "readConcernLevel": "majority", + "w": 5, + "wTimeoutMS": 30000, + "journal": false + } + }, + { + "description": "Arbitrary string readConcernLevel does not cause a warning", + "uri": "mongodb://example.com/?readConcernLevel=arbitraryButStillValid", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "readConcernLevel": "arbitraryButStillValid" + } + }, + { + "description": "Arbitrary string w doesn't cause a warning", + "uri": "mongodb://example.com/?w=arbitraryButStillValid", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "w": "arbitraryButStillValid" + } + }, + { + "description": "Non-numeric wTimeoutMS causes a warning", + "uri": "mongodb://example.com/?wTimeoutMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low wTimeoutMS causes a warning", + "uri": "mongodb://example.com/?wTimeoutMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Invalid journal causes a warning", + "uri": "mongodb://example.com/?journal=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/concern-options.yml b/source/uri-options/tests/concern-options.yml new file mode 100644 index 0000000000..7d31453491 --- /dev/null +++ b/source/uri-options/tests/concern-options.yml @@ -0,0 +1,55 @@ +tests: + - + description: "Valid read and write concern are parsed correctly" + uri: "mongodb://example.com/?readConcernLevel=majority&w=5&wTimeoutMS=30000&journal=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + readConcernLevel: "majority" + w: 5 + wTimeoutMS: 30000 + journal: false + - + description: "Arbitrary string readConcernLevel does not cause a warning" + uri: "mongodb://example.com/?readConcernLevel=arbitraryButStillValid" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + readConcernLevel: "arbitraryButStillValid" + - + description: "Arbitrary string w doesn't cause a warning" + uri: "mongodb://example.com/?w=arbitraryButStillValid" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + w: "arbitraryButStillValid" + - + description: "Non-numeric wTimeoutMS causes a warning" + uri: "mongodb://example.com/?wTimeoutMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low wTimeoutMS causes a warning" + uri: "mongodb://example.com/?wTimeoutMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid journal causes a warning" + uri: "mongodb://example.com/?journal=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/connection-options.json b/source/uri-options/tests/connection-options.json new file mode 100644 index 0000000000..bbaa295ecb --- /dev/null +++ b/source/uri-options/tests/connection-options.json @@ -0,0 +1,273 @@ +{ + "tests": [ + { + "description": "Valid connection and timeout options are parsed correctly", + "uri": "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500&timeoutMS=100", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "appname": "URI-OPTIONS-SPEC-TEST", + "connectTimeoutMS": 20000, + "heartbeatFrequencyMS": 5000, + "localThresholdMS": 3000, + "maxIdleTimeMS": 50000, + "replicaSet": "uri-options-spec", + "retryWrites": true, + "serverSelectionTimeoutMS": 15000, + "socketTimeoutMS": 7500, + "timeoutMS": 100 + } + }, + { + "description": "Non-numeric connectTimeoutMS causes a warning", + "uri": "mongodb://example.com/?connectTimeoutMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low connectTimeoutMS causes a warning", + "uri": "mongodb://example.com/?connectTimeoutMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-numeric heartbeatFrequencyMS causes a warning", + "uri": "mongodb://example.com/?heartbeatFrequencyMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low heartbeatFrequencyMS causes a warning", + "uri": "mongodb://example.com/?heartbeatFrequencyMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-numeric localThresholdMS causes a warning", + "uri": "mongodb://example.com/?localThresholdMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low localThresholdMS causes a warning", + "uri": "mongodb://example.com/?localThresholdMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Invalid retryWrites causes a warning", + "uri": "mongodb://example.com/?retryWrites=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-numeric serverSelectionTimeoutMS causes a warning", + "uri": "mongodb://example.com/?serverSelectionTimeoutMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low serverSelectionTimeoutMS causes a warning", + "uri": "mongodb://example.com/?serverSelectionTimeoutMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-numeric socketTimeoutMS causes a warning", + "uri": "mongodb://example.com/?socketTimeoutMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low socketTimeoutMS causes a warning", + "uri": "mongodb://example.com/?socketTimeoutMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "directConnection=true", + "uri": "mongodb://example.com/?directConnection=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "directConnection": true + } + }, + { + "description": "directConnection=true with multiple seeds", + "uri": "mongodb://example1.com,example2.com/?directConnection=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "directConnection=false", + "uri": "mongodb://example.com/?directConnection=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "directConnection": false + } + }, + { + "description": "directConnection=false with multiple seeds", + "uri": "mongodb://example1.com,example2.com/?directConnection=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "directConnection": false + } + }, + { + "description": "Invalid directConnection value", + "uri": "mongodb://example.com/?directConnection=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "loadBalanced=true", + "uri": "mongodb://example.com/?loadBalanced=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "loadBalanced": true + } + }, + { + "description": "loadBalanced=true with directConnection=false", + "uri": "mongodb://example.com/?loadBalanced=true&directConnection=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "loadBalanced": true, + "directConnection": false + } + }, + { + "description": "loadBalanced=false", + "uri": "mongodb://example.com/?loadBalanced=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "loadBalanced": false + } + }, + { + "description": "Invalid loadBalanced value", + "uri": "mongodb://example.com/?loadBalanced=1", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "loadBalanced=true with multiple hosts causes an error", + "uri": "mongodb://example1,example2/?loadBalanced=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "loadBalanced=true with directConnection=true causes an error", + "uri": "mongodb://example.com/?loadBalanced=true&directConnection=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "loadBalanced=true with replicaSet causes an error", + "uri": "mongodb://example.com/?loadBalanced=true&replicaSet=replset", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "timeoutMS=0", + "uri": "mongodb://example.com/?timeoutMS=0", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "timeoutMS": 0 + } + }, + { + "description": "Non-numeric timeoutMS causes a warning", + "uri": "mongodb://example.com/?timeoutMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low timeoutMS causes a warning", + "uri": "mongodb://example.com/?timeoutMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/connection-options.yml b/source/uri-options/tests/connection-options.yml new file mode 100644 index 0000000000..2d312b0861 --- /dev/null +++ b/source/uri-options/tests/connection-options.yml @@ -0,0 +1,235 @@ +tests: + - + description: "Valid connection and timeout options are parsed correctly" + uri: "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500&timeoutMS=100" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + appname: "URI-OPTIONS-SPEC-TEST" + connectTimeoutMS: 20000 + heartbeatFrequencyMS: 5000 + localThresholdMS: 3000 + maxIdleTimeMS: 50000 + replicaSet: "uri-options-spec" + retryWrites: true + serverSelectionTimeoutMS: 15000 + socketTimeoutMS: 7500 + timeoutMS: 100 + - + description: "Non-numeric connectTimeoutMS causes a warning" + uri: "mongodb://example.com/?connectTimeoutMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low connectTimeoutMS causes a warning" + uri: "mongodb://example.com/?connectTimeoutMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Non-numeric heartbeatFrequencyMS causes a warning" + uri: "mongodb://example.com/?heartbeatFrequencyMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low heartbeatFrequencyMS causes a warning" + uri: "mongodb://example.com/?heartbeatFrequencyMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Non-numeric localThresholdMS causes a warning" + uri: "mongodb://example.com/?localThresholdMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low localThresholdMS causes a warning" + uri: "mongodb://example.com/?localThresholdMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid retryWrites causes a warning" + uri: "mongodb://example.com/?retryWrites=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Non-numeric serverSelectionTimeoutMS causes a warning" + uri: "mongodb://example.com/?serverSelectionTimeoutMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low serverSelectionTimeoutMS causes a warning" + uri: "mongodb://example.com/?serverSelectionTimeoutMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Non-numeric socketTimeoutMS causes a warning" + uri: "mongodb://example.com/?socketTimeoutMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low socketTimeoutMS causes a warning" + uri: "mongodb://example.com/?socketTimeoutMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: directConnection=true + uri: "mongodb://example.com/?directConnection=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + directConnection: true + - + description: directConnection=true with multiple seeds + uri: "mongodb://example1.com,example2.com/?directConnection=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: directConnection=false + uri: "mongodb://example.com/?directConnection=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + directConnection: false + - + description: directConnection=false with multiple seeds + uri: "mongodb://example1.com,example2.com/?directConnection=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + directConnection: false + - + description: Invalid directConnection value + uri: "mongodb://example.com/?directConnection=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: loadBalanced=true + uri: "mongodb://example.com/?loadBalanced=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + loadBalanced: true + - + description: loadBalanced=true with directConnection=false + uri: "mongodb://example.com/?loadBalanced=true&directConnection=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + loadBalanced: true + directConnection: false + - + description: loadBalanced=false + uri: "mongodb://example.com/?loadBalanced=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + loadBalanced: false + - + description: Invalid loadBalanced value + uri: "mongodb://example.com/?loadBalanced=1" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: loadBalanced=true with multiple hosts causes an error + uri: "mongodb://example1,example2/?loadBalanced=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: loadBalanced=true with directConnection=true causes an error + uri: "mongodb://example.com/?loadBalanced=true&directConnection=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: loadBalanced=true with replicaSet causes an error + uri: "mongodb://example.com/?loadBalanced=true&replicaSet=replset" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "timeoutMS=0" + uri: "mongodb://example.com/?timeoutMS=0" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + timeoutMS: 0 + - + description: "Non-numeric timeoutMS causes a warning" + uri: "mongodb://example.com/?timeoutMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low timeoutMS causes a warning" + uri: "mongodb://example.com/?timeoutMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/connection-pool-options.json b/source/uri-options/tests/connection-pool-options.json new file mode 100644 index 0000000000..a582867d07 --- /dev/null +++ b/source/uri-options/tests/connection-pool-options.json @@ -0,0 +1,76 @@ +{ + "tests": [ + { + "description": "Valid connection pool options are parsed correctly", + "uri": "mongodb://example.com/?maxIdleTimeMS=50000&maxPoolSize=5&minPoolSize=3&maxConnecting=1", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "maxIdleTimeMS": 50000, + "maxPoolSize": 5, + "minPoolSize": 3, + "maxConnecting": 1 + } + }, + { + "description": "Non-numeric maxIdleTimeMS causes a warning", + "uri": "mongodb://example.com/?maxIdleTimeMS=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low maxIdleTimeMS causes a warning", + "uri": "mongodb://example.com/?maxIdleTimeMS=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "maxPoolSize=0 does not error", + "uri": "mongodb://example.com/?maxPoolSize=0", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "maxPoolSize": 0 + } + }, + { + "description": "minPoolSize=0 does not error", + "uri": "mongodb://example.com/?minPoolSize=0", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "minPoolSize": 0 + } + }, + { + "description": "maxConnecting=0 causes a warning", + "uri": "mongodb://example.com/?maxConnecting=0", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "maxConnecting<0 causes a warning", + "uri": "mongodb://example.com/?maxConnecting=-1", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/connection-pool-options.yml b/source/uri-options/tests/connection-pool-options.yml new file mode 100644 index 0000000000..bb73093be8 --- /dev/null +++ b/source/uri-options/tests/connection-pool-options.yml @@ -0,0 +1,67 @@ +tests: + - + description: "Valid connection pool options are parsed correctly" + uri: "mongodb://example.com/?maxIdleTimeMS=50000&maxPoolSize=5&minPoolSize=3&maxConnecting=1" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + maxIdleTimeMS: 50000 + maxPoolSize: 5 + minPoolSize: 3 + maxConnecting: 1 + - + description: "Non-numeric maxIdleTimeMS causes a warning" + uri: "mongodb://example.com/?maxIdleTimeMS=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low maxIdleTimeMS causes a warning" + uri: "mongodb://example.com/?maxIdleTimeMS=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + + - + description: "maxPoolSize=0 does not error" + uri: "mongodb://example.com/?maxPoolSize=0" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + maxPoolSize: 0 + + - + description: "minPoolSize=0 does not error" + uri: "mongodb://example.com/?minPoolSize=0" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + minPoolSize: 0 + + - + description: "maxConnecting=0 causes a warning" + uri: "mongodb://example.com/?maxConnecting=0" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + + - + description: "maxConnecting<0 causes a warning" + uri: "mongodb://example.com/?maxConnecting=-1" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/proxy-options.json b/source/uri-options/tests/proxy-options.json new file mode 100644 index 0000000000..585546ead7 --- /dev/null +++ b/source/uri-options/tests/proxy-options.json @@ -0,0 +1,139 @@ +{ + "tests": [ + { + "description": "proxyPort without proxyHost", + "uri": "mongodb://localhost/?proxyPort=1080", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "proxyUsername without proxyHost", + "uri": "mongodb://localhost/?proxyUsername=abc", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "proxyPassword without proxyHost", + "uri": "mongodb://localhost/?proxyPassword=def", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "all other proxy options without proxyHost", + "uri": "mongodb://localhost/?proxyPort=1080&proxyUsername=abc&proxyPassword=def", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "proxyUsername without proxyPassword", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "proxyPassword without proxyUsername", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyPassword=def", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "multiple proxyHost parameters", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyHost=localhost2", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "multiple proxyPort parameters", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyPort=1234&proxyPort=12345", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "multiple proxyUsername parameters", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc&proxyUsername=def&proxyPassword=123", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "multiple proxyPassword parameters", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc&proxyPassword=123&proxyPassword=456", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "only host present", + "uri": "mongodb://localhost/?proxyHost=localhost", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "host and default port present", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyPort=1080", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "host and non-default port present", + "uri": "mongodb://localhost/?proxyHost=localhost&proxyPort=12345", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "replicaset, host and non-default port present", + "uri": "mongodb://rs1,rs2,rs3/?proxyHost=localhost&proxyPort=12345", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "all options present", + "uri": "mongodb://rs1,rs2,rs3/?proxyHost=localhost&proxyPort=12345&proxyUsername=asdf&proxyPassword=qwerty", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + } + ] +} diff --git a/source/uri-options/tests/proxy-options.yml b/source/uri-options/tests/proxy-options.yml new file mode 100644 index 0000000000..a97863dd59 --- /dev/null +++ b/source/uri-options/tests/proxy-options.yml @@ -0,0 +1,121 @@ +tests: + - + description: "proxyPort without proxyHost" + uri: "mongodb://localhost/?proxyPort=1080" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "proxyUsername without proxyHost" + uri: "mongodb://localhost/?proxyUsername=abc" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "proxyPassword without proxyHost" + uri: "mongodb://localhost/?proxyPassword=def" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "all other proxy options without proxyHost" + uri: "mongodb://localhost/?proxyPort=1080&proxyUsername=abc&proxyPassword=def" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "proxyUsername without proxyPassword" + uri: "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "proxyPassword without proxyUsername" + uri: "mongodb://localhost/?proxyHost=localhost&proxyPassword=def" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "multiple proxyHost parameters" + uri: "mongodb://localhost/?proxyHost=localhost&proxyHost=localhost2" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "multiple proxyPort parameters" + uri: "mongodb://localhost/?proxyHost=localhost&proxyPort=1234&proxyPort=12345" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "multiple proxyUsername parameters" + uri: "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc&proxyUsername=def&proxyPassword=123" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "multiple proxyPassword parameters" + uri: "mongodb://localhost/?proxyHost=localhost&proxyUsername=abc&proxyPassword=123&proxyPassword=456" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "only host present" + uri: "mongodb://localhost/?proxyHost=localhost" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "host and default port present" + uri: "mongodb://localhost/?proxyHost=localhost&proxyPort=1080" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "host and non-default port present" + uri: "mongodb://localhost/?proxyHost=localhost&proxyPort=12345" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "replicaset, host and non-default port present" + uri: "mongodb://rs1,rs2,rs3/?proxyHost=localhost&proxyPort=12345" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "all options present" + uri: "mongodb://rs1,rs2,rs3/?proxyHost=localhost&proxyPort=12345&proxyUsername=asdf&proxyPassword=qwerty" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} diff --git a/source/uri-options/tests/read-preference-options.json b/source/uri-options/tests/read-preference-options.json new file mode 100644 index 0000000000..abbf0d0cc6 --- /dev/null +++ b/source/uri-options/tests/read-preference-options.json @@ -0,0 +1,82 @@ +{ + "tests": [ + { + "description": "Valid read preference options are parsed correctly", + "uri": "mongodb://example.com/?readPreference=primaryPreferred&readPreferenceTags=dc:ny,rack:1&maxStalenessSeconds=120&readPreferenceTags=dc:ny", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "readPreference": "primaryPreferred", + "readPreferenceTags": [ + { + "dc": "ny", + "rack": "1" + }, + { + "dc": "ny" + } + ], + "maxStalenessSeconds": 120 + } + }, + { + "description": "Single readPreferenceTags is parsed as array of size one", + "uri": "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:ny", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "readPreferenceTags": [ + { + "dc": "ny" + } + ] + } + }, + { + "description": "Read preference tags are case sensitive", + "uri": "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:NY", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "readPreferenceTags": [ + { + "dc": "NY" + } + ] + } + }, + { + "description": "Invalid readPreferenceTags causes a warning", + "uri": "mongodb://example.com/?readPreferenceTags=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-numeric maxStalenessSeconds causes a warning", + "uri": "mongodb://example.com/?maxStalenessSeconds=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Too low maxStalenessSeconds causes a warning", + "uri": "mongodb://example.com/?maxStalenessSeconds=-2", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/read-preference-options.yml b/source/uri-options/tests/read-preference-options.yml new file mode 100644 index 0000000000..267454c0ea --- /dev/null +++ b/source/uri-options/tests/read-preference-options.yml @@ -0,0 +1,64 @@ +tests: + - + description: "Valid read preference options are parsed correctly" + uri: "mongodb://example.com/?readPreference=primaryPreferred&readPreferenceTags=dc:ny,rack:1&maxStalenessSeconds=120&readPreferenceTags=dc:ny" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + readPreference: "primaryPreferred" + readPreferenceTags: + - + dc: "ny" + rack: "1" + - + dc: "ny" + maxStalenessSeconds: 120 + - + description: "Single readPreferenceTags is parsed as array of size one" + uri: "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:ny" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + readPreferenceTags: + - + dc: "ny" + - + description: "Read preference tags are case sensitive" + uri: "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:NY" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + readPreferenceTags: + - + dc: "NY" + - + description: "Invalid readPreferenceTags causes a warning" + uri: "mongodb://example.com/?readPreferenceTags=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Non-numeric maxStalenessSeconds causes a warning" + uri: "mongodb://example.com/?maxStalenessSeconds=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "Too low maxStalenessSeconds causes a warning" + uri: "mongodb://example.com/?maxStalenessSeconds=-2" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + diff --git a/source/uri-options/tests/sdam-options.json b/source/uri-options/tests/sdam-options.json new file mode 100644 index 0000000000..ae0aeb2821 --- /dev/null +++ b/source/uri-options/tests/sdam-options.json @@ -0,0 +1,46 @@ +{ + "tests": [ + { + "description": "serverMonitoringMode=auto", + "uri": "mongodb://example.com/?serverMonitoringMode=auto", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "serverMonitoringMode": "auto" + } + }, + { + "description": "serverMonitoringMode=stream", + "uri": "mongodb://example.com/?serverMonitoringMode=stream", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "serverMonitoringMode": "stream" + } + }, + { + "description": "serverMonitoringMode=poll", + "uri": "mongodb://example.com/?serverMonitoringMode=poll", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "serverMonitoringMode": "poll" + } + }, + { + "description": "invalid serverMonitoringMode", + "uri": "mongodb://example.com/?serverMonitoringMode=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/sdam-options.yml b/source/uri-options/tests/sdam-options.yml new file mode 100644 index 0000000000..92294b6e52 --- /dev/null +++ b/source/uri-options/tests/sdam-options.yml @@ -0,0 +1,35 @@ +tests: + - description: "serverMonitoringMode=auto" + uri: "mongodb://example.com/?serverMonitoringMode=auto" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + serverMonitoringMode: "auto" + + - description: "serverMonitoringMode=stream" + uri: "mongodb://example.com/?serverMonitoringMode=stream" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + serverMonitoringMode: "stream" + + - description: "serverMonitoringMode=poll" + uri: "mongodb://example.com/?serverMonitoringMode=poll" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + serverMonitoringMode: "poll" + + - description: "invalid serverMonitoringMode" + uri: "mongodb://example.com/?serverMonitoringMode=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/single-threaded-options.json b/source/uri-options/tests/single-threaded-options.json new file mode 100644 index 0000000000..80ac3fa4ee --- /dev/null +++ b/source/uri-options/tests/single-threaded-options.json @@ -0,0 +1,24 @@ +{ + "tests": [ + { + "description": "Valid options specific to single-threaded drivers are parsed correctly", + "uri": "mongodb://example.com/?serverSelectionTryOnce=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "serverSelectionTryOnce": false + } + }, + { + "description": "Invalid serverSelectionTryOnce causes a warning", + "uri": "mongodb://example.com/?serverSelectionTryOnce=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/single-threaded-options.yml b/source/uri-options/tests/single-threaded-options.yml new file mode 100644 index 0000000000..1a94c8e07d --- /dev/null +++ b/source/uri-options/tests/single-threaded-options.yml @@ -0,0 +1,18 @@ +tests: + - + description: "Valid options specific to single-threaded drivers are parsed correctly" + uri: "mongodb://example.com/?serverSelectionTryOnce=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + serverSelectionTryOnce: false + - + description: "Invalid serverSelectionTryOnce causes a warning" + uri: "mongodb://example.com/?serverSelectionTryOnce=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/tests/srv-options.json b/source/uri-options/tests/srv-options.json new file mode 100644 index 0000000000..0670612c0d --- /dev/null +++ b/source/uri-options/tests/srv-options.json @@ -0,0 +1,116 @@ +{ + "tests": [ + { + "description": "SRV URI with custom srvServiceName", + "uri": "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "srvServiceName": "customname" + } + }, + { + "description": "Non-SRV URI with custom srvServiceName", + "uri": "mongodb://example.com/?srvServiceName=customname", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "SRV URI with srvMaxHosts", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "srvMaxHosts": 2 + } + }, + { + "description": "SRV URI with negative integer for srvMaxHosts", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=-1", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "SRV URI with invalid type for srvMaxHosts", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=foo", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "Non-SRV URI with srvMaxHosts", + "uri": "mongodb://example.com/?srvMaxHosts=2", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "SRV URI with positive srvMaxHosts and replicaSet", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&replicaSet=foo", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "SRV URI with positive srvMaxHosts and loadBalanced=true", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "SRV URI with positive srvMaxHosts and loadBalanced=false", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "loadBalanced": false, + "srvMaxHosts": 2 + } + }, + { + "description": "SRV URI with srvMaxHosts=0 and replicaSet", + "uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0&replicaSet=foo", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "replicaSet": "foo", + "srvMaxHosts": 0 + } + }, + { + "description": "SRV URI with srvMaxHosts=0 and loadBalanced=true", + "uri": "mongodb+srv://test3.test.build.10gen.cc/?srvMaxHosts=0&loadBalanced=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "loadBalanced": true, + "srvMaxHosts": 0 + } + } + ] +} diff --git a/source/uri-options/tests/srv-options.yml b/source/uri-options/tests/srv-options.yml new file mode 100644 index 0000000000..991749b0ef --- /dev/null +++ b/source/uri-options/tests/srv-options.yml @@ -0,0 +1,89 @@ +tests: + - description: "SRV URI with custom srvServiceName" + uri: "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + srvServiceName: "customname" + - description: "Non-SRV URI with custom srvServiceName" + uri: "mongodb://example.com/?srvServiceName=customname" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - description: "SRV URI with srvMaxHosts" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + srvMaxHosts: 2 + - description: "SRV URI with negative integer for srvMaxHosts" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=-1" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - description: "SRV URI with invalid type for srvMaxHosts" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=foo" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - description: "Non-SRV URI with srvMaxHosts" + uri: "mongodb://example.com/?srvMaxHosts=2" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # Note: Testing URI validation for srvMaxHosts conflicting with either + # loadBalanced=true or replicaSet specified via TXT records is covered by + # the Initial DNS Seedlist Discovery test suite. + - description: "SRV URI with positive srvMaxHosts and replicaSet" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&replicaSet=foo" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - description: "SRV URI with positive srvMaxHosts and loadBalanced=true" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - description: "SRV URI with positive srvMaxHosts and loadBalanced=false" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + loadBalanced: false + srvMaxHosts: 2 + - description: "SRV URI with srvMaxHosts=0 and replicaSet" + uri: "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0&replicaSet=foo" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + replicaSet: foo + srvMaxHosts: 0 + - description: "SRV URI with srvMaxHosts=0 and loadBalanced=true" + uri: "mongodb+srv://test3.test.build.10gen.cc/?srvMaxHosts=0&loadBalanced=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + loadBalanced: true + srvMaxHosts: 0 diff --git a/source/uri-options/tests/tls-options.json b/source/uri-options/tests/tls-options.json new file mode 100644 index 0000000000..526cde1cbe --- /dev/null +++ b/source/uri-options/tests/tls-options.json @@ -0,0 +1,640 @@ +{ + "tests": [ + { + "description": "Valid required tls options are parsed correctly", + "uri": "mongodb://example.com/?tls=true&tlsCAFile=ca.pem&tlsCertificateKeyFile=cert.pem", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tls": true, + "tlsCAFile": "ca.pem", + "tlsCertificateKeyFile": "cert.pem" + } + }, + { + "description": "Valid tlsCertificateKeyFilePassword is parsed correctly", + "uri": "mongodb://example.com/?tlsCertificateKeyFilePassword=hunter2", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tlsCertificateKeyFilePassword": "hunter2" + } + }, + { + "description": "Invalid tlsAllowInvalidCertificates causes a warning", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates is parsed correctly", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tlsAllowInvalidCertificates": true + } + }, + { + "description": "tlsAllowInvalidHostnames is parsed correctly", + "uri": "mongodb://example.com/?tlsAllowInvalidHostnames=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tlsAllowInvalidHostnames": true + } + }, + { + "description": "Invalid tlsAllowInvalidHostnames causes a warning", + "uri": "mongodb://example.com/?tlsAllowInvalidHostnames=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure is parsed correctly", + "uri": "mongodb://example.com/?tlsInsecure=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tlsInsecure": true + } + }, + { + "description": "Invalid tlsInsecure causes a warning", + "uri": "mongodb://example.com/?tlsInsecure=invalid", + "valid": true, + "warning": true, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsAllowInvalidCertificates both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsAllowInvalidCertificates=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsAllowInvalidCertificates both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsAllowInvalidCertificates=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates and tlsInsecure both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates and tlsInsecure both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsAllowInvalidHostnames both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsAllowInvalidHostnames=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsAllowInvalidHostnames both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsAllowInvalidHostnames=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidHostnames and tlsInsecure both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidHostnames=true&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidHostnames and tlsInsecure both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidHostnames=false&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tls=true and ssl=true doesn't warn", + "uri": "mongodb://example.com/?tls=true&ssl=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "tls=false and ssl=false doesn't warn", + "uri": "mongodb://example.com/?tls=false&ssl=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "ssl=true and tls=true doesn't warn", + "uri": "mongodb://example.com/?ssl=true&tls=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "ssl=false and tls=false doesn't warn", + "uri": "mongodb://example.com/?ssl=false&tls=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": {} + }, + { + "description": "tls=false and ssl=true raises error", + "uri": "mongodb://example.com/?tls=false&ssl=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tls=true and ssl=false raises error", + "uri": "mongodb://example.com/?tls=true&ssl=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "ssl=false and tls=true raises error", + "uri": "mongodb://example.com/?ssl=false&tls=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "ssl=true and tls=false raises error", + "uri": "mongodb://example.com/?ssl=true&tls=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck can be set to true", + "uri": "mongodb://example.com/?tls=true&tlsDisableCertificateRevocationCheck=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tls": true, + "tlsDisableCertificateRevocationCheck": true + } + }, + { + "description": "tlsDisableCertificateRevocationCheck can be set to false", + "uri": "mongodb://example.com/?tls=true&tlsDisableCertificateRevocationCheck=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tls": true, + "tlsDisableCertificateRevocationCheck": false + } + }, + { + "description": "tlsAllowInvalidCertificates and tlsDisableCertificateRevocationCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates=true and tlsDisableCertificateRevocationCheck=false raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates=false and tlsDisableCertificateRevocationCheck=true raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates and tlsDisableCertificateRevocationCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsAllowInvalidCertificates both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsAllowInvalidCertificates=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=true and tlsAllowInvalidCertificates=false raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsAllowInvalidCertificates=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=false and tlsAllowInvalidCertificates=true raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsAllowInvalidCertificates=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsAllowInvalidCertificates both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsAllowInvalidCertificates=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsDisableCertificateRevocationCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure=true and tlsDisableCertificateRevocationCheck=false raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure=false and tlsDisableCertificateRevocationCheck=true raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsDisableCertificateRevocationCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsInsecure both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=true and tlsInsecure=false raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=false and tlsInsecure=true raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsInsecure both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsDisableOCSPEndpointCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=true and tlsDisableOCSPEndpointCheck=false raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck=false and tlsDisableOCSPEndpointCheck=true raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableCertificateRevocationCheck and tlsDisableOCSPEndpointCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsDisableCertificateRevocationCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=true and tlsDisableCertificateRevocationCheck=false raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=false and tlsDisableCertificateRevocationCheck=true raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsDisableCertificateRevocationCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsDisableCertificateRevocationCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsDisableCertificateRevocationCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck can be set to true", + "uri": "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=true", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tls": true, + "tlsDisableOCSPEndpointCheck": true + } + }, + { + "description": "tlsDisableOCSPEndpointCheck can be set to false", + "uri": "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=false", + "valid": true, + "warning": false, + "hosts": null, + "auth": null, + "options": { + "tls": true, + "tlsDisableOCSPEndpointCheck": false + } + }, + { + "description": "tlsInsecure and tlsDisableOCSPEndpointCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure=true and tlsDisableOCSPEndpointCheck=false raises an error", + "uri": "mongodb://example.com/?tlsInsecure=true&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure=false and tlsDisableOCSPEndpointCheck=true raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsInsecure and tlsDisableOCSPEndpointCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsInsecure=false&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsInsecure both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=true and tlsInsecure=false raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=false and tlsInsecure=true raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsInsecure=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsInsecure both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsInsecure=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates and tlsDisableOCSPEndpointCheck both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates=true and tlsDisableOCSPEndpointCheck=false raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates=false and tlsDisableOCSPEndpointCheck=true raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableOCSPEndpointCheck=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsAllowInvalidCertificates and tlsDisableOCSPEndpointCheck both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableOCSPEndpointCheck=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsAllowInvalidCertificates both present (and true) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsAllowInvalidCertificates=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=true and tlsAllowInvalidCertificates=false raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsAllowInvalidCertificates=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck=false and tlsAllowInvalidCertificates=true raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsAllowInvalidCertificates=true", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + }, + { + "description": "tlsDisableOCSPEndpointCheck and tlsAllowInvalidCertificates both present (and false) raises an error", + "uri": "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsAllowInvalidCertificates=false", + "valid": false, + "warning": false, + "hosts": null, + "auth": null, + "options": null + } + ] +} diff --git a/source/uri-options/tests/tls-options.yml b/source/uri-options/tests/tls-options.yml new file mode 100644 index 0000000000..891b4582a7 --- /dev/null +++ b/source/uri-options/tests/tls-options.yml @@ -0,0 +1,570 @@ +tests: + - + description: "Valid required tls options are parsed correctly" + uri: "mongodb://example.com/?tls=true&tlsCAFile=ca.pem&tlsCertificateKeyFile=cert.pem" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tls: true + tlsCAFile: "ca.pem" + tlsCertificateKeyFile: "cert.pem" + - + description: "Valid tlsCertificateKeyFilePassword is parsed correctly" + uri: "mongodb://example.com/?tlsCertificateKeyFilePassword=hunter2" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tlsCertificateKeyFilePassword: "hunter2" + - + description: "Invalid tlsAllowInvalidCertificates causes a warning" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates is parsed correctly" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tlsAllowInvalidCertificates: true + - + description: "tlsAllowInvalidHostnames is parsed correctly" + uri: "mongodb://example.com/?tlsAllowInvalidHostnames=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tlsAllowInvalidHostnames: true + - + description: "Invalid tlsAllowInvalidHostnames causes a warning" + uri: "mongodb://example.com/?tlsAllowInvalidHostnames=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure is parsed correctly" + uri: "mongodb://example.com/?tlsInsecure=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tlsInsecure: true + - + description: "Invalid tlsInsecure causes a warning" + uri: "mongodb://example.com/?tlsInsecure=invalid" + valid: true + warning: true + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsAllowInvalidCertificates both present (and true) raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsAllowInvalidCertificates=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsAllowInvalidCertificates both present (and false) raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsAllowInvalidCertificates=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates and tlsInsecure both present (and true) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates and tlsInsecure both present (and false) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsAllowInvalidHostnames both present (and true) raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsAllowInvalidHostnames=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsAllowInvalidHostnames both present (and false) raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsAllowInvalidHostnames=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidHostnames and tlsInsecure both present (and true) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidHostnames=true&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidHostnames and tlsInsecure both present (and false) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidHostnames=false&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tls=true and ssl=true doesn't warn" + uri: "mongodb://example.com/?tls=true&ssl=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "tls=false and ssl=false doesn't warn" + uri: "mongodb://example.com/?tls=false&ssl=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "ssl=true and tls=true doesn't warn" + uri: "mongodb://example.com/?ssl=true&tls=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "ssl=false and tls=false doesn't warn" + uri: "mongodb://example.com/?ssl=false&tls=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: {} + - + description: "tls=false and ssl=true raises error" + uri: "mongodb://example.com/?tls=false&ssl=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tls=true and ssl=false raises error" + uri: "mongodb://example.com/?tls=true&ssl=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "ssl=false and tls=true raises error" + uri: "mongodb://example.com/?ssl=false&tls=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "ssl=true and tls=false raises error" + uri: "mongodb://example.com/?ssl=true&tls=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck can be set to true" + uri: "mongodb://example.com/?tls=true&tlsDisableCertificateRevocationCheck=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tls: true + tlsDisableCertificateRevocationCheck: true + - + description: "tlsDisableCertificateRevocationCheck can be set to false" + uri: "mongodb://example.com/?tls=true&tlsDisableCertificateRevocationCheck=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tls: true + tlsDisableCertificateRevocationCheck: false + # 4 permutations of [tlsAllowInvalidCertificates=true/false, tlsDisableCertificateRevocationCheck=true/false] + - + description: "tlsAllowInvalidCertificates and tlsDisableCertificateRevocationCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates=true and tlsDisableCertificateRevocationCheck=false raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates=false and tlsDisableCertificateRevocationCheck=true raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates and tlsDisableCertificateRevocationCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableCertificateRevocationCheck=true/false, tlsAllowInvalidCertificates=true/false] + - + description: "tlsDisableCertificateRevocationCheck and tlsAllowInvalidCertificates both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsAllowInvalidCertificates=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=true and tlsAllowInvalidCertificates=false raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsAllowInvalidCertificates=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=false and tlsAllowInvalidCertificates=true raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsAllowInvalidCertificates=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck and tlsAllowInvalidCertificates both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsAllowInvalidCertificates=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsInsecure=true/false, tlsDisableCertificateRevocationCheck=true/false] + - + description: "tlsInsecure and tlsDisableCertificateRevocationCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure=true and tlsDisableCertificateRevocationCheck=false raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure=false and tlsDisableCertificateRevocationCheck=true raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsDisableCertificateRevocationCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableCertificateRevocationCheck=true/false, tlsInsecure=true/false] + - + description: "tlsDisableCertificateRevocationCheck and tlsInsecure both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=true and tlsInsecure=false raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=false and tlsInsecure=true raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck and tlsInsecure both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableCertificateRevocationCheck=true/false, tlsDisableOCSPEndpointCheck=true/false] + - + description: "tlsDisableCertificateRevocationCheck and tlsDisableOCSPEndpointCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=true and tlsDisableOCSPEndpointCheck=false raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=true&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck=false and tlsDisableOCSPEndpointCheck=true raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableCertificateRevocationCheck and tlsDisableOCSPEndpointCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableCertificateRevocationCheck=false&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableOCSPEndpointCheck=true/false, tlsDisableCertificateRevocationCheck=true/false] + - + description: "tlsDisableOCSPEndpointCheck and tlsDisableCertificateRevocationCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=true and tlsDisableCertificateRevocationCheck=false raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=false and tlsDisableCertificateRevocationCheck=true raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsDisableCertificateRevocationCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck and tlsDisableCertificateRevocationCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsDisableCertificateRevocationCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck can be set to true" + uri: "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=true" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tls: true + tlsDisableOCSPEndpointCheck: true + - + description: "tlsDisableOCSPEndpointCheck can be set to false" + uri: "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=false" + valid: true + warning: false + hosts: ~ + auth: ~ + options: + tls: true + tlsDisableOCSPEndpointCheck: false + # 4 permutations of [tlsInsecure=true/false, tlsDisableOCSPEndpointCheck=true/false] + - + description: "tlsInsecure and tlsDisableOCSPEndpointCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure=true and tlsDisableOCSPEndpointCheck=false raises an error" + uri: "mongodb://example.com/?tlsInsecure=true&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure=false and tlsDisableOCSPEndpointCheck=true raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsInsecure and tlsDisableOCSPEndpointCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsInsecure=false&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableOCSPEndpointCheck=true/false, tlsInsecure=true/false] + - + description: "tlsDisableOCSPEndpointCheck and tlsInsecure both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=true and tlsInsecure=false raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=false and tlsInsecure=true raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsInsecure=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck and tlsInsecure both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsInsecure=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsAllowInvalidCertificates=true/false, tlsDisableOCSPEndpointCheck=true/false] + - + description: "tlsAllowInvalidCertificates and tlsDisableOCSPEndpointCheck both present (and true) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates=true and tlsDisableOCSPEndpointCheck=false raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=true&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates=false and tlsDisableOCSPEndpointCheck=true raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableOCSPEndpointCheck=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsAllowInvalidCertificates and tlsDisableOCSPEndpointCheck both present (and false) raises an error" + uri: "mongodb://example.com/?tlsAllowInvalidCertificates=false&tlsDisableOCSPEndpointCheck=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + # 4 permutations of [tlsDisableOCSPEndpointCheck=true/false, tlsAllowInvalidCertificates=true/false] + - + description: "tlsDisableOCSPEndpointCheck and tlsAllowInvalidCertificates both present (and true) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsAllowInvalidCertificates=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=true and tlsAllowInvalidCertificates=false raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=true&tlsAllowInvalidCertificates=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck=false and tlsAllowInvalidCertificates=true raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsAllowInvalidCertificates=true" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ + - + description: "tlsDisableOCSPEndpointCheck and tlsAllowInvalidCertificates both present (and false) raises an error" + uri: "mongodb://example.com/?tlsDisableOCSPEndpointCheck=false&tlsAllowInvalidCertificates=false" + valid: false + warning: false + hosts: ~ + auth: ~ + options: ~ diff --git a/source/uri-options/uri-options.md b/source/uri-options/uri-options.md new file mode 100644 index 0000000000..85a137671c --- /dev/null +++ b/source/uri-options/uri-options.md @@ -0,0 +1,221 @@ +# URI Options Specification + +- Status: Accepted + +- Minimum Server Version: N/A + +## Abstract + +Historically, URI options have been defined in individual specs, and drivers have defined any additional options +independently of one another. Because of the frustration due to there not being a single place where all of the URI +options are defined, this spec aims to do just that—namely, provide a canonical list of URI options that each driver +defines. + +**THIS SPEC DOES NOT REQUIRE DRIVERS TO MAKE ANY BREAKING CHANGES.** + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +## Specification + +### Conflicting TLS options + +Per the [Connection String spec](../connection-string/connection-string-spec.md#repeated-keys), the behavior of +duplicates of most URI options is undefined. However, due to the security implications of certain options, drivers MUST +raise an error to the user during parsing if any of the following circumstances occur: + +1. Both `tlsInsecure` and `tlsAllowInvalidCertificates` appear in the URI options. +2. Both `tlsInsecure` and `tlsAllowInvalidHostnames` appear in the URI options. +3. Both `tlsInsecure` and `tlsDisableOCSPEndpointCheck` appear in the URI options. +4. Both `tlsInsecure` and `tlsDisableCertificateRevocationCheck` appear in the URI options. +5. Both `tlsAllowInvalidCertificates` and `tlsDisableOCSPEndpointCheck` appear in the URI options. +6. Both `tlsAllowInvalidCertificates` and `tlsDisableCertificateRevocationCheck` appear in the URI options. +7. Both `tlsDisableOCSPEndpointCheck` and `tlsDisableCertificateRevocationCheck` appear in the URI options. +8. All instances of `tls` and `ssl` in the URI options do not have the same value. If all instances of `tls` and `ssl` + have the same value, an error MUST NOT be raised. + +### directConnection URI option with multiple seeds or SRV URI + +The driver MUST report an error if the `directConnection=true` URI option is specified with multiple seeds. + +The driver MUST report an error if the `directConnection=true` URI option is specified with an SRV URI, because the URI +may resolve to multiple hosts. The driver MUST allow specifying `directConnection=false` URI option with an SRV URI. + +### srvServiceName and srvMaxHosts URI options + +For URI option validation pertaining to `srvServiceName` and `srvMaxHosts`, please see the +[Initial DNS Seedlist Discovery spec](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#uri-validation) +for details. + +### Load Balancer Mode + +For URI option validation in Load Balancer mode (i.e. `loadBalanced=true`), please see the +[Load Balancer spec](../load-balancers/load-balancers.md#uri-validation) for details. + +### SOCKS5 options + +For URI option validation pertaining to `proxyHost`, `proxyPort`, `proxyUsername` and `proxyPassword` please see the +[SOCKS5 support spec](../socks5-support/socks5.md#mongoclient-configuration) for details. + +### List of specified options + +Each driver option below MUST be implemented in each driver unless marked as optional. If an option is marked as +optional, a driver MUST meet any conditions specified for leaving it out if it is not included. If a driver already +provides the option under a different name, the driver MAY implement the old and new names as aliases. All keys and +values MUST be encoded in UTF-8. All integer options are 32-bit unless specified otherwise. Note that all requirements +and recommendations described in the [Connection String spec](../connection-string/connection-string-spec.md) pertaining +to URI options apply here. + + + +| Name | Accepted Values | Default Value | Optional to implement? | Description | +| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| maxAdaptiveRetries | a non-negative integer | 2 | no | The maximum number of overload retries as described by the [client backpressure spec](../client-backpressure/client-backpressure.md#overload-retry-policy) | +| enableOverloadRetargeting | "true" or "false" | "false" | no | Whether or not to enable overload retargeting as described by the [client backpressure spec](../client-backpressure/client-backpressure.md#overload-retry-policy) | +| appname | any string that meets the criteria listed in the [handshake spec](../mongodb-handshake/handshake.md#client-application-name) | no appname specified | no | Passed into the server in the client metadata as part of the connection handshake | +| authMechanism | any string; valid values are defined in the [auth spec](../auth/auth.md#supported-authentication-methods) | None; default values for authentication exist for constructing authentication credentials per the [auth spec](../auth/auth.md#supported-authentication-methods), but there is no default for the URI option itself. | no | The authentication mechanism method to use for connection to the server | +| authMechanismProperties | comma separated key:value pairs, e.g. "opt1:val1,opt2:val2" | no properties specified | no | Additional options provided for authentication (e.g. to enable hostname canonicalization for GSSAPI) | +| authSource | any string | None; default values for authentication exist for constructing authentication credentials per the [auth spec](../auth/auth.md#supported-authentication-methods), but there is no default for the URI option itself. | no | The database that connections should authenticate against | +| compressors | comma separated list of strings, e.g. "snappy,zlib" | defined in [compression spec](../compression/OP_COMPRESSED.md#compressors) | no | The list of allowed compression types for wire protocol messages sent or received from the server | +| connectTimeoutMS | non-negative integer; 0 means "no timeout" | 10,000 ms (unless a driver already has a different default) | no | Amount of time to wait for a single TCP socket connection to the server to be established before erroring; note that this applies to [SDAM hello and legacy hello operations](../mongodb-handshake/handshake.md) | +| directConnection | "true" or "false" | defined in [SDAM spec](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#initial-topologytype) | no | Whether to connect to the deployment in Single topology. | +| heartbeatFrequencyMS | integer greater than or equal to 500 | defined in [SDAM spec](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#heartbeatfrequencyms) | no | the interval between regular server monitoring checks | +| journal | "true" or "false" | no "j" field specified | no | Default write concern "j" field for the client | +| loadBalanced | "true" or "false" | defined in [Load Balancer spec](../load-balancers/load-balancers.md#loadbalanced) | no | Whether the driver is connecting to a load balancer. | +| localThresholdMS | non-negative integer; 0 means 0 ms (i.e. the fastest eligible server must be selected) | defined in the [server selection spec](../server-selection/server-selection.md#localthresholdms) | no | The amount of time beyond the fastest round trip time that a given server’s round trip time can take and still be eligible for server selection | +| maxIdleTimeMS | non-negative integer; 0 means no minimum | defined in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | required for drivers with connection pools | The amount of time a connection can be idle before it's closed | +| maxPoolSize | non-negative integer; 0 means no maximum | defined in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | required for drivers with connection pools | The maximum number of clients or connections able to be created by a pool at a given time. This count includes connections which are currently checked out. | +| maxConnecting | positive integer | defined in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | required for drivers with connection pools | The maximum number of Connections a Pool may be establishing concurrently. | +| maxStalenessSeconds | -1 (no max staleness check) or integer >= 90 | defined in [max staleness spec](../max-staleness/max-staleness.md#api) | no | The maximum replication lag, in wall clock time, that a secondary can suffer and still be eligible for server selection | +| minPoolSize | non-negative integer | defined in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | required for drivers with connection pools | The number of connections the driver should create and maintain in the pool even when no operations are occurring. This count includes connections which are currently checked out. | +| proxyHost | any string | defined in the [SOCKS5 support spec](../socks5-support/socks5.md#mongoclient-configuration) | no | The IPv4/IPv6 address or domain name of a SOCKS5 proxy server used for connecting to MongoDB services. | +| proxyPort | non-negative integer | defined in the [SOCKS5 support spec](../socks5-support/socks5.md#mongoclient-configuration) | no | The port of the SOCKS5 proxy server specified in `proxyHost`. | +| proxyUsername | any string | defined in the [SOCKS5 support spec](../socks5-support/socks5.md#mongoclient-configuration) | no | The username for username/password authentication to the SOCKS5 proxy server specified in `proxyHost`. | +| proxyPassword | any string | defined in the [SOCKS5 support spec](../socks5-support/socks5.md#mongoclient-configuration) | no | The password for username/password authentication to the SOCKS5 proxy server specified in `proxyHost`. | +| readConcernLevel | any string ([to allow for forwards compatibility with the server](../read-write-concern/read-write-concern.md#unknown-levels-and-additional-options-for-string-based-readconcerns)) | no read concern specified | no | Default read concern for the client | +| readPreference | any string; currently supported values are defined in the [server selection spec](../server-selection/server-selection.md#mode), but must be lowercase camelCase, e.g. "primaryPreferred" | defined in [server selection spec](../server-selection/server-selection.md#mode) | no | Default read preference for the client (excluding tags) | +| readPreferenceTags | comma-separated key:value pairs (e.g. "dc:ny,rack:1" and "dc:ny)

can be specified multiple times; each instance of this key is a separate tag set | no tags specified | no | Default read preference tags for the client; only valid if the read preference mode is not primary

The order of the tag sets in the read preference is the same as the order they are specified in the URI | +| replicaSet | any string | no replica set name provided | no | The name of the replica set to connect to | +| retryReads | "true" or "false" | defined in [retryable reads spec](../retryable-reads/retryable-reads.md#retryreads) | no | Enables retryable reads on server 3.6+ | +| retryWrites | "true" or "false" | defined in [retryable writes spec](../retryable-writes/retryable-writes.md#retrywrites) | no | Enables retryable writes on server 3.6+ | +| serverMonitoringMode | "stream", "poll", or "auto" | defined in [SDAM spec](../server-discovery-and-monitoring/server-monitoring.md#servermonitoringmode) | required for multi-threaded or asynchronous drivers | Configures which server monitoring protocol to use. | +| serverSelectionTimeoutMS | positive integer; a driver may also accept 0 to be used for a special case, provided that it documents the meaning | defined in [server selection spec](../server-selection/server-selection.md#serverselectiontimeoutms) | no | A timeout in milliseconds to block for server selection before raising an error | +| serverSelectionTryOnce | "true" or "false" | defined in [server selection spec](../server-selection/server-selection.md#serverselectiontryonce) | required for single-threaded drivers | Scan the topology only once after a server selection failure instead of repeatedly until the server selection times out | +| socketTimeoutMS | non-negative integer; 0 means no timeout | no timeout | no | NOTE: This option is deprecated in favor of [timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms)

Amount of time spent attempting to send or receive on a socket before timing out; note that this only applies to application operations, not SDAM. | +| srvMaxHosts | non-negative integer; 0 means no maximum | defined in the [Initial DNS Seedlist Discovery spec](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvmaxhosts) | no | The maximum number of SRV results to randomly select when initially populating the seedlist or, during SRV polling, adding new hosts to the topology. | +| srvServiceName | a valid SRV service name according to [RFC 6335](https://datatracker.ietf.org/doc/html/rfc6335#section-5.1) | "mongodb" | no | the service name to use for SRV lookup in [initial DNS seedlist discovery](../initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md#srvservicename) and [SRV polling](../polling-srv-records-for-mongos-discovery/polling-srv-records-for-mongos-discovery.md) | +| ssl | "true" or "false" | same as "tls" | no | alias of "tls"; required to ensure that Atlas connection strings continue to work | +| | | | | | +| tls | "true" or "false" | TLS required if "mongodb+srv" scheme; otherwise, drivers may may enable TLS by default if other "tls"-prefixed options are present

Drivers MUST clearly document the conditions under which TLS is enabled implicitly | no | Whether or not to require TLS for connections to the server | +| tlsAllowInvalidCertificates | "true" or "false" | error on invalid certificates | required if the driver’s language/runtime allows bypassing hostname verification | Specifies whether or not the driver should error when the server’s TLS certificate is invalid | +| tlsAllowInvalidHostnames | "true" or "false" | error on invalid certificates | required if the driver’s language/runtime allows bypassing hostname verification | Specifies whether or not the driver should error when there is a mismatch between the server’s hostname and the hostname specified by the TLS certificate | +| tlsCAFile | any string | no certificate authorities specified | required if the driver's language/runtime allows non-global configuration | Path to file with either a single or bundle of certificate authorities to be considered trusted when making a TLS connection | +| tlsCertificateKeyFile | any string | no client certificate specified | required if the driver's language/runtime allows non-global configuration | Path to the client certificate file or the client private key file; in the case that they both are needed, the files should be concatenated | +| tlsCertificateKeyFilePassword | any string | no password specified | required if the driver's language/runtime allows non-global configuration | Password to decrypt the client private key to be used for TLS connections | +| tlsDisableCertificateRevocationCheck | "true" or "false" | false i.e. driver will reach check a certificate's revocation status | Yes | Controls whether or not the driver will check a certificate's revocation status via CRLs or OCSP. See the [OCSP Support Spec](../ocsp-support/ocsp-support.md#tlsdisablecertificaterevocationcheck) for additional information. | +| tlsDisableOCSPEndpointCheck | "true" or "false" | false i.e. driver will reach out to OCSP endpoints [if needed](../ocsp-support/ocsp-support.md#tlsdisableocspendpointcheck). | Yes | Controls whether or not the driver will reach out to OCSP endpoints if needed. See the [OCSP Support Spec](../ocsp-support/ocsp-support.md#tlsdisableocspendpointcheck) for additional information. | +| tlsInsecure | "true" or "false" | No TLS constraints are relaxed | no | Relax TLS constraints as much as possible (e.g. allowing invalid certificates or hostname mismatches); drivers must document the exact constraints which are relaxed by this option being true | +| w | non-negative integer or string | no "w" value specified | no | Default write concern "w" field for the client | +| waitQueueTimeoutMS | positive number | defined in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | required for drivers with connection pools, with exceptions described in the [Connection Pooling spec](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool-options) | NOTE: This option is deprecated in favor of [timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms)

Amount of time spent attempting to check out a connection from a server's connection pool before timing out | +| wTimeoutMS | non-negative 64-bit integer; 0 means no timeout | no timeout | no | NOTE: This option is deprecated in favor of [timeoutMS](../client-side-operations-timeout/client-side-operations-timeout.md#timeoutms)

Default write concern "wtimeout" field for the client | +| zlibCompressionLevel | integer between -1 and 9 (inclusive) | -1 (default compression level of the driver) | no | Specifies the level of compression when using zlib to compress wire protocol messages; -1 signifies the default level, 0 signifies no compression, 1 signifies the fastest speed, and 9 signifies the best compression | + +## Test Plan + +Tests are implemented and described in the [tests](tests/README.md) directory. + +## Design Rationale + +### Why allow drivers to provide the canonical names as aliases to existing options? + +First and foremost, this spec aims not to introduce any breaking changes to drivers. Forcing a driver to change the name +of an option that it provides will break any applications that use the old option. Moreover, it is already possible to +provide duplicate options in the URI by specifying the same option more than once; drivers can use the same semantics to +resolve the conflicts as they did before, whether it's raising an error, using the first option provided, using the last +option provided, or simply telling users that the behavior is not defined. + +### Why use "tls" as the prefix instead of "ssl" for related options? + +Technically speaking, drivers already only support TLS, which supersedes SSL. While SSL is commonly used in parlance to +refer to TLS connections, the fact remains that SSL is a weaker cryptographic protocol than TLS, and we want to +accurately reflect the strict requirements that drivers have in ensuring the security of a TLS connection. + +### Why use the names "tlsAllowInvalidHostnames" and "tlsAllowInvalidCertificates"? + +The "tls" prefix is used for the same reasons described above. The use of the terms "AllowInvalidHostnames" and +"AllowInvalidCertificates" is an intentional choice in order to convey the inherent unsafety of these options, which +should only be used for testing purposes. Additionally, both the server and the shell use "AllowInvalid" for their +equivalent options. + +### Why provide multiple implementation options for the insecure TLS options (i.e. "tlsInsecure" vs. "tlsAllowInvalidHostnames"/"tlsAllowInvalidCertificates"? + +Some TLS libraries (e.g. Go's standard library implementation) do not provide the ability to distinguish between allow +invalid certificates and hostnames, meaning they either both are allowed, or neither are. However, when more granular +options are available, it's better to expose these to the user to allow them to relax security constraints as little as +they need. + +### Why leave the decision up to drivers to enable TLS implicitly when TLS options are present? + +It can be useful to turn on TLS implicitly when options such as "tlsCAFile" are present and "tls" is not present. +However, with options such as "tlsAllowInvalidHostnames", some drivers may not have the ability to distinguish between +"false" being provided and the option not being specified. To keep the implicit enabling of TLS consistent between such +options, we defer the decision to enable TLS based on the presence of "tls"-prefixed options (besides "tls" itself) to +drivers. + +## Reference Implementations + +Ruby and Python + +## Security Implication + +Each of the "insecure" TLS options (i.e. "tlsInsecure", "tlsAllowInvalidHostnames", "tlsAllowInvalidCertificates", +"tlsDisableOCSPEndpointCheck", and "tlsDisableCertificateRevocationCheck") default to the more secure option when TLS is +enabled. In order to be backwards compatible with existing driver behavior, neither TLS nor authentication is enabled by +default. + +## Future Work + +This specification is intended to represent the current state of drivers URI options rather than be a static description +of the options at the time it was written. Whenever another specification is written or modified in a way that changes +the name or the semantics of a URI option or adds a new URI option, this specification MUST be updated to reflect those +changes. + +## Changelog + +- 2024-05-08: Migrated from reStructuredText to Markdown. + +- 2023-08-21: Add serverMonitoringMode option. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-01-19: Add the timeoutMS option and deprecate some existing timeout options + +- 2021-12-14: Add SOCKS5 options + +- 2021-11-08: Add maxConnecting option. + +- 2021-10-14: Add srvMaxHosts option. Merge headings discussing URI validation for directConnection option. + +- 2021-09-15: Add srvServiceName option + +- 2021-09-13: Fix link to load balancer spec + +- 2021-04-15: Adding in behaviour for load balancer mode. + +- 2021-04-08: Updated to refer to hello and legacy hello + +- 2020-03-03: Add tlsDisableCertificateRevocationCheck option + +- 2020-02-26: Add tlsDisableOCSPEndpointCheck option + +- 2019-09-08: Add retryReads option + +- 2019-04-26: authSource and authMechanism have no default value + +- 2019-02-04: Specified errors for conflicting TLS-related URI options + +- 2019-01-25: Updated to reflect new Connection Monitoring and Pooling Spec + +______________________________________________________________________ diff --git a/source/uri-options/uri-options.rst b/source/uri-options/uri-options.rst new file mode 100644 index 0000000000..00c450c4a3 --- /dev/null +++ b/source/uri-options/uri-options.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `uri-options.md `_. diff --git a/source/uuid.rst b/source/uuid.rst new file mode 100644 index 0000000000..134cb293b9 --- /dev/null +++ b/source/uuid.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `uuid.md `_. diff --git a/source/versioned-api/tests/README.md b/source/versioned-api/tests/README.md new file mode 100644 index 0000000000..46f0a1f928 --- /dev/null +++ b/source/versioned-api/tests/README.md @@ -0,0 +1,24 @@ +# Stable API Tests + +______________________________________________________________________ + +## Notes + +This directory contains tests for the Stable API specification. They are implemented in the +[Unified Test Format](../../unified-test-format/unified-test-format.md), and require schema version 1.1. Note that to +run these tests, the server must be started with both `enableTestCommands` and `acceptApiVersion2` parameters set to +true. + +## Testing with required API version + +Drivers MUST run their test suite against a cluster with the `requireApiVersion` parameter enabled and also requires +authentication. + +To run this test, proceed as follows: - Start a standalone mongod instance + +- Connect to the standalone instance and run the following command on the `admin` database: + `{ setParameter: 1, requireApiVersion: true }` +- Declare an API version for the test run through the `MONGODB_API_VERSION` environment variable. +- If the environment variable is set, all clients created in tests MUST declare the `ServerApiVersion` specified. + +Only standalone servers must be tested. The tests should run for each server version >= 5.0, including `latest`. diff --git a/source/versioned-api/tests/crud-api-version-1-strict.json b/source/versioned-api/tests/crud-api-version-1-strict.json new file mode 100644 index 0000000000..c1c8ecce01 --- /dev/null +++ b/source/versioned-api/tests/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/source/versioned-api/tests/crud-api-version-1-strict.yml b/source/versioned-api/tests/crud-api-version-1-strict.yml new file mode 100644 index 0000000000..17c4bee0f9 --- /dev/null +++ b/source/versioned-api/tests/crud-api-version-1-strict.yml @@ -0,0 +1,419 @@ +description: "CRUD Api Version 1 (strict)" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.9" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + serverApi: + version: "1" + strict: true + - database: + id: &database database + client: *client + databaseName: &databaseName versioned-api-tests + - database: + id: &adminDatabase adminDatabase + client: *client + databaseName: &adminDatabaseName admin + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +_yamlAnchors: + versions: + - &expectedApiVersion + apiVersion: "1" + apiStrict: true + apiDeprecationErrors: { $$unsetOrMatches: false } + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: &pipeline + - $sort: { x : 1 } + - $match: { _id: { $gt: 1 } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: *pipeline + <<: *expectedApiVersion + + - description: "aggregate on database appends declared API version" + runOnRequirements: + # serverless does not support either of the current database-level aggregation stages ($listLocalSessions and + # $currentOp) + - serverless: "forbid" + operations: + - name: aggregate + object: *adminDatabase + arguments: + pipeline: &pipeline + - $listLocalSessions: {} + - $limit: 1 + expectError: + errorCodeName: "APIStrictError" + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *pipeline + <<: *expectedApiVersion + + - 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: *collectionName + documents: + - { _id: 6, x: 66 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: 2 } + u: { $inc: { x: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { x: { $nin: [ 24, 34 ] } }, limit: 0 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { _id: 7 }, limit: 1 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: 4 } + u: { _id: 4, x: 44 } + multi: { $$unsetOrMatches: false } + upsert: true + <<: *expectedApiVersion + + - description: "countDocuments appends declared API version" + operations: + - name: countDocuments + object: *collection + arguments: + filter: &filter + x : { $gt: 11 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: + - { $match: *filter } + - { $group: { _id: 1, n: { $sum: 1 } } } + <<: *expectedApiVersion + + - description: "deleteMany appends declared API version" + operations: + - name: deleteMany + object: *collection + arguments: + filter: { x: { $nin: [ 24, 34 ] } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { x: { $nin: [ 24, 34 ] } }, limit: 0 } + <<: *expectedApiVersion + + - description: "deleteOne appends declared API version" + operations: + - name: deleteOne + object: *collection + arguments: + filter: { _id: 7 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { _id: 7 }, limit: 1 } + <<: *expectedApiVersion + + # distinct will fail until drivers replace it with an alternative + # implementation + - 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: *collectionName + key: x + <<: *expectedApiVersion + + - description: "estimatedDocumentCount appends declared API version" + # See: https://jira.mongodb.org/browse/SERVER-63850 + 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: *collectionName + <<: *expectedApiVersion + + - 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: *collectionName + <<: *expectedApiVersion + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + <<: *expectedApiVersion + + - description: "findOneAndDelete appends declared API version" + operations: + - name: findOneAndDelete + object: *collection + arguments: + filter: &filter { _id: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + remove: true + <<: *expectedApiVersion + + - description: "findOneAndReplace appends declared API version" + operations: + - name: findOneAndReplace + object: *collection + arguments: + filter: &filter { _id: 1 } + replacement: &replacement { x: 33 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + update: *replacement + <<: *expectedApiVersion + + - description: "findOneAndUpdate appends declared API version" + operations: + - name: findOneAndUpdate + object: collection + arguments: + filter: &filter { _id: 1 } + update: &update { $inc: { x: 1 } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + update: *update + <<: *expectedApiVersion + + - 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: *collectionName + documents: + - { _id: 6, x: 66 } + - { _id: 7, x: 77 } + <<: *expectedApiVersion + + - description: "insertOne appends declared API version" + operations: + - name: insertOne + object: *collection + arguments: + document: { _id: 6, x: 66 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 6, x: 66 } + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: 4 } + u: { _id: 4, x: 44 } + multi: { $$unsetOrMatches: false } + upsert: true + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: 2 } + u: { $inc: { x: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion diff --git a/source/versioned-api/tests/crud-api-version-1.json b/source/versioned-api/tests/crud-api-version-1.json new file mode 100644 index 0000000000..23ef59a6d9 --- /dev/null +++ b/source/versioned-api/tests/crud-api-version-1.json @@ -0,0 +1,1182 @@ +{ + "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 + } + ], + "namespace": "versioned-api-tests.test" + }, + "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": "client bulkWrite appends declared API version", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "clientBulkWrite", + "object": "client", + "arguments": { + "models": [ + { + "insertOne": { + "namespace": "versioned-api-tests.test", + "document": { + "_id": 6, + "x": 6 + } + } + } + ], + "verboseResults": true + }, + "expectResult": { + "insertedCount": 1, + "upsertedCount": 0, + "matchedCount": 0, + "modifiedCount": 0, + "deletedCount": 0, + "insertResults": { + "0": { + "insertedId": 6 + } + }, + "updateResults": {}, + "deleteResults": {} + } + } + ], + "expectEvents": [ + { + "client": "client", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "errorsOnly": false, + "ordered": true, + "ops": [ + { + "insert": 0, + "document": { + "_id": 6, + "x": 6 + } + } + ], + "nsInfo": [ + { + "ns": "versioned-api-tests.test" + } + ], + "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/source/versioned-api/tests/crud-api-version-1.yml b/source/versioned-api/tests/crud-api-version-1.yml new file mode 100644 index 0000000000..01e0323420 --- /dev/null +++ b/source/versioned-api/tests/crud-api-version-1.yml @@ -0,0 +1,455 @@ +description: "CRUD Api Version 1" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.9" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + serverApi: + version: "1" + # Deprecation errors is set to true to ensure that drivers don't use any + # deprecated server API in their logic. + deprecationErrors: true + - database: + id: &database database + client: *client + databaseName: &databaseName versioned-api-tests + - database: + id: &adminDatabase adminDatabase + client: *client + databaseName: &adminDatabaseName admin + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + +_yamlAnchors: + versions: + - &expectedApiVersion + apiVersion: "1" + apiStrict: { $$unsetOrMatches: false } + apiDeprecationErrors: true + namespace: &namespace "versioned-api-tests.test" + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: &pipeline + - $sort: { x : 1 } + - $match: { _id: { $gt: 1 } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: *pipeline + <<: *expectedApiVersion + + - description: "aggregate on database appends declared API version" + runOnRequirements: + # serverless does not support either of the current database-level aggregation stages ($listLocalSessions and + # $currentOp) + - serverless: forbid + operations: + - name: aggregate + object: *adminDatabase + arguments: + pipeline: &pipeline + - $listLocalSessions: {} + - $limit: 1 + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: 1 + pipeline: *pipeline + <<: *expectedApiVersion + + - 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: *collectionName + documents: + - { _id: 6, x: 66 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: 2 } + u: { $inc: { x: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { x: { $nin: [ 24, 34 ] } }, limit: 0 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { _id: 7 }, limit: 1 } + <<: *expectedApiVersion + - commandStartedEvent: + command: + update: *collectionName + updates: + - q: { _id: 4 } + u: { _id: 4, x: 44 } + multi: { $$unsetOrMatches: false } + upsert: true + <<: *expectedApiVersion + + - description: "client bulkWrite appends declared API version" + runOnRequirements: + - minServerVersion: "8.0" # `bulkWrite` added to server 8.0 + serverless: forbid + operations: + - name: clientBulkWrite + object: *client + arguments: + models: + - insertOne: + namespace: *namespace + document: { _id: 6, x: 6 } + verboseResults: true + expectResult: + insertedCount: 1 + upsertedCount: 0 + matchedCount: 0 + modifiedCount: 0 + deletedCount: 0 + insertResults: + 0: + insertedId: 6 + updateResults: {} + deleteResults: {} + expectEvents: + - client: *client + events: + - commandStartedEvent: + commandName: bulkWrite + databaseName: admin + command: + bulkWrite: 1 + errorsOnly: false + ordered: true + ops: + - insert: 0 + document: { _id: 6, x: 6 } + nsInfo: + - { ns: *namespace } + <<: *expectedApiVersion + + - description: "countDocuments appends declared API version" + operations: + - name: countDocuments + object: *collection + arguments: + filter: &filter + x : { $gt: 11 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + aggregate: *collectionName + pipeline: + - { $match: *filter } + - { $group: { _id: 1, n: { $sum: 1 } } } + <<: *expectedApiVersion + + - description: "deleteMany appends declared API version" + operations: + - name: deleteMany + object: *collection + arguments: + filter: { x: { $nin: [ 24, 34 ] } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { x: { $nin: [ 24, 34 ] } }, limit: 0 } + <<: *expectedApiVersion + + - description: "deleteOne appends declared API version" + operations: + - name: deleteOne + object: *collection + arguments: + filter: { _id: 7 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + delete: *collectionName + deletes: + - { q: { _id: 7 }, limit: 1 } + <<: *expectedApiVersion + + - description: "distinct appends declared API version" + operations: + - name: distinct + object: *collection + arguments: + fieldName: x + filter: {} + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + distinct: *collectionName + key: x + <<: *expectedApiVersion + + - description: "estimatedDocumentCount appends declared API version" + # See: https://jira.mongodb.org/browse/SERVER-63850 + 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: *collectionName + <<: *expectedApiVersion + + - 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: *collectionName + <<: *expectedApiVersion + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + <<: *expectedApiVersion + + - description: "findOneAndDelete appends declared API version" + operations: + - name: findOneAndDelete + object: *collection + arguments: + filter: &filter { _id: 1 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + remove: true + <<: *expectedApiVersion + + - description: "findOneAndReplace appends declared API version" + operations: + - name: findOneAndReplace + object: *collection + arguments: + filter: &filter { _id: 1 } + replacement: &replacement { x: 33 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + update: *replacement + <<: *expectedApiVersion + + - description: "findOneAndUpdate appends declared API version" + operations: + - name: findOneAndUpdate + object: collection + arguments: + filter: &filter { _id: 1 } + update: &update { $inc: { x: 1 } } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + findAndModify: *collectionName + query: *filter + update: *update + <<: *expectedApiVersion + + - 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: *collectionName + documents: + - { _id: 6, x: 66 } + - { _id: 7, x: 77 } + <<: *expectedApiVersion + + - description: "insertOne appends declared API version" + operations: + - name: insertOne + object: *collection + arguments: + document: { _id: 6, x: 66 } + expectEvents: + - client: *client + events: + - commandStartedEvent: + command: + insert: *collectionName + documents: + - { _id: 6, x: 66 } + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: 4 } + u: { _id: 4, x: 44 } + multi: { $$unsetOrMatches: false } + upsert: true + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + multi: true + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion + + - 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: *collectionName + updates: + - q: { _id: 2 } + u: { $inc: { x: 1 } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + <<: *expectedApiVersion diff --git a/source/versioned-api/tests/runcommand-helper-no-api-version-declared.json b/source/versioned-api/tests/runcommand-helper-no-api-version-declared.json new file mode 100644 index 0000000000..17e0126d10 --- /dev/null +++ b/source/versioned-api/tests/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/source/versioned-api/tests/runcommand-helper-no-api-version-declared.yml b/source/versioned-api/tests/runcommand-helper-no-api-version-declared.yml new file mode 100644 index 0000000000..c17481ab1e --- /dev/null +++ b/source/versioned-api/tests/runcommand-helper-no-api-version-declared.yml @@ -0,0 +1,75 @@ +description: "RunCommand helper: No API version declared" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.9" + serverParameters: + requireApiVersion: false + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + - database: + id: &database database + client: *client + databaseName: &databaseName versioned-api-tests + +tests: + - description: "runCommand does not inspect or change the command document" + runOnRequirements: + # serverless does not currently reject invalid API versions on + # certain commands (CLOUDP-87926) + - 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: *databaseName + + - description: "runCommand does not prevent sending invalid API version declarations" + runOnRequirements: + # serverless does not currently reject invalid API versions on + # certain commands (CLOUDP-87926) + - 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: *databaseName diff --git a/source/versioned-api/tests/test-commands-deprecation-errors.json b/source/versioned-api/tests/test-commands-deprecation-errors.json new file mode 100644 index 0000000000..0668df830a --- /dev/null +++ b/source/versioned-api/tests/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/source/versioned-api/tests/test-commands-deprecation-errors.yml b/source/versioned-api/tests/test-commands-deprecation-errors.yml new file mode 100644 index 0000000000..c2cb827b96 --- /dev/null +++ b/source/versioned-api/tests/test-commands-deprecation-errors.yml @@ -0,0 +1,47 @@ +description: "Test commands: deprecation errors" + +schemaVersion: "1.1" + +runOnRequirements: + - minServerVersion: "4.9" + serverParameters: + enableTestCommands: true + acceptApiVersion2: true + requireApiVersion: false + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + # This client is configured without a declared API version, as we cannot + # declare an unknown API version. + - database: + id: &database database + client: *client + databaseName: &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/source/versioned-api/tests/test-commands-strict-mode.json b/source/versioned-api/tests/test-commands-strict-mode.json new file mode 100644 index 0000000000..9c4ebea785 --- /dev/null +++ b/source/versioned-api/tests/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/source/versioned-api/tests/test-commands-strict-mode.yml b/source/versioned-api/tests/test-commands-strict-mode.yml new file mode 100644 index 0000000000..f53b6e6a2f --- /dev/null +++ b/source/versioned-api/tests/test-commands-strict-mode.yml @@ -0,0 +1,46 @@ +description: "Test commands: strict mode" + +schemaVersion: "1.4" + +runOnRequirements: + - minServerVersion: "4.9" + serverParameters: + enableTestCommands: true + # serverless gives a different error for unrecognized testVersion2 command + serverless: "forbid" + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + serverApi: + version: "1" + strict: true + - database: + id: &database database + client: *client + databaseName: &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/source/versioned-api/tests/transaction-handling.json b/source/versioned-api/tests/transaction-handling.json new file mode 100644 index 0000000000..32031296af --- /dev/null +++ b/source/versioned-api/tests/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/source/versioned-api/tests/transaction-handling.yml b/source/versioned-api/tests/transaction-handling.yml new file mode 100644 index 0000000000..bc7cc318dc --- /dev/null +++ b/source/versioned-api/tests/transaction-handling.yml @@ -0,0 +1,128 @@ +description: "Transaction handling" + +schemaVersion: "1.3" + +runOnRequirements: + - minServerVersion: "4.9" + topologies: [ replicaset, sharded, load-balanced ] + +createEntities: + - client: + id: &client client + observeEvents: + - commandStartedEvent + serverApi: + version: "1" + - database: + id: &database database + client: *client + databaseName: &databaseName versioned-api-tests + - collection: + id: &collection collection + database: *database + collectionName: &collectionName test + - session: + id: &session session + client: *client + +_yamlAnchors: + versions: + - &expectedApiVersion + apiVersion: "1" + apiStrict: { $$unsetOrMatches: false } + apiDeprecationErrors: { $$unsetOrMatches: false } + + +initialData: + - collectionName: *collectionName + databaseName: *databaseName + 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: *collectionName + documents: [ { _id: 6, x: 66 } ] + lsid: { $$sessionLsid: *session } + startTransaction: true + <<: *expectedApiVersion + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 7, x: 77 } ] + lsid: { $$sessionLsid: *session } + <<: *expectedApiVersion + - commandStartedEvent: + command: + commitTransaction: 1 + lsid: { $$sessionLsid: *session } + <<: *expectedApiVersion + - 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: *collectionName + documents: [ { _id: 6, x: 66 } ] + lsid: { $$sessionLsid: *session } + startTransaction: true + <<: *expectedApiVersion + - commandStartedEvent: + command: + insert: *collectionName + documents: [ { _id: 7, x: 77 } ] + lsid: { $$sessionLsid: *session } + <<: *expectedApiVersion + - commandStartedEvent: + command: + abortTransaction: 1 + lsid: { $$sessionLsid: *session } + <<: *expectedApiVersion diff --git a/source/versioned-api/versioned-api.md b/source/versioned-api/versioned-api.md new file mode 100644 index 0000000000..943336fbef --- /dev/null +++ b/source/versioned-api/versioned-api.md @@ -0,0 +1,215 @@ +# Stable API For Drivers + +- Status: Accepted +- Minimum Server Version: N/A + +______________________________________________________________________ + +## Abstract + +As MongoDB moves toward more frequent releases (a.k.a. continuous delivery), we want to enable users to take advantage +of our rapidly released features, without exposing applications to incompatible server changes due to automatic server +upgrades. A stable API will help accomplish that goal. + +## META + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and +"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +This document tends to use "SHOULD" more frequently than other specifications, but mainly in the context of providing +guidance on writing test files. This is discussed in more detail in [Design Rationale](#design-rationale). + +## Specification + +### Background + +When applications interact with MongoDB, both the driver and the server participate in executing operations. Therefore, +when determining application compatibility with MongoDB, both the driver and the server behavior must be taken into +account. + +An application can specify the server API version when creating MongoClient. When this is done: + +- The client sends the specified API version to the server, causing the server to behave in a manner compatible with + that API version. +- The driver will behave in a manner compatible with a server configured with that API version, regardless of the + server's actual release version. + +Presently there is no specification for how a driver must behave when a particular server API version is requested, or +what driver operations are subject to API compatibility guarantees. Such requirements may be stipulated in subsequent +specifications. + +This specification requires MongoClient to validate that it supports the specified server API version, if any, but does +not define what such support means. + +### `MongoClient` changes + +`MongoClient` instances accept a new `serverApi` option to allow the user to declare an API version: + +```typescript +class MongoClient { + MongoClient(... serverApi: ServerApi); +} + +enum ServerApiVersion { + v1 = "1", +} + +class ServerApi { + version: string|ServerApiVersion; + strict: Optional; // Default false + deprecationErrors: Optional; // Default false +} +``` + +Drivers SHOULD group the `serverApi` option with other similar client options like `autoEncryptionOpts`. Drivers MUST +NOT allow specification of any stable API options via the connection string. See the +[Design Rationale](#design-rationale) for more details. + +#### ServerApiVersion enumeration + +This specification and subsequent specifications will define the known API versions. Drivers SHOULD define an +enumeration containing the known API versions, using the version identifiers given. Drivers MAY deviate from the version +identifiers used in this and subsequent specifications if doing so is necessary given the driver's programming +language's constraints. Drivers MUST ensure that adding new API versions to this enumeration does not result in backward +compatibility breaks in non-major releases. This can be the case in languages that allow exhaustive `switch` statements +(e.g. Swift). + +Drivers for languages that don't have enums (e.g. PHP) MUST expose the version as a string, but SHOULD offer constants +to allow for IDE features such as code completion. In these cases, the driver MUST validate (e.g. when the application +provides a version string to the `ServerApi` class) that the version string is valid and trigger a client-side error if +an unknown API version was used. + +#### ServerApi class + +The `ServerApi` class stores an API version, along with flags that decide whether or not unknown or deprecated commands +in the specified API version trigger a server-side error. A `version` MUST be specified when declaring an API version, +while the `strict` and `deprecationErrors` options are both optional. The `ServerApi` class is considered immutable; +changes to the declared API version MUST be prohibited. + +#### Declared Version Inheritance + +Drivers MUST ensure that users cannot override the API version declared in the `MongoClient` instance. This includes the +`MongoDatabase` and `MongoCollection` classes, as well as any operations in these classes. See the rationale for more +details. + +### Sending Declared API Version to the Server + +The declared API version MUST be sent to the server as part of every command request, with the exception of the cases +listed below. Drivers MUST NOT use a server's reported `maxWireVersion` to decide whether it supports the stable API. +The server will reply with an error if the declared API version is not supported, or if the command does not support API +versioning options. If the user does not declare an API version, the driver MUST NOT send any API versioning options to +the server. + +If an API version is declared then the driver MUST use `OP_MSG` for all messages, including the initial handshake. + +#### Command Syntax + +The options from the declared API version are mapped to the following command options: + +| **ServerApi field** | **Command option** | +| ------------------- | ---------------------- | +| `version` | `apiVersion` | +| `strict` | `apiStrict` | +| `deprecationErrors` | `apiDeprecationErrors` | + +If an API version was declared, drivers MUST add the `apiVersion` option to every command that is sent to a server. +Drivers MUST add the `apiStrict` and `apiDeprecationErrors` options if they were specified by the user, even when the +specified value is equal to the server default. Drivers MUST NOT add any API versioning options if the user did not +specify them. This includes the `getMore` command as well as all commands that are part of a transaction. A previous +version of this specification excluded those commands, but that has since changed in the server. + +#### Handshake behavior + +If an API version was declared, drivers MUST NOT use the legacy hello command during the initial handshake or +afterwards. Instead, drivers MUST use the `hello` command exclusively and use the `OP_MSG` protocol. If the server does +not support `hello`, the server description MUST reflect this with an `Unknown` server type. + +#### Cursors + +For `getMore`, drivers MUST submit API parameters. If the values given do not match the API parameters given in the +cursor-initiating command, the server will reply with an error. + +#### Transactions + +When running commands as part of a transaction, drivers MUST send API parameters with all commands that are part of a +transaction, including `commitTransaction` and `abortTransaction`. If the API parameters for a command in a transaction +do not match those of the transaction-starting command, the server will reply with an error. + +#### Generic command helper + +Drivers that offer a generic command helper (e.g. `command()` or `runCommand()`) MUST NOT inspect the command document +to detect API versioning options. As with all other commands, drivers MUST inherit the API version from the client. +Specifying API versioning options in the command document and declaring an API version on the client is not supported. +Drivers MUST document that the behaviour of the command helper is undefined in this case. + +## Design Rationale + +### No URI Options + +Since changing the API version can cause the application to behave differently, drivers MUST NOT allow users to change +the declared API version without deploying code changes. This ensures that users don't copy a connection string with a +declared API version that may be different from what their application expects. A URI option can be added later if we +realise our users need it, while the opposite is not easily accomplished. + +### Don't Allow Overriding the Declared API Version + +While users are used to overriding options like read preference, read concern, and write concern in `MongoDatabase` and +`MongoCollection` objects, or on an operation level, we explicitly decided against this for the declared API version. +With a single API version available to start, we can't anticipate what use cases users may have to override the API +version. Not including this feature at the beginning allows us to gather feedback on use cases and add the features +users are looking for. On the other hand, adding the ability to override the declared API version can't be undone until +a future major release, which is almost impossible to accomplish across all drivers. + +### Generic Command Helper Behaviour + +The runCommand helper is a way for the user to run a native command with the driver doing little to no inspection in the +command. This allows users to run arbitrary commands that may not have helpers in the driver, or to pass options that +are not supported by the driver version they are currently using. Commands run using this helper do not inherit any +`readConcern` or `writeConcern` options that may have been set on the `MongoClient` or `MongoDatabase` objects. + +However, the declared API version is a different case. We are introducing this feature to give users a certain peace of +mind when upgrading driver or server versions, by ensuring that their code will continue to show the same behaviour +they've gotten used to. This includes all commands run using the generic command helper. Thus, the helper will inherit +the API version declared on the client. + +### Hardcode supported versions in drivers + +Since a new API version might require driver changes (e.g. to account for removed commands), we don't yet know what +changes drivers must make for a future version. Until we do, we must prevent users from choosing any unknown API +version. + +## Backward Compatibility + +Driver changes are fully backward compatible. Not declaring an API version when creating a client may cause an error if +the server was started with the `requireApiVersion` option enabled, but this is outside of driver control. + +## Future Work + +### Overriding the Declared API Version + +In the future, we may want to allow users to override the declared API version on a `MongoDatabase`, `MongoCollection`, +or individual operation level. However, this is not necessary until there is a different API version and we have data on +why and how users would want to override the declared API version. + +### Stable CRUD API + +Drivers may also want to provide specialized `MongoClient`, `MongoDatabase`, and `MongoCollection` classes to only +include features that are part of the stable API. This is not covered in this specification. + +## Changelog + +- 2024-09-10: Migrated from reStructuredText to Markdown. + +- 2022-10-05: Remove spec front matter and reformat changelog. + +- 2022-02-24: Rename Versioned API to Stable API + +- 2022-01-14: Require `OP_MSG` for all messages including the initial step of\ + the handshake when using stable API. + +- 2021-05-05: Require sending stable API parameters with `getMore` and\ + transaction-continuing commands. + +- 2021-04-20: Require using `hello` when using the stable API. + +- 2021-04-10: Replaced usages of `acceptAPIVersion2` with `acceptApiVersion2`. diff --git a/source/versioned-api/versioned-api.rst b/source/versioned-api/versioned-api.rst new file mode 100644 index 0000000000..7b08936b86 --- /dev/null +++ b/source/versioned-api/versioned-api.rst @@ -0,0 +1,4 @@ + +.. note:: + This specification has been converted to Markdown and renamed to + `versioned-api.md `_. diff --git a/source/wireversion-featurelist.rst b/source/wireversion-featurelist.rst index 75a68a2757..1ae2436ff8 100644 --- a/source/wireversion-featurelist.rst +++ b/source/wireversion-featurelist.rst @@ -1,62 +1,4 @@ -==================================== -Server Wire version and Feature List -==================================== -.. list-table:: - :header-rows: 1 - - * - Server version - - Wire version - - Feature List - - * - 2.6 - - 1 - - | Aggregation cursor - | Auth commands - - * - 2.6 - - 2 - - | Write commands (insert/update/delete) - | Aggregation $out pipeline operator - - * - 3.0 - - 3 - - | listCollections - | listIndexes - | SCRAM-SHA-1 - | explain command - - * - 3.2 - - 4 - - | (find/getMore/killCursors) commands - | currentOp command - | fsyncUnlock command - | findAndModify take write concern - | Commands take read concern - | Document-level validation - | explain command supports distinct and findAndModify - - * - 3.4 - - 5 - - | Commands take write concern - | Commands take collation - - * - 3.6 - - 6 - - | Supports OP_MSG - | Collection-level ChangeStream support - | Retryable Writes - | Causally Consistent Reads - | Logical Sessions - | update "arrayFilters" option - - * - 4.0 - - 7 - - | ReplicaSet transactions - | Database and cluster-level change streams and startAtOperationTime option - - * - 4.2 - - 8 - - | Sharded transactions - -For more information see MongoDB Server repo: https://github.com/mongodb/mongo/blob/master/src/mongo/db/wire_version.h +.. note:: + This specification has been converted to Markdown and renamed to + `wireversion-featurelist.md `_. diff --git a/source/wireversion-featurelist/wireversion-featurelist.md b/source/wireversion-featurelist/wireversion-featurelist.md new file mode 100644 index 0000000000..dc81f6800a --- /dev/null +++ b/source/wireversion-featurelist/wireversion-featurelist.md @@ -0,0 +1,33 @@ +# Server Wire version and Feature List + +| Server version | Wire version | Feature List | +| -------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2.6 | 1 |

Aggregation cursor

Auth commands

| +| 2.6 | 2 |

Write commands (insert/update/delete)

Aggregation $out pipeline operator

| +| 3.0 | 3 |

listCollections

listIndexes

SCRAM-SHA-1

explain command

| +| 3.2 | 4 |

(find/getMore/killCursors) commands

currentOp command

fsyncUnlock command

findAndModify take write concern

Commands take read concern

Document-level validation

explain command supports distinct and findAndModify

| +| 3.4 | 5 |

Commands take write concern

Commands take collation

| +| 3.6 | 6 |

Supports OP_MSG

Collection-level ChangeStream support

Retryable Writes

Causally Consistent Reads

Logical Sessions

update "arrayFilters" option

| +| 4.0 | 7 |

ReplicaSet transactions

Database and cluster-level change streams and startAtOperationTime option

| +| 4.2 | 8 |

Sharded transactions

Aggregation $merge pipeline operator

update "hint" option

| +| 4.4 | 9 |

Streaming protocol for SDAM

ResumableChangeStreamError error label

delete "hint" option

findAndModify "hint" option

createIndexes "commitQuorum" option

| +| 5.0 | 13 | $out and $merge on secondaries (technically FCV 4.4+) | +| 5.1 | 14 | | +| 5.2 | 15 | | +| 5.3 | 16 | | +| 6.0 | 17 |

Support for Partial Indexes

Sharded Time Series Collections

FCV set to 5.0

| +| 6.1 | 18 |

Update Perl Compatible Regular Expressions version to PCRE2

Add `*UCP` option for regex queries

| +| 6.2 | 19 |

Collection validation ensures BSON documents conform to BSON spec

Collection validation checks time series collections for internal consistency

| +| 7.0 | 21 |

Atlas Search Index Management

`$currentOp` aggregation Metrics

Compound Wildcard Indexes

Support large change stream events via `$changeStreamSplitLargeEvent` stage

`serverStatus` output gets new fields

Slot Based Query Execution

| +| 7.1 | 22 |

Improved Index Builds

Exhaust Cursors Enabled for Sharded Clusters

New Sharding Statistics for Chunk Migrations

Self-Managed Backups of Sharded Clusters | +| 7.2 | 23 |

Database Validation on `mongos` Aggregation Queries

`serverStatus` Metrics

Default Chunks Per Shard

| +| 7.3 | 24 |

Compaction Improvements

New `serverStatus` metrics

| +| 8.0 | 25 |

Range Encryption GA

OIDC authentication mechanism

New `bulkWrite` command

`snapshot` read concern on capped collections

| + +In server versions 5.0 and earlier, the wire version was defined as a numeric literal in +[src/mongo/db/wire_version.h](https://github.com/mongodb/mongo/blob/master/src/mongo/db/wire_version.h). Since server +version 5.1 ([SERVER-58346](https://jira.mongodb.org/browse/SERVER-58346)), the wire version is derived from the number +of releases since 4.0 (using +[src/mongo/util/version/releases.tpl.h](https://github.com/mongodb/mongo/blob/master/src/mongo/util/version/releases.tpl.h) +and +[src/mongo/util/version/releases.yml](https://github.com/mongodb/mongo/blob/master/src/mongo/util/version/releases.yml)).